我读过一篇文章,在实体框架中,我们调用.ToList(), Single(), or First()
后将查询发送到数据库
我有数以千计的数据,而不是加载我想要在分页中返回数据的所有数据。所以我使用PagedList在MVC中创建分页。如果在我们调用示例products.ToPagedList(pageNumber, 10)
时没有错,则只需要10条数据记录,而不是整个数据。我是对的吗?
接下来,我正在使用automapper从实体映射到viewmodel。
List<ProductViewModel> productsVM = Mapper.Map<List<Product>, List<ProductViewModel>>(products);
return productsVM.ToPagedList(pageNumber, 10);
正如您在上面的代码段中看到的那样,在调用.ToPagedList()
之前,它只需要10条记录吗?如果我们进行映射,它将调用.ToList()
内部,我认为它将调用数据库中的所有数据然后返回10条记录。如何追踪它?
答案 0 :(得分:0)
查看数据库级别的最简单方法是使用Sql Server Profiler。然后,您将能够看到实体框架正在执行的SQL查询。
如果您使用的是Sql Express,那么您可以使用Sql Express Profiler执行相同的操作。
答案 1 :(得分:0)
不,它在分页列表之前不会占用10条记录。显示代码的方式,AutoMapper
将导致查询的延迟执行,在它到达分页列表之前,这意味着它将返回所有数据(假设,1000条记录)。然后PagedList
将正确检索已实现的List
中的10个,并确认记录总数为1000。
我认为您希望在数据库中过滤10,这样会有更好的性能,因此您应该在数据库实体的PagedList
中使用IQueryable
,如下所示:
List<Product> filteredProducts = dbContext.Products.OrderBy(p => p.ProductId).ToPagedList(pageNumber, 10);
return Mapper.Map<List<Product>, List<ProductViewModel>>(filteredProducts);
OrderBy
必须PagedList
。
请考虑以下情形。如果您的Product
实体与ProductReview
(ICollection<ProductReview>
)有子女关系,请执行以下操作:
public class ProductReview
{
public int ProductId { get; set; }
public string Description { get; set; }
public int ReviewerId { get; set; }
public double Score { get; set; }
}
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<ProductReview> Reviews { get; set; }
}
...您的ProductViewModel
有一个int
属性ReviewsCount
会在您的视图中显示?
当Automapper
将您的实体映射并转换到视图模型中时,它会访问列表中每个产品的Reviews
属性(让我们假设,在你的情况下,一个接一个,并Reviews.Count()
填写ReviewsCount
中的ProductViewModel
。
考虑到我的示例,即我从不急于加载Reviews
个产品,如果启用了延迟加载,AutoMapper
将执行十个查询(每个产品一个)来计算{ {1}}。伯爵是一个快速的操作,十个产品只是少数。但如果不是计算你实际上是将Reviews
映射到ProductReview
,那么这将是有点沉重的。如果Lazy Load已关闭,我们会收到异常,因为ProductReviewViewModel
为Reviews
。
一种可能的解决方案是在映射期间急切加载您可能需要的所有孩子,如下所示:
null
...因此,只需一个查询即可检索10个产品及其评论,而AutoMapper不会执行任何其他查询。
List<Product> filteredProducts = dbContext.Products.Include("Reviews").OrderBy(p => p.ProductId).ToPagedList(pageNumber, 10);
return Mapper.Map<List<Product>, List<ProductViewModel>>(filteredProducts);
以避免多次查询吗?加载所有评论及其所有昂贵的字段(例如Reviews
可能有数千个字符<}>
是的,绝对的。对于这些情况,请避免将Description
与PagedList
混合使用。
只需做这样的投影:
AutoMapper
现在您正在加载您的10个产品,将它们投影到 List<Product> filteredProducts = dbContext.Products
.Select(p => new ProductViewModel
{
ProductId = p.ProductId,
ProductName = p.Name,
ProductDescription = p.Description,
ReviewsCount = p.Reviews.Count(),
ScoreAverage = p.Reviews.Select(r => r.Score).DefaultIfEmpty().Average()
})
.OrderBy(p => p.ProductId).ToPagedList(pageNumber, 10);
,计算ProductViewModel
计数和得分平均值,而无需从数据库中检索所有Reviews
。
当然,在某些情况下,您可能确实需要加载/实现所有子实体,但除此之外,投影ftw。
您还可以将Reviews
部分放在扩展类中,并将所有投影封装在扩展方法中,这样就可以像Select()
一样重复使用它们。
我不是说AutoMapper
是邪恶的,你不应该使用它,我在某些情况下自己使用它,你只需要在适当的时候使用它。< / p>
我找到了this question,其中@GertArnold解释了关于AutoMapper
的以下内容:
...代码库增加了对已翻译的投影的支持 到表达式,最后是SQL
所以要开心,按照他的建议。