Entity Framework在WCF中序列化的问题
問題描述?
如果你在WCF中用Entity Framework來獲取數(shù)據(jù)并返回實(shí)體對(duì)象,那么對(duì)下面的錯(cuò)誤一定不陌生。
接收對(duì) http://localhost:5115/ReService.svc 的 HTTP 響應(yīng)時(shí)發(fā)生錯(cuò)誤。這可能是由于服務(wù)終結(jié)點(diǎn)綁定未使用 HTTP 協(xié)議造成的。
這還可能是由于服務(wù)器中止了 HTTP 請(qǐng)求上下文(可能由于服務(wù)關(guān)閉)所致。有關(guān)詳細(xì)信息,請(qǐng)參見服務(wù)器日志。
這就是因?yàn)樵诜祷財(cái)?shù)據(jù)的時(shí)候,序列化失敗,導(dǎo)致WCF服務(wù)自動(dòng)停止了。
為什么會(huì)序列化失敗
為了方便說明,我們先做個(gè)示例來重現(xiàn)這個(gè)錯(cuò)誤。
默認(rèn)情況下,Entity Framework為了支持它的一些高級(jí)特性(延遲加載等),默認(rèn)將自動(dòng)生成代理類是設(shè)置為true,即
public MyContext(){this.Configuration.ProxyCreationEnabled = true;}這樣,如果我們的實(shí)體中包含其它實(shí)體的導(dǎo)航屬性,則EF會(huì)自動(dòng)的為這個(gè)實(shí)體生成代理類。
[DataContract(IsReference=true)]public class Student {public Student(){this.Teachers = new HashSet<Teacher>();}[DataMember]public int ID { get; set; }[DataMember]public virtual string Name { get; set; }[DataMember]public virtual ICollection<Teacher> Teachers { get; set; }}[DataContract(IsReference = true)]public class Teacher{[DataMember]public int ID { get; set; }[DataMember]public virtual string Name { get; set; }}觀察上面兩個(gè)實(shí)體,Student中有對(duì)Teacher的導(dǎo)航屬性,而Teacher則沒有。我們看看通過EF對(duì)獲取這兩個(gè)對(duì)象有什么不同的情況
我們可以看到EF為Student生成了值為System.Data.Entity.DynamicProxies.Student_...的代理實(shí)體
而對(duì)于Teacher,返回的就是我們所定義的實(shí)體。
如果我們?cè)赪CF中分別定義一個(gè)契約,來返回這兩個(gè)實(shí)體會(huì)怎么樣呢?
[OperationContract]Student GetStudent();[OperationContract]Teacher GetTeacher();實(shí)現(xiàn)方法
public Student GetStudent(){using (MyContext context = new MyContext()){return context.Students.FirstOrDefault();}}public Teacher GetTeacher(){using (MyContext context = new MyContext()){return context.Teachers.FirstOrDefault();}}調(diào)用 WCF進(jìn)行測(cè)試,我們可以很好的得到GetTeacher()的值,如圖
但是,當(dāng)調(diào)用GetStudent()方法,從服務(wù)端返回結(jié)果到客戶端時(shí)確報(bào)錯(cuò)了。
嗯,沒錯(cuò),就是剛開始我說的那個(gè)錯(cuò)誤。但,這是為什么呢。我們明明在Student中加了DataContract和DataMember關(guān)鍵字啊。
原因就是EF自動(dòng)為Student生成了代理類,WCF序列化的其實(shí)是EF生成的那個(gè)代理類,而不是我們自己定義的Student,而代理類并沒有標(biāo)識(shí)這是一個(gè)可以序列化的實(shí)體。
解決方法
?1.禁用代理類
既然原因是EF生成了代理類,那我們把它禁用了就可以了嘛。也很簡(jiǎn)單,只要將生成代理的配置設(shè)置為false即可。
public MyContext(){this.Configuration.ProxyCreationEnabled = false;}禁用后,看看通過EF獲取Student是怎么樣的。
沒錯(cuò),代理類沒了,但是我們不能直接通過導(dǎo)航屬性來獲取Teacher了。這可是殺敵一千,自損八百啊。有沒有更好的辦法呢?
2 反序列化
既然代理類是由實(shí)體序列化而來的,我們就可以在返回?cái)?shù)據(jù)前將代理類序列化成我們所需要的實(shí)體。
public Student GetStudent(){using (MyContext context = new MyContext()){var stu=context.Students.FirstOrDefault();var serializer = new DataContractSerializer(typeof(Student), new DataContractSerializerSettings(){DataContractResolver = new ProxyDataContractResolver()});using (var stream = new MemoryStream()){// 反序列化serializer.WriteObject(stream, stu);stream.Seek(0, SeekOrigin.Begin);var newStu = (Student)serializer.ReadObject(stream);return newStu;}}}通過這個(gè)方法,再測(cè)試一下.
不錯(cuò),沒有報(bào)錯(cuò),并且成功的得到了我們想要的結(jié)果。
但每個(gè)方法都要這樣序列化一下,是不是很麻煩,有沒有更好的方法。
答案肯定有,我們可以通過自定義Attribute,加在服務(wù)契約上面,標(biāo)識(shí)通過這個(gè)服務(wù)返回的方法都要進(jìn)行反序列化。
public class ProxyDataContractResolver: DataContractResolver{private XsdDataContractExporter _exporter = new XsdDataContractExporter();public override Type ResolveName( string typeName, string typeNamespace, Type declaredType,DataContractResolver knownTypeResolver){return knownTypeResolver.ResolveName(typeName, typeNamespace, declaredType, null);}public override bool TryResolveType(Type dataContractType,Type declaredType,DataContractResolver knownTypeResolver,out XmlDictionaryString typeName,out XmlDictionaryString typeNamespace){Type nonProxyType = ObjectContext.GetObjectType(dataContractType);if (nonProxyType != dataContractType){// Type was a proxy type, so map the name to the non-proxy nameXmlQualifiedName qualifiedName = _exporter.GetSchemaTypeName(nonProxyType);XmlDictionary dictionary = new XmlDictionary(2);typeName = new XmlDictionaryString(dictionary,qualifiedName.Name, 0);typeNamespace = new XmlDictionaryString(dictionary,qualifiedName.Namespace, 1);return true;}else{// Type was not a proxy type, so do the defaultreturn knownTypeResolver.TryResolveType(dataContractType,declaredType,null,out typeName,out typeNamespace);}}}public class ApplyProxyDataContractResolverAttribute : Attribute, IOperationBehavior{public void AddBindingParameters(OperationDescription description, BindingParameterCollection parameters){}public void ApplyClientBehavior(OperationDescription description, ClientOperation proxy){DataContractSerializerOperationBehaviordataContractSerializerOperationBehavior =description.Behaviors.Find<DataContractSerializerOperationBehavior>();dataContractSerializerOperationBehavior.DataContractResolver = new ProxyDataContractResolver();}public void ApplyDispatchBehavior(OperationDescription description, DispatchOperation dispatch){DataContractSerializerOperationBehaviordataContractSerializerOperationBehavior = description.Behaviors.Find<DataContractSerializerOperationBehavior>();dataContractSerializerOperationBehavior.DataContractResolver = new ProxyDataContractResolver();}public void Validate(OperationDescription description){}}
類ApplyProxyDataContractResolverAttribute就是我們想要的結(jié)果?,F(xiàn)在我們只要在定義服務(wù)契約的時(shí)候,加上ApplyProxyDataContractResolver關(guān)鍵字就可以了。
[OperationContract][ApplyProxyDataContractResolver]Student GetStudent();[OperationContract][ApplyProxyDataContractResolver]Teacher GetTeacher();擴(kuò)展
對(duì)于繼承類的序列化,要在基類用KnownType屬性來標(biāo)識(shí)
[KnownType(typeof(ClassB))][KnownType(typeof(ClassA))][DataContract]public class BaseClass{}[DataContract]public class ClassA : BaseClass{}[DataContract]public class ClassB : BaseClass{}PS:雖然這樣可以解決問題,但是多一層序列化會(huì)影響效率,希望EF的后續(xù)版本可以解決問題吧。
轉(zhuǎn):http://www.cnblogs.com/Gyoung/p/3153875.html
總結(jié)
以上是生活随笔為你收集整理的Entity Framework在WCF中序列化的问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 哪家公司做防火限流保护器专业?
- 下一篇: 日立中央空调如何设置感应区域