问题 具有EF Code First的WebApi在具有父子关系时会生成错误


我对这个问题感到不满。我确实在互联网上找到了一些关于它的东西,但不是一个明确的答案。我的问题:

我必须在MVC3 Web应用程序的Model部分中编写类: ParentClass和ChildClass 在ParentClass上有一个属性为List的类型的子属性

我使用了EF Code First,它在数据库中为我整齐地生成了父表和子表。

现在我需要一个REST服务,它返回一个List或一个ParentClass。

当我从ParentClass中删除属性Children时没有问题。但随着孩子们的到来,我不断收到错误。

错误: "The type System.Data.Entity.DynamicProxies.ParentClass_A0EBE0D1022D01EB84B81873D49DEECC60879FC4152BB115215C3EC16FB8003A was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically."}

一些代码:

类别:

     public class ParentClass
{
    public int ID { get; set; }
    public string Name {get;set;}
    public virtual List<ChildrenClass> Children { get; set; }

}

public class ChildrenClass
{
    public int ID { get; set; }
    public string MyProperty { get; set; }
}

服务:

[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
[ServiceBehavior(IncludeExceptionDetailInFaults = true)] 
public class MyService
{

    static MyContext db;
    public MyService() { db = new MyContext(); }


    [WebGet(UriTemplate = "")]
    public List<ParentClass> GetParents()
    {
        var result = db.Parents.ToList();
        return result;

    }

callinh这项服务时我不会得到结果。我究竟做错了什么?


9798
2017-11-17 20:04


起源



答案:


我不得不在上下文配置中使用DisableProxyCreation:

[OperationContract] 
[WebGet(UriTemplate = "")] 
public List<ParentClass> GetParents() 
{ 
     using (DBContext context = new DBContext()) 
     {
         context.Configuration.ProxyCreationEnabled = false; 
         List<ParentClass> parents = context.Parents
             .Include("Children") 
             .ToList();
         return parents; 
      }
}

这对我来说很好。


12
2017-11-18 16:34



正是我在寻找的东西。你知道禁用这个的任何缺点吗? - hooked82
Proxies有许多好的东西,跟踪更新等。忘记包含语句没有错误。我仍然没有序列化proxi实体的解决方案 - Poul K. Sørensen


答案:


我不得不在上下文配置中使用DisableProxyCreation:

[OperationContract] 
[WebGet(UriTemplate = "")] 
public List<ParentClass> GetParents() 
{ 
     using (DBContext context = new DBContext()) 
     {
         context.Configuration.ProxyCreationEnabled = false; 
         List<ParentClass> parents = context.Parents
             .Include("Children") 
             .ToList();
         return parents; 
      }
}

这对我来说很好。


12
2017-11-18 16:34



正是我在寻找的东西。你知道禁用这个的任何缺点吗? - hooked82
Proxies有许多好的东西,跟踪更新等。忘记包含语句没有错误。我仍然没有序列化proxi实体的解决方案 - Poul K. Sørensen


它似乎是为您的POCO序列化代理类,我的第一个建议是使用proxydatacontractresolver: http://msdn.microsoft.com/en-us/library/system.data.objects.proxydatacontractresolver.aspx

在加载通过网络服务发送的数据时,我还会明确说明明确的内容......即

将Parent类更改为

public class ParentClass
{
    public int ID { get; set; }
    public string Name {get;set;}
    public List<ChildrenClass> Children { get; set; }

}

更改内容以关闭延迟加载: 默认情况下,在Entity Framework 4中禁用延迟加载

并且在返回通过线路发送的数据时明确指定要加载的内容。

[WebGet(UriTemplate = "")]
public List<ParentClass> GetParents()
{
    var result = db.Parents.Include("Children").ToList();
    return result;

}

看看以下答案: 实体框架代码优先 - 渴望加载不按预期工作? 更高级的包含电话。

另外,根据经验提供的建议,我不会通过网络返回您的数据类,因为它们构成了Web服务的使用者的合同。您最好使用另一组类来映射数据值。

这样,如果数据类发生更改,除非明确要求,否则不必更改Web服务客户端。

如果您希望在Parent或Child类中有1000行,则使用分页很重要,否则您最终会选择N + 1,请参阅: 什么是SELECT N + 1?


0
2017-11-18 01:10



我已经尝试过DataContractResolver,但这并没有帮助。 disableproxy创建确实有帮助。但是,如果我理解正确,我应该将数据库中填充的POCO与要在服务中使用的类分开。但这并不意味着每个对象都需要重复,这意味着在站点和业务逻辑中使用ParentClass,在服务中使用单独的ParentClass。复制代码不是太多吗? - Mounhim
我们使用DTO和Automapper将我们的资源模型与域模型分开。这有许多优点,包括单独的验证和单独/不同的结构 - suing
复制您的POCO用于N种不同的用途往往是过度的,包括添加工作和地图/ xlats的维护。许多开发人员采用一次仔细定义其POCO的方法,然后将它们用于数据传输和数据访问,尤其是在较小的项目上。如果有时间不再起作用(模式更改,重构等),则应定义新的POCO并根据需要执行映射。这种转换/重构根本不会影响Web客户端,对于类库的.NET使用者来说,这只是一个问题。例如。二元依赖。 - Shaun Wilson
我同意复制POCO会适得其反。如果使用POCO背后的潜在意图不同,您应该有不同的POCO。根据经验,我知道数据层的类不应该作为数据收缩而通过线路公开。一旦更改数据库层,即推断数据协定已更改,并且所有使用的客户端也应更改。因此,重用数据POCO不允许您灵活地为客户端版本化数据/服务接口。 - JTew


在某些情况下,一个简单的解决方案是使用包装类,以便公开的所有属性都是已知类型。

通常,您不会在控制器类中使用ObjectContext或DbContext,因此在早期层(业务层或服务层)中,您可以从来自DB的对象快速转换为ViewModel样式对象,类似于您'在MVC应用程序中执行,但不是将它们传递给View,而是将它们返回给调用者。

也许你不能在所有情况下使用它,但通常这是一个可行的妥协。


0
2018-05-11 17:58