Entity Framework在WCF中序列化的问题
问题描述
如果你在WCF中用Entity Framework来获取数据并返回实体对象,那么对下面的错误一定不陌生。
接收对 http://localhost:5115/ReService.svc 的 HTTP 响应时发生错误。这可能是由于服务终结点绑定未使用 HTTP 协议造成的。
这还可能是由于服务器中止了 HTTP 请求上下文(可能由于服务关闭)所致。有关详细信息,请参见服务器日志。
这就是因为在返回数据的时候,序列化失败,导致WCF服务自动停止了。
为什么会序列化失败
为了方便说明,我们先做个示例来重现这个错误。
默认情况下,Entity Framework为了支持它的一些高级特性(延迟加载等),默认将自动生成代理类是设置为true,即
public MyContext()
{
this.Configuration.ProxyCreationEnabled = true;
}
这样,如果我们的实体中包含其它实体的导航属性,则EF会自动的为这个实体生成代理类。
[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; }
}
观察上面两个实体,Student中有对Teacher的导航属性,而Teacher则没有。我们看看通过EF对获取这两个对象有什么不同的情况

我们可以看到EF为Student生成了值为System.Data.Entity.DynamicProxies.Student_...的代理实体

而对于Teacher,返回的就是我们所定义的实体。
如果我们在WCF中分别定义一个契约,来返回这两个实体会怎么样呢?
[OperationContract]
Student GetStudent(); [OperationContract]
Teacher GetTeacher();
实现方法
public Student GetStudent()
{
using (MyContext context = new MyContext())
{
return context.Students.FirstOrDefault();
}
} public Teacher GetTeacher()
{
using (MyContext context = new MyContext())
{
return context.Teachers.FirstOrDefault();
}
}
调用 WCF进行测试,我们可以很好的得到GetTeacher()的值,如图

但是,当调用GetStudent()方法,从服务端返回结果到客户端时确报错了。

嗯,没错,就是刚开始我说的那个错误。但,这是为什么呢。我们明明在Student中加了DataContract和DataMember关键字啊。
原因就是EF自动为Student生成了代理类,WCF序列化的其实是EF生成的那个代理类,而不是我们自己定义的Student,而代理类并没有标识这是一个可以序列化的实体。
解决方法
1.禁用代理类
既然原因是EF生成了代理类,那我们把它禁用了就可以了嘛。也很简单,只要将生成代理的配置设置为false即可。
public MyContext()
{
this.Configuration.ProxyCreationEnabled = false;
}
禁用后,看看通过EF获取Student是怎么样的。

没错,代理类没了,但是我们不能直接通过导航属性来获取Teacher了。这可是杀敌一千,自损八百啊。有没有更好的办法呢?
2 反序列化
既然代理类是由实体序列化而来的,我们就可以在返回数据前将代理类序列化成我们所需要的实体。
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(, SeekOrigin.Begin);
var newStu = (Student)serializer.ReadObject(stream);
return newStu;
}
}
}
通过这个方法,再测试一下.

