【问题标题】:ASP.NET MVC 4 - Using Linq to read databse once & use multiple timesASP.NET MVC 4 - 使用 Linq 读取数据库一次并多次使用
【发布时间】:2013-06-09 04:27:31
【问题描述】:

我正在使用 Linq 创建 4 个对象集合:

var Restaurants = db.Places.Where(p => p.Tags.Any(t => t.Name == "Restaurant")).ToList();
var Bars = db.Places.Where(p => p.Tags.Any(t => t.Name == "Bar")).ToList();
var Pubs = db.Places.Where(p => p.Tags.Any(t => t.Name == "Pub")).ToList();
var Hotels = db.Places.Where(p => p.Tags.Any(t => t.Name == "Hotel")).ToList();

显然最好从数据库中读取一次,上面是在 SQL Server 2012 上打开 4 个连接和 4 个查询还是 1 个?

如果我改为执行以下操作,Entity Framework 是否只会从数据库中读取一次,我自己测试的最佳方法是什么?

var places = db.Places;
var Restaurants = places.Where(p => p.Tags.Any(t => t.Name == "Restaurant")).ToList();
var Bars = places.Where(p => p.Tags.Any(t => t.Name == "Bar")).ToList();
var Pubs = places.Where(p => p.Tags.Any(t => t.Name == "Pub")).ToList();
var Hotels = places.Where(p => p.Tags.Any(t => t.Name == "Hotel")).ToList();

这里的最佳实践是什么?有这方面的统计数据吗?

谢谢。

【问题讨论】:

  • 这些似乎是等价的。两者都将导致对数据库的四次命中。如果您希望它们不同,则第二个代码块的开头应为:var places = db.places.ToList();

标签: sql-server asp.net-mvc linq entity-framework asp.net-mvc-4


【解决方案1】:
var types = new[]{"Restaurant","Bar","Pub","Hotel"};
var places = db.Places.Include(p=>p.Tags).Where(p=>p.Tags.Any(t=>types.Contains(t.Name))).ToList();

var Restaurants = places.Where(p => p.Tags.Any(t => t.Name == "Restaurant"));
var Bars = places.Where(p => p.Tags.Any(t => t.Name == "Bar"));
var Pubs = places.Where(p => p.Tags.Any(t => t.Name == "Pub"));
var Hotels = places.Where(p => p.Tags.Any(t => t.Name == "Hotel"));

应该只产生 1 个查询。虽然,您正在使用水合标记 DbSet 填充位置,这可能会显着减慢查询速度。您可能可以创建一个稍微不同的查询来检索 Places 并存储它的类型,这样您就不必拥有一个完全水合的 Tags DbSet,但如果您认为有必要,我会将其留给您。

【讨论】:

  • 有用地提及“水合作用”——我对此并不陌生,当然也会对此进行研究......
【解决方案2】:

您可以欺骗 Entity Framework 将它们作为单个查询的一部分加载,如下所示:

var q = 
    from p in db.Places
    group p by 1 into g
    select new {
        Restaurants = g.Where(p => p.Tags.Any(t => t.Name == "Restaurant")),
        Bars = g.Where(p => p.Tags.Any(t => t.Name == "Bar")),
        Pubs = g.Where(p => p.Tags.Any(t => t.Name == "Pub")),
        Hotels = g.Where(p => p.Tags.Any(t => t.Name == "Hotel"))
    }
var results = q.Single();

【讨论】:

  • 非常好 - 所以我会访问这样的? q.餐厅 - 等等?
  • @niico:我更新了示例以表明您想要获取此查询应返回的单个对象(因为它按常量分组)。那么是的,你可以访问results.Restaurants等。
【解决方案3】:

您的两个代码都会导致对 DB 的四个查询。

在 NHibernate 中,您可以使用 future 来做到这一点:

var Restaurants = places.Where(p => p.Tags.Any(t => t.Name == "Restaurant")).Future<Place>();
var Bars = places.Where(p => p.Tags.Any(t => t.Name == "Bar")).Future<Place>();
var Pubs = places.Where(p => p.Tags.Any(t => t.Name == "Pub")).Future<Place>();
var Hotels = places.Where(p => p.Tags.Any(t => t.Name == "Hotel")).Future<Place>();

只要数据库支持,NHibernate 将在其中任何一个请求结果时在一次往返中设置四个查询。

我认为 EF 目前没有类似的功能,但在某些情况下,就像您展示的那样,您可以 OR 子句:

var myPlaces = places.Where(p => p.Tags.Any(t => t.Name == "Restaurant")
    || p.Tags.Any(t => t.Name == "Bar")
    || t.Name == "Pub"
    || t.Name == "Hotel")).ToList();

尽管您显然无法将它们按类型分开,并且必须在内存中再次过滤它们。这是否会导致性能提升各不相同。如果不合理,我建议不要这样做。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-13
    • 1970-01-01
    • 2010-09-05
    • 1970-01-01
    • 1970-01-01
    • 2021-07-29
    • 1970-01-01
    相关资源
    最近更新 更多