NHibernate 매핑된 객체의 모든 속성을 어떻게 직렬화합니까?
-
19-09-2019 - |
문제
내 개체를 직렬화된 XML로 다시 반환하는 몇 가지 웹 메서드가 있습니다.그것은 단지 객체의 Hibernate 매핑 속성을 직렬화하는 것뿐입니다...누구든지 통찰력이 있습니까?웹 메소드가 실제로 내 클래스 대신 NHibernate 프록시를 직렬화하는 것 같습니다.[XMLInclude] 및 [XMLElement]를 사용해 보았지만 속성이 여전히 직렬화되지 않습니다.나는 이 문제를 해결하는 정말 끔찍한 해킹 방법을 가지고 있지만 더 좋은 방법이 있는지 궁금합니다!
이 같은:
<?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 속성)이 없으면 해당 속성은 직렬화되지 않습니다.지금은 (데이터) 전송 개체를 사용하는 방법을 조사하고 있으며 리플렉션을 사용하여 뭔가를 할 수도 있습니다.
해결책
NH 매핑된 객체를 직렬화하는 가장 좋은 방법은 직렬화하지 않는 것입니다. :)
유선으로 전송하는 경우 실제로 DTO를 만들어야 합니다.해당 개체를 생성하지 않으려면 직렬화하지 않으려는 속성에 [XmlIgnore]를 설정할 수 있습니다.
모든 속성을 원하면 데이터베이스에서 모두 로드해야 합니다. 어떤 경우에는 열성적인 로드로 충분하고 다른 경우에는(너무 많은 조인으로 인해 데이터 복제가 시작됨) 원하는 방식으로 해당 속성에 액세스해야 합니다. 부하를 트리거합니다.
편집하다:
그리고 한 가지 더 추가하고 싶습니다. 도메인 엔터티를 유선으로 보내는 것은 언제나 나쁜 생각이다.제 경우에는 WebService를 통해 일부 엔터티를 노출하는 등 어려운 방법으로 배웠습니다. 이제 내 도메인에 대한 거의 모든 변경 사항(속성 이름 바꾸기, 속성 제거 등)으로 인해 WS를 사용하는 앱이 종료됩니다. 속성에는 [XmlIgnore]가 있습니다(순환 종속성을 잊지 마세요).
우리는 곧 재작성을 할 것입니다. 하지만 그건 제가 할 일이 아니라는 점을 명심하세요. 항상 다시 해요.:)
편집 2
당신은 사용할 수 있습니다 자동매퍼 귀하의 법인에서 DTO로 데이터를 전송합니다.사이트에 몇 가지 예가 있습니다.
다른 팁
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);
}
}
}
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;
}