2009-07-27 18 views
7

que tienen algunos métodos web que devuelven los objetos de mi espalda como XML serializado. Solo está serializando las propiedades asignadas de NHibernate del objeto ... ¿alguien tiene alguna idea? Parece ser que los métodos web en realidad están serializando los proxies NHibernate en lugar de mis clases. He intentado usar [XMLInclude] y [XMLElement], pero las propiedades todavía no están serializadas. Tengo una forma realmente horrible de evitar esto, pero me preguntaba si habría una mejor manera.¿Cómo serializar todas las propiedades de un objeto mapeado-NHibernate?

Algo como esto:

<?xml version="1.0" ?> 
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="StoryManager" assembly="StoryManager"> 
    <class name="Graphic" table="graphics" lazy="false"> 
    <id name="Id" column="id" type="int" unsaved-value="0" > 
     <generator class="identity"/> 
    </id> 

    <property name="Assigned" /> 
    <property name="Due" /> 
    <property name="Completed" /> 
    <property name="UglyHack" insert="false" update="false" /> 


    <many-to-one name="Parent" class="Story" column="story_id"/> 

    </class> 
</hibernate-mapping> 

public class Graphic 
{ 
    private int m_id; 
    public virtual int Id 
    { 
     get { return m_id; } 
     set { m_id = value; } 
    } 

    private DateTime? m_assigned; 
    public virtual DateTime? Assigned 
    { 
     get { return m_assigned; } 
     set { m_assigned = value; } 
    } 

    private DateTime? m_due; 
    public virtual DateTime? Due 
    { 
     get { return m_due; } 
     set { m_due = value; } 
    } 

    private DateTime? m_completed; 
    public virtual DateTime? Completed 
    { 
     get { return m_completed; } 
     set { m_completed = value; } 
    } 

    public bool UglyHack 
    { 
     get { return m_due < m_completed; } // return something besides a real mapped variable 
     set {} // trick NHibernate into thinking it's doing something 
    } 
} 

Esto es, obviamente, no hay manera de escribir código. Si no tengo el mapeo "falso" en ese país (propiedad UglyHack), no será serializado esa propiedad. Por ahora estoy buscando en el uso (datos) Transfer Objects, y puede ser a algo utilizando la reflexión ...

+0

Por favor, publique un pequeño ejemplo. –

Respuesta

21

La mejor manera de serializar el NH mapeados objetivo es no serializarlo :).

Si lo está enviando a través del cable que realmente debería crear un DTO para ello. Si no desea crear ese objeto, puede configurar [XmlIgnore] en las propiedades que no desea serializar.

Si quiere todas las propiedades, tiene que cargarlas TODAS de la base de datos - para algunas, una carga ansiosa será suficiente para otras (donde muchas uniones comenzarán a duplicar datos) tendrá que acceder a esa propiedad en cualquier forma en que desea activar la carga.

Editar:

Y me gustaría añadir otra cosa - el envío de sus entidades de dominio sobre el alambre es siempre una mala idea. En mi caso he aprendido de la manera difícil - expongo algunas entidades sobre un servicio web - y ahora casi cualquier cambio (cambio el nombre de una propiedad, eliminar una propiedad .etc) a mi dominio mata a la aplicación a través de los WS - además de un montón de las propiedades tienen [XmlIgnore] en ellos (no te olvides de las dependencias circulares).

Vamos a hacer una reescritura lo suficientemente pronto, pero descanse asegúrense de que no es algo que voy a alguna vez hacer de nuevo. :)

Editar 2

usted podría utilizar AutoMapper para transferir los datos de su entidad a la DTO. Ellos tienen algunos ejemplos en el sitio.

+0

¡Gracias por el consejo! – wprl

+0

Veo tu punto :) Ahora solo intento descubrir cómo generarlos a través de la reflexión ... – wprl

+1

+1 ¡Excelente respuesta! –

0

Estoy de acuerdo con sirrocco, he pasado momentos terribles tratando de serializar las entidades de NHibernate a través de WCF, y eventualmente fui con una solución de DTO hecha genéricamente a través de la reflexión.

Editar: Toda la solución es demasiado grande para publicar aquí, y es la medida para mis necesidades por supuesto, así que voy a publicar algunas secciones relevantes:

[DataContract] 
public class DataTransferObject 
{ 
    private Dictionary<string, object> propertyValues = new Dictionary<string, object>(); 
    private Dictionary<string, object> fieldValues = new Dictionary<string, object>(); 
    private Dictionary<string, object> relatedEntitiesValues = new Dictionary<string, object>(); 
    private Dictionary<string, object> primaryKey = new Dictionary<string, object>(); 
    private Dictionary<string,List<DataTransferObject>> subEntities = new Dictionary<string, List<DataTransferObject>>(); 

... 

    public static DataTransferObject ConvertEntityToDTO(object entity,Type transferType) 
    { 
     DataTransferObject dto = new DataTransferObject(); 
     string[] typePieces = transferType.AssemblyQualifiedName.Split(','); 

     dto.AssemblyName = typePieces[1]; 
     dto.TransferType = typePieces[0]; 

     CollectPrimaryKeyOnDTO(dto, entity); 
     CollectPropertiesOnDTO(dto, entity); 
     CollectFieldsOnDTO(dto, entity); 
     CollectSubEntitiesOnDTO(dto, entity); 
     CollectRelatedEntitiesOnDTO(dto, entity); 

     return dto; 
    } 
.... 

