2009-07-27 38 views
7

Tôi có một số phương thức web trả về các đối tượng của tôi trở lại dưới dạng XML được tuần tự hóa. Nó chỉ là tuần tự hóa các thuộc tính ánh xạ NHibernate của đối tượng ... bất cứ ai có một số cái nhìn sâu sắc? Có vẻ như các phương pháp web thực sự đang tuần tự hóa các proxy NHibernate thay vì các lớp của tôi. Tôi đã thử sử dụng [XMLInclude] và [XMLElement], nhưng các thuộc tính vẫn không được tuần tự hóa. Tôi có một cách hackish thực sự khủng khiếp của việc này, nhưng tôi tự hỏi nếu có một cách tốt hơn!Làm cách nào để tuần tự hóa tất cả các thuộc tính của đối tượng được ánh xạ NHibernate?

Something như thế này:

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

Điều này rõ ràng là không có cách nào để viết mã. Nếu tôi không có bản đồ "giả" trong đó (thuộc tính UglyHack), thuộc tính đó sẽ không được đăng. Hiện tại, tôi đang xem xét sử dụng các đối tượng chuyển dữ liệu (Data), và có thể sử dụng một cái gì đó bằng cách phản chiếu ...

+0

Vui lòng đăng một ví dụ nhỏ. –

Trả lời

21

Cách tốt nhất để tuần tự hóa đối tượng ánh xạ NH là không tuần tự hóa nó :).

Nếu bạn đang gửi nó qua dây, bạn nên thực sự tạo DTO cho nó. Nếu bạn không muốn tạo đối tượng đó, bạn có thể đặt [XmlIgnore] trên các thuộc tính bạn không muốn tuần tự hóa.

Nếu bạn muốn tất cả thuộc tính, bạn phải tải chúng TẤT CẢ từ cơ sở dữ liệu - cho một số tải mong muốn sẽ đủ cho người khác (nơi có quá nhiều kết nối sẽ bắt đầu sao chép dữ liệu), bạn sẽ phải truy cập thuộc tính đó cách bạn muốn kích hoạt tải.

Edit:

Và tôi muốn thêm điều khác - gửi các đơn vị tên miền của bạn trong vòng dây là luôn một ý tưởng tồi. Trong trường hợp của tôi, tôi đã học được nó một cách khó khăn - tôi vạch trần một số thực thể trên WebService - và bây giờ hầu như bất kỳ thay đổi nào (đổi tên thuộc tính, xóa thuộc tính ..etc) sang miền của tôi giết chết ứng dụng bằng WS - cộng với toàn bộ nhóm các thuộc tính có [XmlIgnore] trên chúng (đừng quên phụ thuộc vòng tròn).

Chúng tôi sẽ viết lại sớm đủ - nhưng phần còn lại đảm bảo đó không phải là điều tôi sẽ làm lại. :)

Chỉnh sửa 2

Bạn có thể sử dụng AutoMapper để chuyển dữ liệu từ tổ chức của mình vào DTO. Họ có một số ví dụ trên trang web.

+0

Cảm ơn mẹo! – wprl

+0

Tôi thấy điểm của bạn :) Bây giờ tôi chỉ đang cố gắng tìm ra cách tạo ra chúng thông qua sự phản chiếu ... – wprl

+1

+1 Câu trả lời tuyệt vời! –

0

Đồng ý với sirrocco, tôi đã có thời gian khủng khiếp nhất khi cố gắng tuần tự hóa các thực thể NHibernate thông qua WCF, và cuối cùng đã đi kèm với một giải pháp DTO được thực hiện thông qua phản ánh.

Chỉnh sửa: Toàn bộ giải pháp là quá lớn để gửi ở đây, và được tùy biến cho nhu cầu của tôi là dĩ nhiên, vì vậy tôi sẽ đăng một vài bộ phận có liên quan:

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

sau đó, khi bạn muốn phục hồi lại các 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

chăm sóc để đăng một ví dụ? – wprl

+0

rất thú vị – wprl

+1

Sẽ không AutoMapper làm nhiệm vụ này? – Lijo

4

nếu nó là một dịch vụ WCF, bạn có thể sử dụng một 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 trong máy chủ:

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); 
       } 
      } 
     } 
Các vấn đề liên quan