在单个LINQ语句中加载父级及其子记录

时间:2011-09-16 20:42:00

标签: linq linq-to-sql eager-loading

我知道这可以通过对数据库的两次点击轻松完成,但我一直在尝试使用单个LINQ语句按ID加载订单及其订单项,同时将它们转换为ViewModel对象: / p>

var query = from orderLine in db.PurchaseOrderLines
            where orderLine.PurchaseOrderId == id
            orderby orderLine.Id
            group orderLine by orderLine.PurchaseOrder into grouped
            select new PurchaseOrderViewModel
            {
                Id = grouped.Key.Id,
                PlacedDateTime = grouped.Key.PlacedDateTime,
                Reference = grouped.Key.OrderReference,
                StatusId = grouped.Key.PurchaseOrderStatusId,
                Status = grouped.Key.PurchaseOrderStatus.Description,
                Supplier = grouped.Key.Supplier.Name,
                SupplierId = grouped.Key.SupplierId,
                OrderLines = grouped.Select(row => new PurchaseOrderLineViewModel
                {
                    Id = row.Id,
                    PartNumber = row.Product.PartNumber,
                    ProductDescription = row.Product.Description,
                    Quantity = row.Quantity
                })
            };

但是,query的类型为System.Data.Objects.ObjectQuery<...PurchaseOrderViewModel>,尝试迭代结果会引发异常:

System.InvalidOperationException was unhandled by user code
  Message=Sequence contains no elements
  Source=System.Core
  StackTrace:
       at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source)
       at System.Data.Objects.ELinq.ObjectQueryProvider.<GetElementFunction>b__3[TResult](IEnumerable`1 sequence)
       at System.Data.Objects.ELinq.ObjectQueryProvider.ExecuteSingle[TResult](IEnumerable`1 query, Expression queryRoot)
       at System.Data.Objects.ELinq.ObjectQueryProvider.System.Linq.IQueryProvider.Execute[S](Expression expression)
       at System.Linq.Queryable.Single[TSource](IQueryable`1 source)
       at MyApp.Controllers.PurchaseOrderController.Details(Int32 id) in E:\Code\WCs\MyApp\MyApp\Controllers\PurchaseOrderController.cs:line 69
       at lambda_method(Closure , ControllerBase , Object[] )
       at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
       at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
       at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
       at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass15.<InvokeActionMethodWithFilters>b__12()
       at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)
  InnerException: 

我做错了什么?我是否犯了错误甚至尝试过这个?

非常感谢提前。

1 个答案:

答案 0 :(得分:0)

您有几个选项可以强制在LINQ-to-SQL中加载。在不知道你是如何执行查询或者是否正在实现分页等的情况下,很难说。

我尝试的第一件事(仍在你现有的查询中)只是在执行查询时强制OrderLines到一个新列表,如下所示:

OrderLines = grouped.Select(row => new PurchaseOrderLineViewModel
{
    Id = row.Id,
    PartNumber = row.Product.PartNumber,
    ProductDescription = row.Product.Description,
    Quantity = row.Quantity
}).ToList()

如果这样有效,那很简单。

另一种方法是使用DataLoadOptions类。这是在LINQ-to-SQL中实现预先加载的常用方法。

var loadOptions = new DataLoadOptions();
loadOptions.LoadWith<PurchaseOrder>(p => p.PurchaseOrderLines);
db.LoadOptions = loadOptions;

唯一的问题是您需要在使用上下文之前设置datacontext的LoadOptions属性。如果您以推荐的方式使用上下文(短期,每个操作的实例),这应该不是问题,但是这种使用模式似乎并不像预期的那样普遍。如果您创建一个datacontext并传递它或挂起它,那么这可能会导致您遇到问题。