如何序列化NHibernate映射对象的所有属性?

时间:2009-07-27 21:24:40

标签: c# web-services nhibernate xml-serialization proxy-classes

我有一些Web方法可以将我的对象作为序列化XML返回。它只是序列化对象的NHibernate映射属性......任何人都有一些洞察力?似乎Web方法实际上是序列化NHibernate代理而不是我的类。我尝试过使用[XMLInclude]和[XMLElement],但属性仍然没有序列化。我有一种非常糟糕的hackish方式绕过这个,但我想知道是否有更好的方法!

这样的事情:

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

这显然无法编写代码。如果我没有那里的“假”映射(UglyHack属性),那么该属性将不会被序列化。现在我正在研究使用(数据)传输对象,并可能使用反射......

3 个答案:

答案 0 :(得分:21)

序列化NH映射对象的最佳方法是不对其进行序列化:)。

如果你是通过网络发送它,你应该真的为它创建一个DTO。如果您不想创建该对象,可以在不希望序列化的属性上设置[XmlIgnore]。

如果你想要所有的属性,你必须从数据库加载它们 - 对于一些急切的加载对于其他人来说已经足够了(其中有太多的连接将开始复制数据)你将不得不以任何方式访问该属性你想要触发负载。

修改

我想添加另一件事 - 通过网络发送您的域名实体是总是一个坏主意。在我的情况下,我学习了很难的方法 - 我通过WebService公开一些实体 - 现在几乎任何更改(重命名属性,删除属性..etc)到我的域使用WS杀死应用程序 - 加上一大堆属性上有[XmlIgnore](不要忘记循环依赖)。

我们很快就会重写 - 但请确保我不会再次再做一次。 :)

修改2

您可以使用AutoMapper将数据从实体传输到DTO。他们在网站上有一些例子。

答案 1 :(得分:4)

如果是WCF服务,您可以使用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;
    }
}

在主持人中实施:

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

答案 2 :(得分:0)

同意sirrocco,我有最糟糕的时间尝试通过WCF序列化NHibernate实体,并最终通过反射完成了DTO解决方案。

编辑:整个解决方案太大,无法在此处发布,并且根据我的需求进行了定制,因此我将发布一些相关部分:

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

然后,当你想要复活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;
    }