Загрузка родительских и дочерних записей в одном операторе LINQ - PullRequest
2 голосов
/ 17 сентября 2011

Я знаю, что это легко можно сделать двумя попаданиями в базу данных, но я экспериментировал с одним оператором LINQ, чтобы загрузить заказ по идентификатору и его элементам заказа, одновременно переводя их в объекты ViewModel:

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 голосов
/ 19 сентября 2011

У вас есть несколько вариантов принудительной загрузки в 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;

Единственная загвоздка в том, что вам нужно установить свойство LoadOptions вашего datacontext перед использованием контекста. Это не должно быть проблемой, если вы используете контекст рекомендованным способом (краткосрочный, экземпляр для каждой операции), но этот шаблон использования, кажется, не настолько распространен, как предполагалось. Если вы создадите текстовый текст и передадите его или повесьте на него, это может вызвать проблемы.

...