首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用EF6优化LINQ查询

使用EF6优化LINQ查询
EN

Stack Overflow用户
提问于 2021-04-19 12:10:58
回答 1查看 59关注 0票数 0

我第一次尝试在LINQ,只是想发布一个小问题,以确定这是否是最好的方式。我想要一个表中每一个值的列表。到目前为止,这是我有,它的工作,但这是最好的方式去收集所有的LINQ友好方式?

代码语言:javascript
运行
复制
    public static List<Table1> GetAllDatainTable()
    {
        List<Table1> Alldata = new List<Table1>();

        using (var context = new EFContext())
        {
           Alldata = context.Tablename.ToList();
        }
        return Alldata;
    }
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-04-19 21:52:08

对于简单实体,即不引用其他实体(导航属性)的实体,您的方法本质上是很好的。它可以浓缩成:

代码语言:javascript
运行
复制
public static List<Table1> GetAllDatainTable()
{
    using (var context = new EFContext())
    {
       return context.Table1s.ToList();
    }
}

然而,在大多数真实世界的场景中,您将希望将导航属性之类的东西用于实体之间的关系。例如,订单使用地址详细信息引用客户,并包含OrderLines,每个客户都引用产品等。以这种方式返回实体会出现问题,因为接受这种方法返回的实体的任何代码都应该是完整的,或者是可完成的实体。

例如,如果我有一个返回订单的方法,并且有各种使用订单信息的代码:其中一些代码可能试图获取有关订单客户的信息,其他代码可能对这些产品感兴趣。EF支持延迟加载,以便相关数据可以在需要时被提取,但是这只能在DbContext的生命周期内工作。像这样的方法处理DbContext,所以延迟加载是不可能的。

一种选择是急切地加载所有内容:

代码语言:javascript
运行
复制
using (var context = new EFContext())
{
    var order = context.Orders
        .Include(o => o.Customer)
            .ThenInclude(c => c.Addresses)
        .Include(o => o.OrderLines)
            .ThenInclude(ol => ol.Product)
        .Single(o => o.OrderId == orderId);
    return order;
}

然而,这种方法有两个缺点。首先,这意味着每次获取订单时都要加载更多的数据。消费代码可能并不关心客户或订单行,但我们已经加载了所有这些代码。其次,随着系统的发展,可能会引入新的关系,即当包含越来越多的相关数据时,不一定会注意到更新旧代码以包括可能导致的NullReferenceException、bug或性能问题。视图或其他最初使用该实体的东西可能不会期望引用这些新关系,但是一旦您开始将实体传递到视图、视图和其他方法,任何接受实体的代码都应该期望依赖于实体是完整的或可以使其完整这一事实。如果命令可能加载在不同级别的“完整性”和处理数据是否加载的代码中,这可能是一场噩梦。作为一般性建议,我建议不要在加载实体的DbContext范围之外传递实体。

更好的解决方案是利用投影从适合代码消耗的实体中填充视图模型。WPF通常使用MVVM模式,因此这意味着使用EF的Select方法或Automapper的ProjectTo方法来填充基于每个使用者需求的视图模型。当您的代码使用包含数据视图和此类需求的ViewModels时,然后根据需要加载和填充实体,这将使您能够产生更高效(快速)和弹性更强的查询来获取数据。

如果我有一个视图,该视图列出具有创建日期、客户名称和产品/w数量列表的订单,那么我们为视图定义一个视图模型:

代码语言:javascript
运行
复制
[Serializable]
public class OrderSummary
{
    public int OrderId { get; set; }
    public string OrderNumber { get; set; }
    public DateTime CreatedAt { get; set; }
    public string CustomerName { get; set; }
    public ICollection<OrderLineSummary> OrderLines { get; set; } = new List<OrderLineSummary>();
}

[Serializable]
public class OrderLineSummary
{
    public int OrderLineId { get; set; }
    public int ProductId { get; set; }
    public string ProductName { get; set; }
    public int Quantity { get; set; }
}

然后在Linq查询中投影视图模型:

代码语言:javascript
运行
复制
using (var context = new EFContext())
{
    var orders = context.Orders
        // add filters & such /w Where() / OrderBy() etc.
        .Select(o => new OrderSummary
        {
            OrderId = o.OrderId,
            OrderNumber = o.OrderNumber,
            CreatedAt = o.CreatedAt,
            CustomerName = o.Customer.Name,
            OrderLines = o.OrderLines.Select( ol => new OrderLineSummary
            {
                OrderLineId = ol.OrderLineId,
                ProductId = ol.Product.ProductId,
                ProductName = ol.Product.Name,
                Quantity = ol.Quantity
            }).ToList()
        }).ToList();
    return orders;
}

请注意,我们不需要担心急切地加载相关实体,而且如果以后订单或客户或这样的客户获得新的关系,那么上面的查询将继续工作,只有在新的关系信息对其服务的视图有用的情况下才会进行更新。它可以组成一个速度更快、内存更少的查询,获取更少的字段,以便从数据库传递到应用程序,并且可以使用索引来进一步调优这一点,以适应高使用的查询。

更新:

附加性能提示:通常避免使用像GetAll*()这样的方法作为最低的公共分母方法。我在这种方法中遇到的性能问题太多了,它们的形式是:

代码语言:javascript
运行
复制
var ordersToShip = GetAllOrders()
    .Where(o => o.OrderStatus == OrderStatus.Pending)
    .ToList();
foreach(order in ordersToShip)
{
    // do something that only needs order.OrderId.
}

其中GetAllOrders()返回List<Order>IEnumerable<Order>。有时会有类似于GetAllOrders().Count() > 0之类的代码。

这样的代码效率极低,因为GetAllOrders()从数据库中获取*所有记录,只是将它们加载到应用程序中的内存中,以便稍后被过滤或计数等等。

如果您遵循一条通过方法将EF DbContext和实体抽象到服务/存储库中的路径,那么您应该确保服务公开方法以产生有效的查询,或者放弃抽象,并在需要数据的地方直接利用DbContext。

代码语言:javascript
运行
复制
var orderIdsToShip = context.Orders
    .Where(o => o.OrderStatus == OrderStatus.Pending)
    .Select(o => o.OrderId)
    .ToList();


var customerOrderCount = context.Customer
    .Where(c => c.CustomerId == customerId)
    .Select(c => c.Orders.Count())
    .Single();

EF是非常强大的,当选择为您的应用程序服务时,您的应用程序应该作为应用程序的一部分来获得最大的好处。我建议您避免纯粹为了抽象而编写代码,除非您希望使用单元测试将依赖于数据的依赖与模拟隔离开来。在本例中,我建议利用DbContext和Repository模式的工作包装单元,利用IQueryable来简化业务逻辑的隔离。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67161769

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档