使用一对多查找优化Linq-to-Sql映射

时间:2010-12-02 16:37:59

标签: linq-to-sql database-design optimization orm entity-relationship

我在使用以下数据结构优化数据查找时遇到了问题:

Order
----- 
Id 
Customer 
Date 
... etc


OrderStatus
------
Id
OrderId
Date
UpdatedBy
StatusTypeId
...etc

这导致我在订单列表页面上头疼,该页面基本上显示了Orders的列表。列表中的每个订单摘要都会显示来自Order和当前OrderStatus的一系列字段,即OrderStatus,其中包含最新的Date字段已链接到Order

Order List
-------------------------------------------------------
Order Id | Customer     | Order Date  | CurrentStatus |
-------------------------------------------------------
1        | Someone      |  1.10.2010  | Completed     |
-------------------------------------------------------
2        | Someone else | 12.10.2010  | In Progress   |
-------------------------------------------------------
3        | Whoever      | 17.10.2010  | On Hold       |
-------------------------------------------------------

现在,我想列出今年的所有订单。我的存储库提取Order个对象

var orders = _repository.GetAllOrdersSinceDate(dt);

现在我最终得到像

这样的东西
foreach (Order order in orders)
{
    OrderSummary summary = new OrderSummary();
    summary.Customer = order.Customer;
    summary.Date = order.Date;
    // ...etc

    // problem here!!
    summary.OrderStatus = order.OrderStatus
                .OrderByDescending(s => status.Date).First();
}

所以我最终得到的是SELECT上的Order声明,然后SELECT声明了OrderStatus对于每个Order返回的声明。

因此,要显示今年所有记录的摘要,需要大约20,000个单独的SQL查询,并需要花费很多分钟才能加载。

有没有任何巧妙的方法来解决这个问题?

我正在考虑重新编写数据库,以便在OrderStatus表格中保留当前 Order,所以我最终会得到类似

的内容
Order
----- 
Id 
Customer 
Date 
CurrentStatusTypeId
CurrentStatusDate
CurrentStatusUpdatedBy
...etc


OrderStatusHistory
------
Id
OrderId
Date
UpdatedBy
StatusTypeId
...etc

这是我能看到解决问题的唯一方法,但似乎是一个非常讨厌的解决方案。

这是前进的最佳方式吗?

2 个答案:

答案 0 :(得分:1)

请不要对数据库模型进行非规范化以解决您的问题。这只会让事情变得更糟。您可以通过编写一个返回数据传输对象(DTO)列表而不是LINQ to SQL实体的服务方法来解决此问题。例如,服务方法可能如下所示:

public OrderSummary[] GetOrderSummariesSinceDate(DateTime d)
{
    return (
        from order in this.context.Orders
        where order.Date >= d
        let lastStatus = (
            from status in order.OrderStatusses
            orderby status.Date descending
            select status).First()
        select new OrderSummary
        {
            OrderId = order.Id,
            CustomerName = order.Customer.Name,
            Date = order.Date,
            OrderStatus = lastStatus.StatusType.Name
        }).ToArray();    
}

请注意以下事项:

  • 此代码将作为数据库中的单个SQL查询执行。
  • 此方法将返回一个仅包含客户端所需数据的对象,但仅此而已。没有Customer个对象,没有OrderStatus个对象。
  • 通过调用ToArray,我们确保此时查询数据库并且不会延迟。

这三点可确保性能最大化,并允许服务层保持对数据库执行内容的控制。

我希望这会有所帮助。

答案 1 :(得分:0)

您可以按如下方式创建DataLoadOptions对象:

DataContext db = new DataContext
DataLoadOptions ds = new DataLoadOptions();
ds.LoadWith<OrderStatus>(c => c.Orders);
db.LoadOptions = ds;

然后当您运行查询时,它应该预取OrderStatus表