尝试将数据从 db 返回到控制器并出现“如果您正在使用 DataContractSerializer,请考虑使用 DataContractResolver”错误
Posted
技术标签:
【中文标题】尝试将数据从 db 返回到控制器并出现“如果您正在使用 DataContractSerializer,请考虑使用 DataContractResolver”错误【英文标题】:Trying to return data from db back to controller and getting "Consider using a DataContractResolver if you are using DataContractSerializer" error 【发布时间】:2021-11-04 12:29:27 【问题描述】:我在我的TabController.cs
内实例化DashboardActions.cs
的一个实例,它应该返回一个来自实体框架的IEnumerable<Tab>
。我已经展示了控制器正在实例化的 DashboardActions.cs 类的 TabController.cs
的 GET 方法和 GetTabs
方法的代码。 Tab
是我的 EF 模型中的一个实体。以下是完整的错误信息:
<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'.</ExceptionMessage>
<ExceptionType>System.InvalidOperationException</ExceptionType>
<StackTrace/>
<InnerException>
<Message>An error has occurred.</Message>
<ExceptionMessage>Type 'System.Data.Entity.DynamicProxies.Tab_3CD81772060539EC79CB677CA17899B90117059EFFC0976D087CA8B0FBE38520' with data contract name 'Tab_3CD81772060539EC79CB677CA17899B90117059EFFC0976D087CA8B0FBE38520:http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies' is not expected. Consider using a DataContractResolver if you are using DataContractSerializer or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to the serializer.</ExceptionMessage>
<ExceptionType>System.Runtime.Serialization.SerializationException</ExceptionType>
<StackTrace> at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeAndVerifyType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, Boolean verifyKnownType, RuntimeTypeHandle declaredTypeHandle, Type declaredType) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiType(XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle objectTypeHandle, Type objectType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle, Type declaredType) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.InternalSerialize(XmlWriterDelegator xmlWriter, Object obj, Boolean isDeclaredType, Boolean writeXsiType, Int32 declaredTypeID, RuntimeTypeHandle declaredTypeHandle) at WriteArrayOfTabToXml(XmlWriterDelegator , Object , XmlObjectSerializerWriteContext , CollectionDataContract ) at System.Runtime.Serialization.CollectionDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteDataContractValue(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle declaredTypeHandle) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeAndVerifyType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, Boolean verifyKnownType, RuntimeTypeHandle declaredTypeHandle, Type declaredType) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiTypeAtTopLevel(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle originalDeclaredTypeHandle, Type graphType) at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.DataContractSerializer.WriteObject(XmlWriter writer, Object graph) at System.Net.Http.Formatting.XmlMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content) at System.Net.Http.Formatting.XmlMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.WebHost.HttpControllerHandler.<WriteBufferedResponseContentAsync>d__22.MoveNext()</StackTrace>
</InnerException>
</Error>
TabController.cs
public IEnumerable<Tab> GetTabs()
IEnumerable<Tab> recentPages = null;
try
using (var context = new Clarity.BusinessLayer.CLARITY_DNN())
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);
catch (Exception e)
Console.WriteLine(e.Message);
return recentPages;
TabController.cs
public class TabController : ApiController
// GET: Dashboard
public IEnumerable<Tab> Get()
var recentTabsInstance = new DashboardActions();
var recentTabs = recentTabsInstance.GetTabs();
return recentTabs;
我尝试使用 KnownType 装饰器更改控制器操作,但无济于事:
public class TabController : ApiController
[KnownType(typeof(DashboardActions))]
[DataContract]
// GET: Dashboard
public IEnumerable<Tab> Get()
var recentTabsInstance = new DashboardActions();
var recentTabs = recentTabsInstance.GetTabs();
return recentTabs;
它一直说KnownType
只能用于类、结构等,但DashboardActions
是一个类。非常感谢任何帮助。
【问题讨论】:
【参考方案1】:最后一个错误意味着 KnownType 属性只能应用于一个类,在您的示例中,您将其应用于Get()
方法。但是,让我们忘记这一点。
根本问题是实体框架是如何工作的,以及这如何混淆数据契约层。简单的解决方案不是直接返回实体框架对象,而是将值复制到视图模型类中并作为结果返回。
因为 EF 在内部创建了额外的类型来保存值。这些看起来像您定义的类,但它们并不相同。
在这种情况下,EF 创建了 System.Data.Entity.DynamicProxies.Tab_3CD81772060539EC79CB677CA17899B90117059EFFC0976D087CA8B0FBE38520
类型来实现 Tab 类。 DataContracts 层无法识别此类。 (谁可以?老实说。)
所以我的解决方案是创建一个 TabModel 类并在查询中的select t)
行复制所需的值
新课程:
public class TabModel
public int TabID get;set;
public DateTime LastModifiedOnDate get;set;
public DateTime CreatedOnDate get;set;
// Other properties you need.
现在更改查询以返回此类型的对象,而不是 EF 对象本身。像这样结束查询:
select new TabModel()
TabID = t.TabID,
LastModifiedOnDate = t.LastModifiedOnDate,
CreatedOnDate= t.CreatedOnDate,
// Other properties
)
.OrderByDescending(p => p.LastModifiedOnDate ?? p.CreatedOnDate)
.Take(20);
别忘了把IEnumerable<Tab> GetTabs()
的签名改成IEnumerable<TabModel> GetTabs()
现在 TabController 更加简洁,因为它不会将 EF 对象暴露给外部世界,并且 Data Constracts 层可以处理一个漂亮而简单的 TabModel 对象。此外,如果数据模型需要在未来版本中更改,则 Web 界面可以保持不变。这在将来更改此代码时提供了很大的灵活性。
【讨论】:
查询位于“业务”项目中,因此我添加了您提到的 TabModel,并且得到“找不到类型或命名空间”。因为我的 MVC 项目中已经引用了 Business 项目,所以我不能反向引用,因为它给了我“可能的循环循环”错误。我是否只是将查询放入 MVC 项目中? DTO 与您的控制器属于同一个项目,只需要从控制器中引用,反之则不然。例如recentTabsInstance.GetTabs().MapTo<TabModel>()
或类似的。
做到了!非常感谢!【参考方案2】:
System.Data.Entity.DynamicProxies.Tab_3CD81772060539EC79CB677CA17899B90117059EFFC0976D087CA8B0FBE38520
这是您实体的延迟加载代理包装器。所以关闭该控制器的延迟加载。见:
EF Core
EF6
【讨论】:
我尝试关闭延迟加载,但仍然出现同样的错误。 如果有,您应该不会看到 DynamicProxy。 除非我没有正确点击 EF6 的链接:我添加了 this.Configuration.LazyLoadingEnabled = false;进入我的 dbcontext 的构造函数,但仍然收到 DynamicProxies.Tab 错误。以上是关于尝试将数据从 db 返回到控制器并出现“如果您正在使用 DataContractSerializer,请考虑使用 DataContractResolver”错误的主要内容,如果未能解决你的问题,请参考以下文章