繁体   English   中英

ASP.NET MVC和EF4实体框架-使用实体与仅检索我需要的字段时是否存在性能问题?

[英]ASP.NET MVC & EF4 Entity Framework - Are there any performance concerns in using the entities vs retrieving only the fields i need?

假设我们有3个表,即用户,产品,购买。 有一个视图需要显示用户进行的购买。

我可以通过执行以下操作查找所需的数据:

from p in DBSet<Purchases>.Include("User").Include("Product") select p;

但是,我担心这可能会对性能产生影响,因为它将检索完整的对象。 另外,我可以只选择我需要的字段:

from p in DBSet<Purchases>.Include("User").Include("Product") select new SimplePurchaseInfo() { UserName = p.User.name, Userid = p.User.Id, ProductName = p.Product.Name ... etc };

所以我的问题是:这样做的最佳实践是什么?

==编辑

感谢所有的答复。

[问题1]:我想知道是所有视图都应与具有该视图非常特定的数据的平面ViewModel一起使用,还是应包含实体对象。

真实示例:用户评论产品

var query = from dr in productRepository.FindAllReviews()
            where dr.User.UserId = 'userid'
            select dr;
string sql = ((ObjectQuery)query).ToTraceString();

SELECT [Extent1].[ProductId] AS [ProductId], 
       [Extent1].[Comment] AS [Comment], 
       [Extent1].[CreatedTime] AS [CreatedTime], 
       [Extent1].[Id] AS [Id], 
       [Extent1].[Rating] AS [Rating], 
       [Extent1].[UserId] AS [UserId], 
       [Extent3].[CreatedTime] AS [CreatedTime1], 
       [Extent3].[CreatorId] AS [CreatorId], 
       [Extent3].[Description] AS [Description], 
       [Extent3].[Id] AS [Id1], 
       [Extent3].[Name] AS [Name], 
       [Extent3].[Price] AS [Price], 
       [Extent3].[Rating] AS [Rating1], 
       [Extent3].[ShopId] AS [ShopId], 
       [Extent3].[Thumbnail] AS [Thumbnail], 
       [Extent3].[Creator_UserId] AS [Creator_UserId], 
       [Extent4].[Comment] AS [Comment1], 
       [Extent4].[DateCreated] AS [DateCreated], 
       [Extent4].[DateLastActivity] AS [DateLastActivity], 
       [Extent4].[DateLastLogin] AS [DateLastLogin], 
       [Extent4].[DateLastPasswordChange] AS [DateLastPasswordChange], 
       [Extent4].[Email] AS [Email], 
       [Extent4].[Enabled] AS [Enabled], 
       [Extent4].[PasswordHash] AS [PasswordHash], 
       [Extent4].[PasswordSalt] AS [PasswordSalt], 
       [Extent4].[ScreenName] AS [ScreenName], 
       [Extent4].[Thumbnail] AS [Thumbnail1], 
       [Extent4].[UserId] AS [UserId1], 
       [Extent4].[UserName] AS [UserName]
       FROM    [ProductReviews] AS [Extent1]
       INNER JOIN [Users] AS [Extent2] ON [Extent1].[UserId] = [Extent2].[UserId]
       LEFT OUTER JOIN [Products] AS [Extent3] ON [Extent1].[ProductId] = [Extent3].[Id]
       LEFT OUTER JOIN [Users] AS [Extent4] ON [Extent1].[UserId] = [Extent4].[UserId]
       WHERE N'615005822' = [Extent2].[UserId]

要么

from d in productRepository.FindAllProducts()
from dr in d.ProductReviews
where dr.User.UserId == 'userid'
orderby dr.CreatedTime
select new ProductReviewInfo()
       {
           product = new SimpleProductInfo() { Id = d.Id, Name = d.Name, Thumbnail = d.Thumbnail, Rating = d.Rating },
           Rating = dr.Rating,
           Comment = dr.Comment,
           UserId = dr.UserId,
           UserScreenName = dr.User.ScreenName,
           UserThumbnail = dr.User.Thumbnail,
           CreateTime = dr.CreatedTime
       };

SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[Name] AS [Name], 
[Extent1].[Thumbnail] AS [Thumbnail], 
[Extent1].[Rating] AS [Rating], 
[Extent2].[Rating] AS [Rating1], 
[Extent2].[Comment] AS [Comment], 
[Extent2].[UserId] AS [UserId], 
[Extent4].[ScreenName] AS [ScreenName], 
[Extent4].[Thumbnail] AS [Thumbnail1], 
[Extent2].[CreatedTime] AS [CreatedTime]
FROM    [Products] AS [Extent1]
INNER JOIN [ProductReviews] AS [Extent2] ON [Extent1].[Id] = [Extent2].[ProductId]
INNER JOIN [Users] AS [Extent3] ON [Extent2].[UserId] = [Extent3].[UserId]
LEFT OUTER JOIN [Users] AS [Extent4] ON [Extent2].[UserId] = [Extent4].[UserId]
WHERE N'userid' = [Extent3].[UserId]
ORDER BY [Extent2].[CreatedTime] ASC

[问题2]:丑陋的外部连接怎么了?

通常,只检索您需要的内容,但请记住要检索足够的信息,以使您的应用程序不会显得过于闲谈,因此,如果您可以将很多东西分批处理,请这样做,否则,您将在每次需要时支付网络流量成本返回数据库并检索更多内容。

在这种情况下,假设您只需要这些信息,我将采用第二种方法(如果您确实需要)。

我认为第二个查询将引发异常,因为您无法将结果映射到Linq-to-entities中未映射的.NET类型。 您必须返回匿名类型并将其映射到Linq-to-objects中的对象,或者必须使用一些高级概念进行投影-QueryView(在ESQL中为投影)或DefiningQuery(将自定义SQL查询映射到新的只读实体)。

通常,它更多地是关于您实体的设计。 如果选择单个小实体,则全部加载而不是投影都没什么大不同。 如果要选择实体列表,则应考虑预测-特别是如果表包含诸如nvarchar(max)或varbinar(max)之类的列,则结果中不需要这些列!

当您要过滤(或为此排序)时,使用.Include预先加载.Include并不能很好地发挥作用。

第一个查询基本上是这样的:

select p.*, u.*, p2.*
from products p
left outer join users u on p.userid = u.userid
left outer join purchases p2 on p.productid = p2.productid
where u.userid == @p1

那真的是你想要的吗?

有一个视图需要显示用户进行的购买。

那么,为什么要包含“产品”?

不应该只是:

from p in DBSet<Purchases>.Include("User") select p;

您的第二个查询将出错。 必须投影到模型上的实体或匿名类型-而不是随机的类/ DTO。

老实说,当前情况下最简单,性能最好的选择是在FK本身上查询:

var purchasesForUser = DBSet<Purchases>.Where(x => x.UserId == userId);

那应该产生:

select p.*
from products p
where p.UserId == @p1

上面的查询当然需要您在模型中包括外键。

如果您的模型中没有FK,那么您将需要采用匿名类型投影的形式进行更多的LINQ-Entities欺骗。

总体而言,不要为了寻求优化外出 创建符合方案/业务需求的查询,然后根据需要进行优化-或寻找LINQ-Entities的替代方案,例如存储过程,视图或已编译的查询。

请记住:过早的优化是万恶之源。

* 编辑-响应问题更新*

[问题1]:我想知道是所有视图都应与具有该视图非常特定的数据的平面ViewModel一起使用,还是应包含实体对象。

是的-ViewModel仅应包含该View所需的内容。 否则为什么要有ViewModel? 您也可以直接绑定到EF模型。 因此,设置ViewModel仅包含视图所需的字段。

[问题2]:丑陋的外部联接是什么?

这是.Include默认行为。 .Include 总是产生左外部.Include

两者都创建几乎相同的查询:从一个表中进行选择,并带有两个内部联接。 从数据库的角度来看,唯一改变的是返回的字段数量,但这并不重要。

我认为这里DRY赢得了性能上的成功(如果存在的话):所以我的选择是第一个选择。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM