【问题标题】:WebAPI and EntityFramework *without* LazyLoadingWeb API 和实体框架*没有*延迟加载
【发布时间】:2012-11-23 14:11:03
【问题描述】:

在我当前的项目中,我首先使用 WebAPIEntityFramework 模型。经过一番调查,看来我必须使用LazyLoading 才能加载相关实体。

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

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

【问题讨论】:

  • 如果我的回答对您有所帮助,请考虑通过单击其分数下方的绿色复选框将其标记为“已接受”。如果没有,请让我知道您需要什么进一步的信息,以便我可以帮助您解决问题。

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


【解决方案1】:

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

例如:假设您有一个 Customer 对象,该对象具有关联的 ReferredBy 对象,该对象引用另一个客户。在您的应用程序中,您希望返回 Customers 的列表以及相关联的 Customer 是谁推荐了每个人。

您的 WebAPI 方法可能类似于:

[HttpGet]
public IQueryable<Customer> Customers() {
    return db.Customers.OrderBy(c => c.LastName).Top(10);
}

当序列化程序掌握这一点时,您可能会遇到各种错误,您可以阅读有关in this article 的更多信息。但从本质上讲,它来自两件事:

  1. ProxyCreation / LazyLoading - EF 表示“仅在我请求时才按需加载我的对象图的关联对象”,并且
  2. 循环序列化——这意味着——A 指的是BB 指的是A——所以每次我序列化一个循环时,我都会再次序列化另一个循环作为它的子节点。这会创建一个无限循环。

我不会详细介绍所有细节或其他问题 - 我已经给您提供了一篇深入探讨它的文章。相反,这是我在应用程序中解决问题的方法:

  1. 使用 JSON.net 作为您的序列化程序。您可以参考this link,了解如何在 Visual Studio 中将其设置为项目的默认序列化程序(假设尚未设置)
  2. 在您的Global.asax 或作为配置的一部分加载的.cs 文件之一中,使用以下设置:

    config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = 
        Newtonsoft.Json.ReferenceLoopHandling.Ignore;
    

    这告诉 JSON.net 不要序列化引用循环。

  3. DataContext 中的MergeOption 及其每个Collection 属性设置为MergeOption.NoTracking。在我的应用程序中,我通过编辑创建 DataContext.tt 文件来做到这一点:

    首先,找到创建构造函数的那一行,改成:

    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,并自动禁用ProxyCreationLazyLoading。当您创建 DataContext 的实例以从中提取时,您现在可以简单地说:

    var db = new MyDataContext("ConnectionStringGoesHere");
    

鉴于上述情况,您的新 WebAPI 方法变得如此简单:

[HttpGet]
public IQueryable<Customer> Customers() {
    return db.Customers.Include("ReferredBy")
             .OrderBy(c => c.LastName).Top(10);
}

.Include() 将加载子记录作为初始 SQL 语句的一部分(总共对数据库进行一次命中),并且序列化程序将忽略反向引用,从而允许您生成类似于以下内容的 JSON:

[{
    Id: 1,
    FirstName: 'Frank',
    LastName: 'Abba',
    ReferredBy: {
        Id: 4,
        FirstName: 'Bob',
        LastName: 'Jones',
        ReferredBy: null
    }
 }, {
    Id: 4,
    FirstName: 'Bob',
    LastName: 'Jones',
    ReferredBy: null
 }
}]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-14
    • 1970-01-01
    • 1970-01-01
    • 2011-10-10
    相关资源
    最近更新 更多