WCF与Nhibernate 对象循环引用问题
2008-03-14 11:01
288 查看
导读:
On a recent project, I was trying to return an object (with child objects) from an ASMX Web Service and was using the XmlSerializer for serialization. (When doing ASMX Web Services, you really have no choice but to use the XmlSerializer.) The child objects contained a circular reference, which ended up being the bane of my existence because the XmlSerializer can't handle circular references. The XmlSerializer reports, "Circular reference detected" and gives up. It just can't serialize it. The amount of code (and changes to the domain model) to work around this limitation was just plain stupid, not to mention the subtle bugs that needed to be fixed when object identity wasn't respected in other areas of the object graph.
So what does this have to do with Windows Communication Foundation (WCF)? Given how excited I've been about WCF, my fellow developers (and the PM) asked if WCF would have avoided the XmlSerializer problems that we had experienced. My curiosity was piqued and I had to find out. As it turns out, WCF can handle circular references and preserve object identity. Or more correctly, the DataContractSerializer can. This would have saved me untold effort if I had been able to use WCF on the project. (Unfortunately release dates didn't match up. Ironically the project released into production the day before WCF went RTM.) Just a pronouncement that WCF can handle circular references wouldn't make for a very interesting blog entry. The reality is that out-of-the-box, WCF cannot handle circular references even though the DataContractSerializer can. With a bit of digging, I was able to configure WCF to serialize circular references and properly preserve object identity.
Let's take a deeper look... Consider a simple object graph with a circular reference. The simplest is a parent-child relationship where the child has a back-reference to the parent. (My situation was more complex, but this example suffices.) Here is a Person class, which has a list of Children, as well as a reference to its parent.
[DataContract(Namespace="http://www.jameskovacs.com/Examples/WcfCircularRefs")]
publicclassPersonDTO{
[DataMember]
publicstringName {
get{ returnm_name; }
set{ m_name = value}
}
privatestringm_name;
[DataMember(Name="Parent")]
privatePersonm_parent;
[DataMember(Name = "Children")]
privateListm_children = newList();
/* Additional constructors/properties/methods elided. */
/* I've also omitted the Order and IsRequired DataMember parameters above. */
/* Full source can be found in the linked zip file at the end. */
}
One of the nice things about the DataContractSerializer is that serialized members are opt-in. If you don't decorate a member with the [DataMember] attribute, it is not serialized. For instance, m_name is not decorated and is therefore ignored by the DataContractSerializer. This is in stark contrast to the XmlSerializer where all public read/write properties are serialized unlessthey are marked with [XmlIgnore]. Another point to note is that I can serialize both properties and fields, which allows me to have read-only properties, such as m_parent and m_children, while still serializing them at the field level. (N.B. You can still configure WCF to use the XmlSerializer for compatibility, though the preferred serializer is the DataContractSerializer.)
Now why can't WCF handle circular references out-of-the-box. The reason is that there is no industry-accepted, interoperable way of expressing anything but parent-child relationships in XML. You can use the ID/IDREF feature of XML or the key/keyref feature of XML Schema, but a lot of serializers don't respect these attributes or handle them properly. So if you want to serialize circular references, you need to stray out of the realm of safe interoperability. I found some very intriguing information in Aaron Skonnard's MSDN article about the DataContractSerializer, Serialization in Windows Communication Foundation. When instantiating the DataContractSerializer, you can tell it to respect object identity (and thus handle circular references) by passing truefor preserveObjectReferences constructor parameter.
DataContractSerializerserializer = newDataContractSerializer(typeof(PersonDTO), null, int.MaxValue, false, true/* preserveObjectReferences */, null);
serializer.WriteObject(memStream, person);
The resulting XML looks like this:
Sam Spade
Unknown
Jane Spade
Bobby Spoon
You'll notice that references to other objects are encoded using the z:Id attribute. So the DataContractSerializer can handle circular references, but how do we tell WCF to pass true for the preserveObjectReferences parameter when creating a DataContractSerializer? We can't, but what we can do is do it ourselves and wedge our code into the WCF pipeline. Is that cool or what? All the details can be found in Ingo Rammer's MSDN Library Article, From .NET Remoting to the Windows Communication Foundation (WCF). We create a class derived from DataContractSerializerOperationBehaviour, which we'll call PreserveReferencesOperationBehavior. Here's the important overload:
publicoverrideXmlObjectSerializerCreateSerializer(
Typetype, XmlDictionaryStringname, XmlDictionaryStringns,
IListknownTypes) {
returnnewDataContractSerializer(type, name, ns, knownTypes,
0x7FFF,
false,
true/* preserveObjectReferences */,
null);
}
So we've wedged in the preserveObjectReferences = true. We then create an attribute, PreserveReferencesAttribute, which implements IOperationBehavior, to inject the PreserveReferencesOperationBehavior.
publicvoidApplyDispatchBehavior(OperationDescriptiondescription,
DispatchOperationdispatch) {
IOperationBehaviorinnerBehavior =
newPreserveReferencesOperationBehavior(description);
innerBehavior.ApplyDispatchBehavior(description, dispatch);
}
Finally, we apply this attribute to the OperationContract in our ServiceContract.
[ServiceContract(Namespace="http://www.jameskovacs.com/Examples/WcfCircularRefs")]
publicinterfaceIPersonService{
[OperationContract]
[PreserveReferences]
PersonDTOGetDefaultPerson();
}
Our service is now able to serialize circular references, as well as preserve object identity. If we need to transmit objects with circular references back to the service, we could make similar changes to inject PreserveReferencesOperationBehavior into the client-side proxy.
To summarize, WCF is a very extensible framework for distributed communication. We were able to make some small modifications to the WCF pipeline that allowed us to return an object graph with circular references from a service. With the release of WCF, the life of the distributed application developer just got a whole lot easier. Full source code can be found here (16 KB).
本文转自
http://www.jameskovacs.com/blog/CommentView.aspx?guid=477b077c-e65e-4547-8289-4e1bc17b3de7
On a recent project, I was trying to return an object (with child objects) from an ASMX Web Service and was using the XmlSerializer for serialization. (When doing ASMX Web Services, you really have no choice but to use the XmlSerializer.) The child objects contained a circular reference, which ended up being the bane of my existence because the XmlSerializer can't handle circular references. The XmlSerializer reports, "Circular reference detected" and gives up. It just can't serialize it. The amount of code (and changes to the domain model) to work around this limitation was just plain stupid, not to mention the subtle bugs that needed to be fixed when object identity wasn't respected in other areas of the object graph.
So what does this have to do with Windows Communication Foundation (WCF)? Given how excited I've been about WCF, my fellow developers (and the PM) asked if WCF would have avoided the XmlSerializer problems that we had experienced. My curiosity was piqued and I had to find out. As it turns out, WCF can handle circular references and preserve object identity. Or more correctly, the DataContractSerializer can. This would have saved me untold effort if I had been able to use WCF on the project. (Unfortunately release dates didn't match up. Ironically the project released into production the day before WCF went RTM.) Just a pronouncement that WCF can handle circular references wouldn't make for a very interesting blog entry. The reality is that out-of-the-box, WCF cannot handle circular references even though the DataContractSerializer can. With a bit of digging, I was able to configure WCF to serialize circular references and properly preserve object identity.
Let's take a deeper look... Consider a simple object graph with a circular reference. The simplest is a parent-child relationship where the child has a back-reference to the parent. (My situation was more complex, but this example suffices.) Here is a Person class, which has a list of Children, as well as a reference to its parent.
[DataContract(Namespace="http://www.jameskovacs.com/Examples/WcfCircularRefs")]
publicclassPersonDTO{
[DataMember]
publicstringName {
get{ returnm_name; }
set{ m_name = value}
}
privatestringm_name;
[DataMember(Name="Parent")]
privatePersonm_parent;
[DataMember(Name = "Children")]
privateListm_children = newList();
/* Additional constructors/properties/methods elided. */
/* I've also omitted the Order and IsRequired DataMember parameters above. */
/* Full source can be found in the linked zip file at the end. */
}
One of the nice things about the DataContractSerializer is that serialized members are opt-in. If you don't decorate a member with the [DataMember] attribute, it is not serialized. For instance, m_name is not decorated and is therefore ignored by the DataContractSerializer. This is in stark contrast to the XmlSerializer where all public read/write properties are serialized unlessthey are marked with [XmlIgnore]. Another point to note is that I can serialize both properties and fields, which allows me to have read-only properties, such as m_parent and m_children, while still serializing them at the field level. (N.B. You can still configure WCF to use the XmlSerializer for compatibility, though the preferred serializer is the DataContractSerializer.)
Now why can't WCF handle circular references out-of-the-box. The reason is that there is no industry-accepted, interoperable way of expressing anything but parent-child relationships in XML. You can use the ID/IDREF feature of XML or the key/keyref feature of XML Schema, but a lot of serializers don't respect these attributes or handle them properly. So if you want to serialize circular references, you need to stray out of the realm of safe interoperability. I found some very intriguing information in Aaron Skonnard's MSDN article about the DataContractSerializer, Serialization in Windows Communication Foundation. When instantiating the DataContractSerializer, you can tell it to respect object identity (and thus handle circular references) by passing truefor preserveObjectReferences constructor parameter.
DataContractSerializerserializer = newDataContractSerializer(typeof(PersonDTO), null, int.MaxValue, false, true/* preserveObjectReferences */, null);
serializer.WriteObject(memStream, person);
The resulting XML looks like this:
Sam Spade
Unknown
Jane Spade
Bobby Spoon
You'll notice that references to other objects are encoded using the z:Id attribute. So the DataContractSerializer can handle circular references, but how do we tell WCF to pass true for the preserveObjectReferences parameter when creating a DataContractSerializer? We can't, but what we can do is do it ourselves and wedge our code into the WCF pipeline. Is that cool or what? All the details can be found in Ingo Rammer's MSDN Library Article, From .NET Remoting to the Windows Communication Foundation (WCF). We create a class derived from DataContractSerializerOperationBehaviour, which we'll call PreserveReferencesOperationBehavior. Here's the important overload:
publicoverrideXmlObjectSerializerCreateSerializer(
Typetype, XmlDictionaryStringname, XmlDictionaryStringns,
IListknownTypes) {
returnnewDataContractSerializer(type, name, ns, knownTypes,
0x7FFF,
false,
true/* preserveObjectReferences */,
null);
}
So we've wedged in the preserveObjectReferences = true. We then create an attribute, PreserveReferencesAttribute, which implements IOperationBehavior, to inject the PreserveReferencesOperationBehavior.
publicvoidApplyDispatchBehavior(OperationDescriptiondescription,
DispatchOperationdispatch) {
IOperationBehaviorinnerBehavior =
newPreserveReferencesOperationBehavior(description);
innerBehavior.ApplyDispatchBehavior(description, dispatch);
}
Finally, we apply this attribute to the OperationContract in our ServiceContract.
[ServiceContract(Namespace="http://www.jameskovacs.com/Examples/WcfCircularRefs")]
publicinterfaceIPersonService{
[OperationContract]
[PreserveReferences]
PersonDTOGetDefaultPerson();
}
Our service is now able to serialize circular references, as well as preserve object identity. If we need to transmit objects with circular references back to the service, we could make similar changes to inject PreserveReferencesOperationBehavior into the client-side proxy.
To summarize, WCF is a very extensible framework for distributed communication. We were able to make some small modifications to the WCF pipeline that allowed us to return an object graph with circular references from a service. With the release of WCF, the life of the distributed application developer just got a whole lot easier. Full source code can be found here (16 KB).
本文转自
http://www.jameskovacs.com/blog/CommentView.aspx?guid=477b077c-e65e-4547-8289-4e1bc17b3de7
相关文章推荐
- WCF+Nhibernate循环引用导致序列化的问题
- WCF+Nhibernate循环引用导致序列化的问题
- 关于java中对象的循环引用问题
- 【Java】消除fastjson对同一对象循环引用的问题
- OC_内存管理(二)对象复制、循环引用问题、自动释放池
- 关于java中对象的循环引用问题(java编程思想)
- Wcf序列化的循环引用问题1
- python类中显示重写__del__方法,引起循环引用的对象无法释放,造成垃圾泄露问题
- 采用CXF解决webservice循环引用对象的问题
- 使用 EntityFramework后把一个对象序列化成json字符串引起循环引用的问题
- WCF自引用和循环引用导致的序列化问题
- Block的引用循环问题 (ARC & non-ARC) 【 引用外部变量或对象 】
- 关于java中对象的循环引用问题
- 采用CXF解决webservice循环引用对象的问题
- 用JAXB转换XML和Java对象时的循环引用问题的解决方法
- iPhone开发资料之内存管理 ,循环引用导致的内存问题
- 求助!!!未将对象设置引用到实例的问题
- 关于Block的copy和循环引用的问题
- 如何解决引用对象时,必须加所有者(owner)的问题
- NSTimer循环引用的问题