实体框架断开POCO,与巨大的记录数量的关系

时间:2012-07-03 10:41:48

标签: entity-framework orm entity-relationship

我在分层架构中使用Entity Framework POCO。我们应该在获得权利(y / ies)之后断开工作 假设我们有两个实体:

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

public class Order
{
  public int Id;
  ....
}

并且假设有一个客户的订单数量为1000 在这种情况下,我们希望获得该客户的订单,该订单仅在本周以服务方式完成 我们应该如何使用Repository方法和Service方法?

编辑

我想我应该扩展我的问题 首先,我的意思是“有1000个订单的客户”,我不想得到那个客户。我想获得“客户的订单(具有已知的Id,假设,Customer.Id = 5),Order.Date&gt; DateTime.Now.AddDays(-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层最佳实践是否正确?由于订单不是“聚合根”,因此客户是“聚合根”   - 我是否必须在CustomerService类或OrderService类中使用此GetOrdersInLastWeek?   - 对我来说最后也是最重要的一个;如果我有像GetOrdersInLastWeek这样的方法,以及其他方法,那么其他层中的“Customer.Orders”导航属性(例如表示层)(假设我们正在使用POCO实体并断开连接)?
 - 另一个例子;我想在服务方法中对Customer.Orders导航属性进行分页。我如何以及在哪里这样做(我知道该怎么做,但我的意思是根据最佳实践)?

1 个答案:

答案 0 :(得分:1)

您可以尝试为此目的使用热切和显式加载的组合,例如使用DbContext(EF =&gt; 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();
}

关于您的问题:

  

我是否必须在CustomerService中拥有此GetOrdersInLastWeek   class,还是OrderService类?

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

  

我想对Customer.Orders中的导航属性进行分页   服务方式。我如何以及在哪里这样做(我知道该怎么做,但是   我的意思是根据最佳实践)?

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