实体框架 断开连接的 POCO,关系具有巨大的记录计数



我在分层架构中使用实体框架POCO。我们应该在获得实体后断开连接。
假设我们有两个实体:

public class Customer
{  
  public int Id;  
  public string Name;
  public virtual IList<Order> Orders;
}  
public class Order
{
  public int Id;
  ....
}

假设有一个客户的订单计数为 1000。
在这种情况下,我们希望通过服务方法获取此客户的订单,该订单仅在本周完成。
我们应该如何拥有存储库方法和服务方法?

编辑

我想我应该扩展我的问题。
首先,我的意思是"有 1000 个订单的客户",我不想得到那个客户。我想获取"客户的订单(具有已知 ID,假设 Customer.Id=5),该订单>日期日期时间.现在.添加天数(-7)"。
表示 IQueryable 对象很容易。

public List<Order> GetOrdersInLastWeek(int CustomerId)  
{  
  Customer customer = (from c in ctx.Customers where c.Id == CustomerId).SingleOrDefault();  
  List<Order> ordersThatIWant = customer.Orders.Where(o => o.Date > DateTime.Now.AddDays(-7)).ToList();
  return ordersThatIWant;  
}

我对这些很好奇:上面的代码运行良好。
- 但是,ORM/N层最佳实践是否正确?由于订单不是"聚合根",因此客户是"聚合根"。
- 我应该在客户服务类或订单服务类中拥有此GetOrdersInLastWeek吗?
- 对我来说最后一个也是重要的一个;如果我有像GetOrdersInLastWeek这样的方法,以及其他类似的方法,那么其他层(例如表示层)中的"Customer.Orders"导航属性呢(假设我们正在使用POCO实体并且断开连接)?
- 另一个例子;我想在服务方法中的客户订单导航属性上执行分页。我如何以及在哪里可以做到这一点(我知道该怎么做,但我的意思是根据最佳实践)?

为此,您可以尝试混合使用预先加载和显式加载,例如使用 DbContext (EF => 4.1),如下所示:

public List<Customer> GetCustomersWithOrders()
{
    using (var ctx = new MyDbContext())
    {
        var customers1 = ctx.Customers.Include(c => c.Orders)
            .Where(c => c.Orders.Count() < 1000)
            .AsEnumerable();
        var customers2 = ctx.Customers
            .Where(c => c.Orders.Count() >= 1000)
            .AsEnumerable();
        DayOfWeek today = DateTime.Now.DayOfWeek;
        DateTime monday = DateTime.Now.AddDays(-1*(today-DayOfWeek.Monday));
        DateTime sunday = monday.AddDays(6);
        foreach (var customer in customers2)
        {
            ctx.Entry(customer).Collection(c => c.Orders).Query()
                .Where(o => o.ShipDate >= monday && o.ShipDate <= sunday)
                .Load();
        }
        return customers1.Concat(customers2).ToList();
    }
}

结果将由1 + 1 + customers2.Count数据库查询的结果组成。如果客户拥有超过 1000 个订单的情况很少见,我认为这些多个查询不会是一个大的性能问题。

编辑

我建议更改您的查询。问题是您加载了一个客户(查询 1),然后触发了所有订单的延迟加载(查询 2)。因此,在内存中应用筛选器之前,您可能已经加载了 1000 多个订单。您可以通过使用以下方法避免这种情况:

public List<Order> GetOrdersInLastWeek(int CustomerId)  
{  
    var date = DateTime.Now.AddDays(-7);
    var orders = ctx.Customers
        .Where(c => c.Id == CustomerId)
        .Select(c => c.Orders.Where(o => o.Date > date))
        .SingleOrDefault();
    if (orders == null)
        return null; // or create an empty list
    return orders.ToList();
}

关于您的问题:

我是否应该在客户服务中拥有这个GetOrdersInLastWeek 类,还是订单服务类?

CustomerService.因为您正在加载特定客户的数据。但在我看来,使用OrderService也没有错。

我想对客户进行分页。订单导航属性中的 服务方法。我如何以及在哪里可以做到这一点(我知道该怎么做,但是 我的意思是根据最佳实践)?

使用当前的体系结构,您必须在服务方法中执行此操作(使用 SkipTake 创建查询),因为您正在公开内存中的集合,即服务方法的具体化结果。因此,不应在服务外部进行分页,因为这会强制您在分页之前加载所有数据。在我看来,唯一的选择是从服务返回IQueryable<T>,然后通过向查询添加SkipTake来返回 UI 层中的页面。但这意味着您无法在 UI 层断开连接。

最新更新