不错,没有报错,并且成功的得到了我们想要的结果。
但每个方法都要这样序列化一下,是不是很麻烦,有没有更好的方法。
答案肯定有,我们可以通过自定义Attribute,加在服务契约上面,标识通过这个服务返回的方法都要进行反序列化。
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 name
XmlQualifiedName qualifiedName = _exporter.GetSchemaTypeName(nonProxyType);
XmlDictionary dictionary = new XmlDictionary();
typeName = new XmlDictionaryString(dictionary,
qualifiedName.Name, );
typeNamespace = new XmlDictionaryString(dictionary,
qualifiedName.Namespace, );
return true;
}
else
{
// Type was not a proxy type, so do the default
return 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)
{
DataContractSerializerOperationBehavior
dataContractSerializerOperationBehavior =
description.Behaviors.Find<DataContractSerializerOperationBehavior>();
dataContractSerializerOperationBehavior.DataContractResolver = new ProxyDataContractResolver();
} public void ApplyDispatchBehavior(OperationDescription description, DispatchOperation dispatch)
{
DataContractSerializerOperationBehavior
dataContractSerializerOperationBehavior = description.Behaviors.Find<DataContractSerializerOperationBehavior>();
dataContractSerializerOperationBehavior.DataContractResolver = new ProxyDataContractResolver();
}
public void Validate(OperationDescription description)
{
}
}
类ApplyProxyDataContractResolverAttribute就是我们想要的结果。现在我们只要在定义服务契约的时候,加上ApplyProxyDataContractResolver关键字就可以了。
[OperationContract]
[ApplyProxyDataContractResolver]
Student GetStudent(); [OperationContract]
[ApplyProxyDataContractResolver]
Teacher GetTeacher();
扩展
对于继承类的序列化,要在基类用KnownType属性来标识
[KnownType(typeof(ClassB))]
[KnownType(typeof(ClassA))]
[DataContract]
public class BaseClass
{
} [DataContract]
public class ClassA : BaseClass
{
} [DataContract]
public class ClassB : BaseClass
{
}
PS:虽然这样可以解决问题,但是多一层序列化会影响效率,希望EF的后续版本可以解决问题吧。
Entity Framework在WCF中序列化的问题的更多相关文章
- Entity Framework在WCF中序列化的问题(转)
问题描述 如果你在WCF中用Entity Framework来获取数据并返回实体对象,那么对下面的错误一定不陌生. 接收对 http://localhost:5115/ReService.svc 的 ...
- 在Entity Framework 4.0中使用 Repository 和 Unit of Work 模式
[原文地址]Using Repository and Unit of Work patterns with Entity Framework 4.0 [原文发表日期] 16 June 09 04:08 ...
- Entity Framework 4.3 中使用存储过程
尽管 Entity Framework 4.3 都已经发布了,且表示在 EF 5 中性能将会有很大提升.但很多存储过程控,始终不会放弃使用存储过程,那今天就让我们看看在 EF 4.3 中怎么使用存储过 ...
- Entity Framework 在MySQL中执行SQL语句,关于参数问题
在Entity Framework中添加MySQL模型,在写代码的过程中需要直接执行SQL语句. 在SQL语句中用到了@curRank := 0 这样在SQL语句中定义参数,同时还会有传入参数:ai. ...
- WCF中序列化(XML\JSON\Dt)
序列化 是将对象转换为容易传输的格式的过程.例如,可以序列化一个对象,然后使用 HTTP 通过 Internet 在客户端和服务器之间传输该对象.反之,反序列化根据流重新构造对象. 序列化描述了持久化 ...
- Entity Framework Code First 中使用 Fluent API 笔记。
在做MVC+EF CodeFirst 的Demo时,碰到的问题, 在组册用户时,要让用户输入确认密码,但是数据库中又不需要保存这个字段,解决方案很多了,这里我列出通过EF Code First的解决方 ...
- 使用Entity Framework和WCF Ria Services开发SilverLight之6:查找指定字段
对数据库表指定字段的查找,又是实际工作中的一项必要工作.SL客户端仅获取实际需要的指定的字段,好处很多,比如:有助于减少网络流量. 有两类这样的使用场景. 1:联表查询不需要外键表 在上一篇中,我们使 ...
- wcf中序列化BinaryFormatter,DataContractJsonSerializer,DataContractSerializer,SoapFormatter,XmlSerializer
using System; using System.Runtime.Serialization; using System.Xml.Serialization; namespace Larryle. ...
- EF Entity Framework Core DBContext中文文档
Add(Object) 以添加状态开始跟踪给定的实体和任何其他尚未被跟踪的可访问实体,以便在调用SaveChanges()时将它们插入数据库.使用State设置单个实体的状态. Add<TEnt ...
随机推荐
- 第27章 结构型模式大PK
27.1 代理模式 VS 装饰模式 27.1.1 代理模式 (1)场景:客人找运动员代理要求安排运动员参加比赛 (2)说明:代理人有控制权,可以拒绝客人的要求,也可以答应安排,甚至自己下去跑(因为有些 ...
- 移动前端UI选择
移动前端UI选择 目录 三.SUI Mobile 一.jQuery Mobile jQuery Mobile 是jQuery在移动设备上的版本,做为主要针对移动设备的框架来说,它提供一个移动设备平 ...
- Maven与Ant使用reportNG代替testng
大家在使用TestNG时一定会发现其本身的报告生成不但简陋而且相当的不美观,而ReportNG正是一款为TestNG量身定做的报告生成插件,其报告美观.简约.清晰.漂亮的特点让很多TestNG开始慢慢 ...
- JQuery 获取touchstart,touchmove,touchend 坐标
JQuery写法: $('#id').on('touchstart',function(e) { var _touch = e.originalEvent.targetTouches[0]; var ...
- Docker简明教程(以安装wget程序为例)
本文计划: 一.安装Docker(Centos) 二.注册Docker官网帐号 三.下载基础centos镜像,安装需要的软件和环境后,push到自己的repository 一.安装Docker(Cen ...
- Cordova - 与iOS原生代码交互2(使用Swift开发Cordova的自定义插件)
在前一篇文章中我介绍了如何通过 js 与原生代码进行交互(Cordova - 与iOS原生代码交互1(通过JS调用Swift方法)),当时是直接对Cordova生成的iOS工程项目进行编辑操作的(添加 ...
- vmware安装cent os 6.5 + oracle 11g xe + jboss eap 6.2 + weblogic 12c+ webshpere mq 7.5
前言: mac系统发展速度确实很快,短短数年,mac os上已经能网银支付(中行.招行.工商.支付宝等均已全面支持mac os了),windows上的经典常用软件:qq.飞信.旺旺.有道词典.有道云笔 ...
- 发布我的图片预加载控件YPreLoadImg v1.0
介绍 大家好!很高兴向大家介绍我的图片预加载控件YPreLoadImg.它可以帮助您预加载图片,并且能显示加载的进度,在预加载完成后调用指定的方法. YPreLoadImg控件由一个名为PreLoad ...
- Java 基础【10】 I/O流概念分析整理
转载地址:http://blog.csdn.net/yuebinghaoyuan/article/details/7388059 java.io 中的流,可以从不同的角度进行分类. 按照数据流的方向不 ...
- [codeforces 519E]E. A and B and Lecture Rooms(树上倍增)
题目:http://codeforces.com/problemset/problem/519/E 题意:给你一个n个点的树,有m个询问(x,y),对于每个询问回答树上有多少个点和x,y点的距离相等 ...