EF查询结果返回Web API控制器时出现DataContractSerializer序列化错误如何解决
问题根因
你的报错本质是Entity Framework默认启用了动态代理生成(用于延迟加载、变更追踪),你从EF查询返回的Tab对象不是你定义的原始实体类实例,而是EF动态生成的System.Data.Entity.DynamicProxies.Tab_xxxx代理子类实例,Web API的XML序列化器识别不了这个动态生成的类型,所以抛出序列化异常。
你之前的KnownType用法完全错误:
KnownType特性只能修饰类、结构体、枚举这些类型定义,不能修饰控制器的Action方法- 你加的
[KnownType(typeof(DashboardActions))]完全不相关,序列化的是Tab类的实例,和DashboardActions没有任何关系
解决方案
方案1:查询时禁用代理生成,返回原始实体实例(最推荐)
修改DashboardActions.cs的GetTabs方法,在Context初始化时关闭代理创建,同时要注意你当前的查询是延迟执行的,using块释放Context之后才枚举会触发上下文已释放的报错,所以要加.ToList()立即执行查询:
public IEnumerable<Tab> GetTabs() { IEnumerable<Tab> recentPages = null; try { using (var context = new Clarity.BusinessLayer.CLARITY_DNN()) { // 禁用EF动态代理生成 context.Configuration.ProxyCreationEnabled = false; recentPages = (from t in context.Tabs join tp in context.TabPermissions on t.TabID equals tp.TabID where tp.RoleID == -1 && tp.AllowAccess && tp.PermissionID == 3 && !t.IsDeleted && t.IsVisible && t.PortalID == 0 && !string.IsNullOrEmpty(t.Description) && t.TabName != "Home" select t) .OrderByDescending(p => p.LastModifiedOnDate ?? p.CreatedOnDate) .Take(20) .ToList(); // 立即执行查询,避免Context释放后访问报错 } } catch (Exception e) { Console.WriteLine(e.Message); } return recentPages; }
这个方案不需要改任何其他配置,代码侵入最小,也能避免延迟加载导致的额外查询问题。
方案2:全局关闭EF代理生成
如果你全局都不需要EF的延迟加载、动态代理功能,可以在你的DbContext构造函数里统一关闭:
public CLARITY_DNN() : base("name=CLARITY_DNN") { this.Configuration.ProxyCreationEnabled = false; }
之后所有查询返回的都是原始实体实例,不需要每个查询单独设置。
方案3:配置Web API序列化器支持代理类(不推荐)
如果你确实需要保留EF动态代理功能,可以修改Web API的Xml序列化器配置,在Global.asax.cs或者WebApiConfig的Register方法里添加:
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter; xml.SetSerializer<IEnumerable<Tab>>(new DataContractSerializer(typeof(IEnumerable<Tab>), new DataContractSerializerSettings { DataContractResolver = new ProxyDataContractResolver() }));
这个方案需要引用System.Data.Objects命名空间,而且只对XML序列化生效,如果你还要支持JSON序列化还要额外配置JSON序列化器,不如直接关闭代理方便。
内容的提问来源于stack exchange,提问作者jmath412
相关产品推荐
相关产品推荐

