当我期望可以从EF缓存中获取对象时,为什么Entity Framework执行查询?
使用这些简单的模型类:
public class Blog
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
public class Post
{
public int Id { get; set; }
public string Content { get; set; }
public virtual Blog Blog { get; set; }
}
public class BlogDbContext : DbContext
{
public BlogDbContext() : base("BlogDbContext") {}
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
}
我分析了后续操作的查询
public class HomeController : Controller
{
public ActionResult Index()
{
var ctx = new BlogDbContext();
// expecting posts are retrieved and cached by EF
var posts = ctx.Posts.ToList();
var blogs = ctx.Blogs.ToList();
var wholeContent = "";
foreach (var blog in blogs)
foreach (var post in blog.Posts) // <- query is executed
wholeContent += post.Content;
return Content(wholeContent);
}
}
为什么EF不重用我在Post
语句中抓住的var posts = ctx.Posts.ToList();
实体?
进一步的解释:
现有应用程序具有Excel导出报告。数据是通过主Linq2Sql查询获取的,该查询具有包含(〜20)的树。然后通过自动映射器进行映射,并添加来自手动缓存的附加数据(如果将其添加到主查询中,以前会减慢执行速度)。
现在,数据增长了,并且尝试执行错误查询时SQL Server崩溃:
查询处理器用尽了内部资源,无法生成查询计划。
延迟加载将导致> 100.000个查询。因此,我想我可以通过一些简单的查询来预加载所有必需的数据,并让EF在延迟加载期间自动使用缓存中的对象。
最初,我对TSQL IN()
子句的限制还有其他问题,我通过MoreLinq的Batch扩展解决了该问题。
答案 0 :(得分:0)
启用延迟加载后,EF仍将重新加载集合导航属性。可能是因为EF不知道您是否真的加载了所有帖子。像这样的EG代码
var post = db.Posts.First();
var relatedPosts = post.Blog.Posts.ToList();
会很棘手,因为Blog会已经加载了一个帖子,但是显然需要提取其他帖子。
无论如何,当依靠变更跟踪器修正导航属性时,无论如何都应该禁用延迟加载。 EG
using (var db = new BlogDbContext())
{
db.Configuration.LazyLoadingEnabled = false;
. . .
答案 1 :(得分:0)
鉴于您具有导航属性,请看一下如何在查询中利用它们来为Automapper提供动态对象,以将其映射到ViewModel / DTO而不是您急于加载或等待延迟的顶级实体正在加载。
这是通过在查询中发出.Select()完成的。使用一个简单的示例来提取订单详细信息,包括客户名称,订单行中的产品名称和数量列表以及订单中引用客户的交货地址,该客户具有交货地址的交货地址,订单行集合,每个都有一个产品...
var orderDetails = dbContext.Orders
.Where(o => /* Insert criteria */)
.Select(o => new
{
o.OrderId,
o.OrderNumber,
o.Customer.CustomerId,
CustomerName = x.Customer.FullName,
o.Customer.DeliveryAddress, // Address entity if no further dependencies, or extract fields/relations from the Address.
o.OrderLines.Select( ol = > new
{
ol.OrderLineId,
ProductName = ol.Product.Name,
ol.Quantity
}
}).ToList(); // Ready to feed into Automapper.
包含〜20个内容,您的Select无疑会更复杂一些,但其想法是向SQL Server提供查询以仅检索您想要的数据,然后您可以将其输入到Automapper中以导航任何子关系可以由EF展平或简化,然后返回给您的映射器以充实到生成的模型中。
随着系统的不断发展,您还需要考虑使用分页/ w跳过并获取而不是ToList,或者至少要利用获取来确保返回的数据量没有上限。 ToList是我在EF代码中寻找的主要性能巨魔,因为它的滥用会杀死应用程序。