1

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

4

1 回答 1

1

为此,您可以尝试混合使用急切加载和显式加载,例如使用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();
}

关于您的问题:

我应该在 CustomerService 类或 OrderService 类中拥有这个 GetOrdersInLastWeek 吗?

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

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

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

于 2012-07-03T16:37:36.173 回答