    private static void CollectPropertiesOnDTO(DataTransferObject dto, object entity) 
    { 
     List<PropertyInfo> transferProperties = ReflectionHelper.GetProperties(entity,typeof(PropertyAttribute)); 

     CollectPropertiesBasedOnFields(entity, transferProperties); 

     foreach (PropertyInfo property in transferProperties) 
     { 
      object propertyValue = ReflectionHelper.GetPropertyValue(entity, property.Name); 

      dto.PropertyValues.Add(property.Name, propertyValue); 
     } 
    } 

entonces, cuando se quiere resucitar el DTO :

private static DTOConversionResults ConvertDTOToEntity(DataTransferObject transferObject,object parent) 
    { 
     DTOConversionResults conversionResults = new DTOConversionResults(); 

     object baseEntity = null; 
     ObjectHandle entity = Activator.CreateInstance(transferObject.AssemblyName, 
                 transferObject.TransferType); 

     if (entity != null) 
     { 
      baseEntity = entity.Unwrap(); 

      conversionResults.Add(UpdatePrimaryKeyValue(transferObject, baseEntity)); 
      conversionResults.Add(UpdateFieldValues(transferObject, baseEntity)); 
      conversionResults.Add(UpdatePropertyValues(transferObject, baseEntity)); 
      conversionResults.Add(UpdateSubEntitiesValues(transferObject, baseEntity)); 
      conversionResults.Add(UpdateRelatedEntitiesValues(transferObject, baseEntity,parent)); 
.... 

    private static DTOConversionResult UpdatePropertyValues(DataTransferObject transferObject, object entity) 
    {    
     DTOConversionResult conversionResult = new DTOConversionResult(); 

     foreach (KeyValuePair<string, object> values in transferObject.PropertyValues) 
     { 
      try 
      { 
       ReflectionHelper.SetPropertyValue(entity, values.Key, values.Value); 
      } 
      catch (Exception ex) 
      { 
       string failureReason = "Failed to set property " + values.Key + " value " + values.Value; 

       conversionResult.Failed = true; 
       conversionResult.FailureReason = failureReason; 

       Logger.LogError(failureReason); 
       Logger.LogError(ExceptionLogger.BuildExceptionLog(ex)); 
      } 
     } 

     return conversionResult; 
    } 
+0

¿Te importa publicar un ejemplo? – wprl

+0

muy interesante – wprl

+1

¿No hará AutoMapper esta tarea? – Lijo

4

si es un servicio WCF, se podría utilizar un IDataContractSurrogate

public class HibernateDataContractSurrogate : IDataContractSurrogate 
{ 
    public HibernateDataContractSurrogate() 
    { 
    } 

    public Type GetDataContractType(Type type) 
    { 
     // Serialize proxies as the base type 
     if (typeof(INHibernateProxy).IsAssignableFrom(type)) 
     { 
      type = type.GetType().BaseType; 
     } 

     // Serialize persistent collections as the collection interface type 
     if (typeof(IPersistentCollection).IsAssignableFrom(type)) 
     { 
      foreach (Type collInterface in type.GetInterfaces()) 
      { 
       if (collInterface.IsGenericType) 
       { 
        type = collInterface; 
        break; 
       } 
       else if (!collInterface.Equals(typeof(IPersistentCollection))) 
       { 
        type = collInterface; 
       } 
      } 
     } 

     return type; 
    } 

    public object GetObjectToSerialize(object obj, Type targetType) 
    { 
     // Serialize proxies as the base type 
     if (obj is INHibernateProxy) 
     { 
      // Getting the implementation of the proxy forces an initialization of the proxied object (if not yet initialized) 
      try 
      { 
       var newobject = ((INHibernateProxy)obj).HibernateLazyInitializer.GetImplementation(); 
       obj = newobject; 
      } 
      catch (Exception) 
      { 
       // Type test = NHibernateProxyHelper.GetClassWithoutInitializingProxy(obj); 
       obj = null; 
      } 
     } 

     // Serialize persistent collections as the collection interface type 
     if (obj is IPersistentCollection) 
     { 
      IPersistentCollection persistentCollection = (IPersistentCollection)obj; 
      persistentCollection.ForceInitialization(); 
      //obj = persistentCollection.Entries(); // This returns the "wrapped" collection 
      obj = persistentCollection.Entries(null); // This returns the "wrapped" collection 
     } 

     return obj; 
    } 



    public object GetDeserializedObject(object obj, Type targetType) 
    { 
     return obj; 
    } 

    public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType) 
    { 
     return null; 
    } 

    public object GetCustomDataToExport(Type clrType, Type dataContractType) 
    { 
     return null; 
    } 

    public void GetKnownCustomDataTypes(Collection<Type> customDataTypes) 
    { 
    } 

    public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData) 
    { 
     return null; 
    } 

    public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit) 
    { 
     return typeDeclaration; 
    } 
} 

implemen taion en el host:

foreach (ServiceEndpoint ep in host.Description.Endpoints) 
     { 
      foreach (OperationDescription op in ep.Contract.Operations) 
      { 
       var dataContractBehavior = op.Behaviors.Find<DataContractSerializerOperationBehavior>(); 
       if (dataContractBehavior != null) 
       { 
        dataContractBehavior.DataContractSurrogate = new HibernateDataContractSurrogate(); 
       } 
       else 
       { 
        dataContractBehavior = new DataContractSerializerOperationBehavior(op); 
        dataContractBehavior.DataContractSurrogate = new HibernateDataContractSurrogate(); 
        op.Behaviors.Add(dataContractBehavior); 
       } 
      } 
     } 
Cuestiones relacionadas