2009-07-27 19 views
7

Mam kilka metod internetowych, które zwracają moje obiekty z powrotem jako serializowane XML. Jest tylko serializacją właściwości obiektu odwzorowanych NHibernate ... każdy ma pewien wgląd? Wydaje się, że metody sieciowe faktycznie serializują proxy NHibernate zamiast moich klas. Próbowałem już używać [XMLInclude] i [XMLElement], ale właściwości nadal nie są serializowane. Mam naprawdę okropny hackowy sposób na obejście tego, ale zastanawiałem się, czy istnieje lepszy sposób!Jak serializować wszystkie właściwości obiektu odwzorowanego NHibernate?

coś takiego:

<?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 
    } 
} 

To oczywiście nie sposób pisać kod. Jeśli nie mam tam "fałszywego" mapowania (właściwość UglyHack), ta właściwość nie będzie serializowana. Na razie zajmuję się używaniem (Data) Przenoszenia Obiektów i może być na czymś przy użyciu refleksji ...

+0

Proszę napisać mały przykład. –

Odpowiedz

21

Najlepszym sposobem serializowania obiektu odwzorowanego NH jest nie przekształcanie go do postaci szeregowej :).

Jeśli przesyłasz go przez kabel, powinieneś stworzyć dla niego DTO. Jeśli nie chcesz tworzyć tego obiektu, możesz ustawić [XmlIgnore] na właściwościach, które nie mają być serializowane.

Jeśli chcesz wszystkie właściwości, musisz załadować je WSZYSTKIE z bazy danych - dla niektórych z nich będzie wystarczające obciążenie dla innych (gdzie zbyt wiele sprzężeń rozpocznie duplikowanie danych) będziesz musiał uzyskać dostęp do tej właściwości w dowolnym sposób, w jaki chcesz uruchomić ładunek.

Edit:

I chciałbym dodać jeszcze jedno - wysyłanie swoich jednostek domen przez przewód jest zawsze zły pomysł. W moim przypadku nauczyłem się tego na własnej skórze - ujawniam niektóre podmioty za pośrednictwem usługi WebService - a teraz prawie każda zmiana (zmiana nazwy usługi, usunięcie właściwości ..etc) w mojej domenie zabija aplikację za pomocą WS - oraz całą masę właściwości mają na nich [XmlIgnore] (nie zapomnij o zależnościach cyklicznych).

Wkrótce zrobimy przeróbkę - ale zapewniamy, że to nie jest coś, co jeszcze raz zrobię . :)

Edycja 2

Można użyć AutoMapper do przenoszenia danych z jednostki do DTO. Mają kilka przykładów na stronie.

+0

Dzięki za napiwek! – wprl

+0

Widzę twój punkt :) Teraz tylko próbuję dowiedzieć się, jak je wygenerować poprzez odbicie ... – wprl

+1

+1 Doskonała odpowiedź! –

0

Zgadzam się z sirrocco, miałem najstraszniejszy czas próbując serializować postaci NHibernate poprzez WCF, i ostatecznie poszedłem z rozwiązaniem DTO zrobionym generalnie przez refleksję.

Edit: Całe rozwiązanie jest zbyt duża, aby umieścić tu, i jest dostosowany do moich potrzeb oczywiście, więc będę pisać kilka odpowiednich działach:

[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); 
     } 
    } 

wtedy, gdy chcemy wskrzesić 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

dbałość o przesłanie przykładu? – wprl

+0

bardzo interesujące – wprl

+1

Czy AutoMapper nie wykona tego zadania? – Lijo

4

jeśli jego usługa WCF, można użyć 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; 
    } 
} 

reali taion na hoście:

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); 
       } 
      } 
     } 
Powiązane problemy