WebAPI和实体框架*没有*延迟加载

时间:2012-11-23 14:11:04

标签: entity-framework lazy-loading asp.net-web-api

在我当前的项目中,我首先使用 WebAPI EntityFramework 模型。经过一些调查后,似乎我必须使用LazyLoading才能加载相关实体。

我在很多博客中都读过,在服务中使用LazyLoading会导致性能和序列化问题 - 所以我想尽可能避免使用它。

关于如何在不创建POCO对象的情况下实现此目的的任何建议?

1 个答案:

答案 0 :(得分:1)

您问题的简单答案是 - 使用.Include()

作为示例:假设您有一个Customer对象,该对象具有关联的ReferredBy对象,该对象引用另一个客户。在您的应用程序中,您希望返回Customer的列表以及引用每个Customer的关联[HttpGet] public IQueryable<Customer> Customers() { return db.Customers.OrderBy(c => c.LastName).Top(10); }

您的WebAPI方法可能类似于:

A

当序列化程序得到此信息时,您可能会遇到各种错误,您可以阅读更多关于in this article的错误。但实质上,它来自两件事:

  1. ProxyCreation / LazyLoading - 这是EF代表“仅在我要求时为我的对象图加载关联对象”,并且
  2. 循环的序列化 - 这意味着 - B表示B,而A表示Global.asax - 所以每次我序列化一个,我再次序列化另一个一个孩子。这会产生无限循环。
  3. 我不会涉及所有细节或其他问题 - 我已经给了你一篇深入研究的文章。相反,这是我在我的应用程序中解决问题的方式:

    1. 使用JSON.net作为序列化程序。您可以参考this link获取有关如何在Visual Studio中将其设置为项目的默认序列化程序的说明(假设它尚未存在)
    2. .cs或其中一个config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; 文件中,这些文件是作为配置的一部分加载的,请使用以下设置:

      MergeOption

      这告诉JSON.net 序列化参考循环。

    3. DataContext及其Collection个属性中的MergeOption.NoTracking设置为.tt。在我的应用程序中,我通过编辑创建DataContext

      MergeOption _defaultMergeOption = MergeOption.AppendOnly; public <#=code.Escape(container)#>() : this("name=<#=container.Name#>") { } public <#=code.Escape(container)#>(String connectionString) : base(connectionString) { <# if (!loader.IsLazyLoadingEnabled(container)) { #> this.Configuration.LazyLoadingEnabled = false; <# } #> this.Configuration.ProxyCreationEnabled = false; _defaultMergeOption = MergeOption.NoTracking; } 文件来完成此操作

      首先,找到创建构造函数的行,然后更改为:

      <#  foreach (var entitySet in container.BaseEntitySets.OfType<EntitySet>()) { #>
      

      找到以:

      开头的行
      <#  foreach (var entitySet in container.BaseEntitySets.OfType<EntitySet>()) { #>
      <#= Accessibility.ForReadOnlyProperty(entitySet)#> ObjectQuery<<#=typeMapper.GetTypeName(entitySet.ElementType)#>> <#=code.Escape(entitySet)#> {
          get {
              var set = (((IObjectContextAdapter)this).ObjectContext).CreateObjectSet<<#=typeMapper.GetTypeName(entitySet.ElementType)#>>();
              set.MergeOption = _defaultMergeOption;
      
              return set;
          }
      }
      <#  }
      

      现在编辑下几行说:

      DataContext

      这样做会为您MergeOption.NoTracking提供一个构造函数,该构造函数可以将所有集合默认为ProxyCreation,并自动禁用LazyLoadingDataContext。当您创建var db = new MyDataContext("ConnectionStringGoesHere"); 的实例以进行拉取时,您现在可以简单地说:

      [HttpGet]
      public IQueryable<Customer> Customers() {
          return db.Customers.Include("ReferredBy")
                   .OrderBy(c => c.LastName).Top(10);
      }
      
    4. 鉴于上述情况,您的新WebAPI方法变得如此简单:

      .Include()

      [{ Id: 1, FirstName: 'Frank', LastName: 'Abba', ReferredBy: { Id: 4, FirstName: 'Bob', LastName: 'Jones', ReferredBy: null } }, { Id: 4, FirstName: 'Bob', LastName: 'Jones', ReferredBy: null } }] 将子记录作为初始SQL语句的一部分加载(一次命中总数),Serializer将忽略后引用,允许您生成类似于以下内容的JSON:

      {{1}}