尝试将数据从 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&lt;Tab&gt; GetTabs()的签名改成IEnumerable&lt;TabModel&gt; GetTabs()

现在 TabController 更加简洁,因为它不会将 EF 对象暴露给外部世界,并且 Data Constracts 层可以处理一个漂亮而简单的 TabModel 对象。此外,如果数据模型需要在未来版本中更改,则 Web 界面可以保持不变。这在将来更改此代码时提供了很大的灵活性。

【讨论】:

查询位于“业务”项目中,因此我添加了您提到的 TabModel,并且得到“找不到类型或命名空间”。因为我的 MVC 项目中已经引用了 Business 项目,所以我不能反向引用,因为它给了我“可能的循环循环”错误。我是否只是将查询放入 MVC 项目中? DTO 与您的控制器属于同一个项目,只需要从控制器中引用,反之则不然。例如recentTabsInstance.GetTabs().MapTo&lt;TabModel&gt;() 或类似的。 做到了!非常感谢!【参考方案2】:

System.Data.Entity.DynamicProxies.Tab_3CD81772060539EC79CB677CA17899B90117059EFFC0976D087CA8B0FBE38520

这是您实体的延迟加载代理包装器。所以关闭该控制器的延迟加载。见:

EF Core

EF6

【讨论】:

我尝试关闭延迟加载,但仍然出现同样的错误。 如果有,您应该不会看到 DynamicProxy。 除非我没有正确点击 EF6 的链接:我添加了 this.Configuration.LazyLoadingEnabled = false;进入我的 dbcontext 的构造函数,但仍然收到 DynamicProxies.Tab 错误。

以上是关于尝试将数据从 db 返回到控制器并出现“如果您正在使用 DataContractSerializer,请考虑使用 DataContractResolver”错误的主要内容,如果未能解决你的问题,请参考以下文章

将多个参数从控制器传递到工厂服务进行 REST 调用并返回(可选)数据

将视图中的 db 数据返回给 doophp

从数据库返回数据到控制器

如何将数据从代码插入 Db 并创建 POST 方法

通过jquery将数据库中的列填充到select2中

(rails) 从 DB 中获取并渲染成 HTML