在MVC中使用AutoMapper和PagedList

时间:2014-08-29 10:01:22

标签: asp.net-mvc entity-framework automapper pagedlist

我读过一篇文章,在实体框架中,我们调用.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条记录。如何追踪它?

2 个答案:

答案 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

小心使用AUTOMAPPER

请考虑以下情形。如果您的Product实体与ProductReviewICollection<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已关闭,我们会收到异常,因为ProductReviewViewModelReviews

一种可能的解决方案是在映射期间急切加载您可能需要的所有孩子,如下所示:

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可能有数千个字符<}>

是的,绝对的。对于这些情况,请避免将DescriptionPagedList混合使用。

只需做这样的投影:

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>

编辑:AUTOMAPPER支持投影

我找到了this question,其中@GertArnold解释了关于AutoMapper的以下内容:

  

...代码库增加了对已翻译的投影的支持   到表达式,最后是SQL

所以要开心,按照他的建议。