【问题标题】:Help Refactoring Entity Framework 4 Query帮助重构实体框架 4 查询
【发布时间】:2011-05-21 22:42:49
【问题描述】:

我有两个实体:

  • 位置
  • 发布

1..* 在 LocationPost 之间。

Location 是抽象的,我有许多派生实体,例如 City。我为我的模型使用 Table-Per-Type 继承。

我正在尝试编写以下查询:(简化)

  • 获取排名前 20 的城市,并包含“评分最高的帖子”(最高评分)。

因此,Location 有一个名为 Posts 的导航属性。

要记住的一件事 - 我禁用延迟加载,因此我必须预先加载,或者执行两个查询

所以,这是我目前的查询。请记住,我需要从这个方法返回一个ICollection<Location>

public ICollection<Location> FindTopTwentyLocations()
{
    var results = new List<Location>();

    var cities = locationRepository
                   .Find()
                   .OfType<City>()
                   .Select(x => new
                    {
                       Location = x,
                       TopPost = x.Posts.OrderByDescending(r => x.Rating).FirstOrDefault()
                    }).Take(20).ToList();

    foreach (var city in cities)
    {
       var aggregatedCity = city.Location;
       aggregatedCity.Posts = new List<Post> { aggregatedCity.TopPost };
       results.Add(city);
    }

    return results;
}

所以本质上,我抓取了前 20 个城市,投影到一个匿名类型,这样我就可以抓取顶部的帖子,然后循环遍历该匿名类型的集合,将帖子推回“城市 "对象,以便添加到List&lt;Location&gt;的返回类型中。

  • 我不能使用.Include,因为它会返回所有帖子
  • 我不想执行 2 个查询
  • 我必须使用匿名类型投影,否则会抛出 EF 错误(无法翻译查询)

考虑到这些要点,有没有更好的方法可以做到这一点?我对var cities 查询相当满意,但我不太喜欢将匿名类型属性循环/复制到我的模型实体。

有什么想法吗?

编辑

我还注意到匿名类型的投影正在丢失我正在检索的急切加载的位置关联。

例如

var query = locationRepository.Find().OfType<City>().Include("State").ToList();

有效 - 返回所有“状态”关联。

但是:

var query = locationRepository.Find().OfType<City>().Include("State").Select(x => new {
   Location = x,
   TopPost = x.Posts.OrderByDescending(r => x.Rating).FirstOrDefault()
}).ToList();

导致所有“状态”关联为空。

奇怪!

【问题讨论】:

    标签: linq entity-framework entity-framework-4 linq-to-entities eager-loading


    【解决方案1】:

    很高兴将其保持开放一段时间并查看其他答案,但我坚持我现在所拥有的。

    关于我上面的编辑,我想提一点。

    将预先加载的查询投影到匿名类型似乎会丢失包含的关联。我不知道为什么。

    我使用的一种解决方法是在匿名类型中包含预先加载的关联:

    var cities = locationRepository
                       .Find()
                       .OfType<City>()
                       .Select(x => new
                        {
                           Location = x,
                           State = x.State, // include association in anon type
                           TopPost = x.Posts.OrderByDescending(r => x.Rating).FirstOrDefault()
                        }).Take(20).ToList();
    
        foreach (var city in cities)
        {
           var aggregatedCity = city.Location;
           aggregatedCity.State = city.State; // copy anon type association over
           aggregatedCity.Posts = new List<Post> { aggregatedCity.TopPost };
           results.Add(city);
        }
    

    这似乎行得通。

    在重构代码方面,我不认为var cities 查询可以优化。我试图将foreach 从左到右的复制移动到另一个.Select 投影中(例如,在查询实现之后 - 在.ToList() 之后),但为了做到这一点,我必须从左到 -正确复制所有属性,例如:

    var cities = locationRepository
                       .Find()
                       .OfType<City>()
                       .Select(x => new
                        {
                           Location = x,
                           State = x.State, // include association in anon type
                           TopPost = x.Posts.OrderByDescending(r => x.Rating).FirstOrDefault()
                        }).Take(20).ToList().Select(x => new City
                        {
                           CityName = Location.Name,
                           State = State,
                           // etc etc
                        });
    

    考虑到我的实体有 20 多个属性,我不想这样做。让我回到了 Linq-To-Sql 和 POCO 的痛苦世界。

    正如我所说 - 接受其他建议。

    编辑

    我最终在这里使用了存储过程。我的代码有效,但它有 100 多行。我宁愿将这 100 多行代码抽象成一个存储过程。

    此外,这个结果基本上是只读的(我不需要图表中的实体 - 我只是得到结果,显示它们并完成它)。

    【讨论】:

      【解决方案2】:

      我可能遗漏了一些东西,但你不能这样做吗?

      var cities = locationRepository 
                         .Find() 
                         .OfType<City>() 
                         .Select(x => 
                          { 
                             x.Posts = x.Posts.OrderByDescending(r => x.Rating).Take(1);
                             return x;
                          }).Take(20).ToList(); 
      

      或者,如果 EF 抱怨更改了 Posts 属性,则创建一个新的 Location 对象并在选择操作中添加 Post。

      【讨论】:

      • 有趣,我会试一试 - 不确定它是否有效,但我会尝试。哦,我不能创建一个新的位置,因为它是抽象的 - 我使用泛型,所以我的代码实际上是 .OfType&lt;T&gt; (where T : Location)。我不能“新建”一个城市(例如),因为我必须从左到右复制所有属性。不过我会试试你的例子,然后告诉你。
      • 不,无法让它工作。说“无法将 IEnumerable 转换为 ICollection”。如果我尝试做.ToList(),它会说“带有语句体的 lambda 表达式不能转换为表达式树”。无论如何,我已经让它使用 SPROC 更好/更快地工作。不过感谢您的帮助。 :)
      • 很公平,我知道有时设置那些 ORM 集合属性可能很困难。当查询变得复杂时,使用存储过程总是一个好主意。
      猜你喜欢
      • 2011-06-30
      • 1970-01-01
      • 2011-05-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-16
      • 2011-12-06
      • 1970-01-01
      相关资源
      最近更新 更多