3

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

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

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

4

1 回答 1

1

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

举个例子:假设你有一个Customer对象,它有一个关联ReferredBy对象,它引用另一个客户。在您的应用程序中,您希望返回一个Customers 列表以及Customer引用每个 s 的相关联的人。

您的 WebAPI 方法可能类似于:

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

当序列化程序掌握了这一点时,您可能会遇到各种错误,您可以在本文中了解更多信息。但从本质上讲,它来自两件事:

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

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

  1. 使用 JSON.net 作为您的序列化程序。您可以参考此链接以获取有关如何在 Visual Studio 中将其设置为项目的默认序列化程序的说明(假设还没有)
  2. 在您Global.asax.cs作为配置的一部分加载的文件之一中,使用以下设置:

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

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

  3. MergeOptionDataContext及其每个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,并自动禁用ProxyCreationand LazyLoading。当您创建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
 }
}]
于 2013-04-24T20:49:23.277 回答