Как объединить таблицы из нескольких dbcontexts в одну ViewModel?

Итак, у меня есть два dbcontexts для получения данных из разных таблиц, и я хочу объединить их в ViewModel. Я знаю, что мне, вероятно, следует использовать .Join(), но мне было интересно, возможно ли что-то подобное:

Мой контроллер:

public class HomeController : Controller
{
    private readonly NAV_FS_DBContext _navContext;
    private readonly DostebaPpsContext _baseDataContext;

    public HomeController(NAV_FS_DBContext navContext, DostebaPpsContext baseDataContext)
    {
        _navContext = navContext;
        _baseDataContext = baseDataContext;
    }

    // GET: Home
    public async Task<IActionResult> Index()
    {
        List<SpeweSalesLine> speweSalesList = await _navContext.SpeweSalesLines.ToListAsync();
        List<SpeweProdOrderLine> spewePoList = await _navContext.SpeweProdOrderLines.ToListAsync();
        List<Variable> baseDataList = await _baseDataContext.Variables.ToListAsync();
        List<PoItemViewModel> poItems = await _navContext.SpeweProductionOrder
            .Select(x => new PoItemViewModel
            {
                Status = x.Status,
                PONr = x.No,
                ItemNr = x.SourceNo,
                FormType = x.Description,
                Quantity = x.Quantity,
                StartingDate = x.StartingDate,
                EndingDate = x.EndingDate,
                ShipmentDate = speweSalesList.Where(y => y.No == x.No).Select(y => y.ShipmentDate).FirstOrDefault(),
                Priority = spewePoList.Where(y => y.ProdOrderNo == x.No).Select(y => y.Priority).FirstOrDefault(),
                Molds = baseDataList.Where(y => y.SourceNo == x.SourceNo).Select(y => y.MoldPerDay).FirstOrDefault()
            }).ToListAsync();

        return View(poItems);
    }
}

Модель представления:

public class PoItemViewModel
{
    public string? ItemNr { get; set; }
    public int? Status { get; set; }
    public string? FormType { get; set; }
    public decimal? Quantity { get; set; }
    public int? Molds { get; set; }
    public decimal? Days { get; set; }
    public string? PONr { get; set; }
    public DateTime? StartingDate { get; set; }
    public DateTime? EndingDate { get; set; }
    public DateTime? ShipmentDate { get; set; }
    public int? Priority { get; set; }
}

NAV_FS_DBКонтекст:

public partial class NAV_FS_DBContext : DbContext
{
    public NAV_FS_DBContext()
    {
    }

    public NAV_FS_DBContext(DbContextOptions<NAV_FS_DBContext> options)
        : base(options)
    {
    }

    public virtual DbSet<SpeweItem> SpeweItem { get; set; }
    public virtual DbSet<SpeweProductionOrder> SpeweProductionOrder { get; set; }

    public virtual DbSet<SpeweItemLedgerEntry> SpeweItemLedgerEntries { get; set; }

    public virtual DbSet<SpeweSalesLine> SpeweSalesLines { get; set; }

    public virtual DbSet<SpeweValueEntry> SpeweValueEntries { get; set; }
    public virtual DbSet<SpeweProdOrderLine> SpeweProdOrderLines { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder.UseSqlServer("Name=ConnectionStrings:CS2");

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.UseCollation("Latin1_General_100_CI_AS");

        modelBuilder.Entity<SpeweItem>(entity =>
        {
            entity.HasKey(e => e.No).HasName("SPEWE$Item$0");

            entity.ToTable("SPEWE$Item", "dbo");
            //etc...
            });
            OnModelCreatingPartial(modelBuilder);
        }

        partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
    }
}

Модель SpeweProductionOrder (из dbcontext):

public partial class SpeweProductionOrder
{
    public byte[] Timestamp { get; set; } = null!;

    public int Status { get; set; }

    public string No { get; set; } = null!;

    public string Description { get; set; } = null!;

    public string SearchDescription { get; set; } = null!;

    public string Description2 { get; set; } = null!;

    public DateTime CreationDate { get; set; }

    public DateTime LastDateModified { get; set; }

    public int SourceType { get; set; }

    public string SourceNo { get; set; } = null!;

    public string RoutingNo { get; set; } = null!;

    public string InventoryPostingGroup { get; set; } = null!;

    public string GenProdPostingGroup { get; set; } = null!;

    public string GenBusPostingGroup { get; set; } = null!;

    public DateTime StartingTime { get; set; }

    public DateTime StartingDate { get; set; }

    public DateTime EndingTime { get; set; }

    public DateTime EndingDate { get; set; }

    public DateTime DueDate { get; set; }

    public DateTime FinishedDate { get; set; }

    public byte Blocked { get; set; }

    public string ShortcutDimension1Code { get; set; } = null!;

    public string ShortcutDimension2Code { get; set; } = null!;

    public string LocationCode { get; set; } = null!;

    public string BinCode { get; set; } = null!;

    public string ReplanRefNo { get; set; } = null!;

    public int ReplanRefStatus { get; set; }

    public int LowLevelCode { get; set; }

    public decimal Quantity { get; set; }

    public decimal UnitCost { get; set; }

    public decimal CostAmount { get; set; }

    public string NoSeries { get; set; } = null!;

    public string PlannedOrderNo { get; set; } = null!;

    public string FirmPlannedOrderNo { get; set; } = null!;

    public string SimulatedOrderNo { get; set; } = null!;

    public DateTime StartingDateTime { get; set; }

    public DateTime EndingDateTime { get; set; }

    public int DimensionSetId { get; set; }

    //etc...
}

Модель SpeweSalesLine (из dbcontext):

public partial class SpeweSalesLine
{
    public byte[] Timestamp { get; set; } = null!;

    public int DocumentType { get; set; }

    public string DocumentNo { get; set; } = null!;

    public int LineNo { get; set; }

    public string SellToCustomerNo { get; set; } = null!;

    public int Type { get; set; }

    public string No { get; set; } = null!;

    public string LocationCode { get; set; } = null!;

    public string PostingGroup { get; set; } = null!;

    public DateTime ShipmentDate { get; set; }

    public string Description { get; set; } = null!;

    public string Description2 { get; set; } = null!;

    public string UnitOfMeasure { get; set; } = null!;

    public decimal Quantity { get; set; }

    public decimal OutstandingQuantity { get; set; }

    public decimal QtyToInvoice { get; set; }

    public decimal QtyToShip { get; set; }

    public decimal UnitPrice { get; set; }

    public decimal UnitCostLcy { get; set; }

    public decimal Vat { get; set; }

    public decimal LineDiscount { get; set; }

    public decimal LineDiscountAmount { get; set; }

    public decimal Amount { get; set; }

    public decimal AmountIncludingVat { get; set; }

    public byte AllowInvoiceDisc { get; set; }

    public decimal GrossWeight { get; set; }

    public decimal NetWeight { get; set; }

    public decimal UnitsPerParcel { get; set; }

    public decimal UnitVolume { get; set; }

    public int ApplToItemEntry { get; set; }

    public string ShortcutDimension1Code { get; set; } = null!;

    public string ShortcutDimension2Code { get; set; } = null!;

    public string CustomerPriceGroup { get; set; } = null!;

    public string JobNo { get; set; } = null!;

    public string WorkTypeCode { get; set; } = null!;

    public byte RecalculateInvoiceDisc { get; set; }

    public decimal OutstandingAmount { get; set; }

    public decimal QtyShippedNotInvoiced { get; set; }

    public decimal ShippedNotInvoiced { get; set; }

    public decimal QuantityShipped { get; set; }

    public decimal QuantityInvoiced { get; set; }

    public string ShipmentNo { get; set; } = null!;

    public int ShipmentLineNo { get; set; }

    public decimal Profit { get; set; }

    public string BillToCustomerNo { get; set; } = null!;

    public decimal InvDiscountAmount { get; set; }

    public string PurchaseOrderNo { get; set; } = null!;

    public int PurchOrderLineNo { get; set; }

    public byte DropShipment { get; set; }

    public string GenBusPostingGroup { get; set; } = null!;

    public string GenProdPostingGroup { get; set; } = null!;

    public int VatCalculationType { get; set; }

    public string TransactionType { get; set; } = null!;

    public string TransportMethod { get; set; } = null!;

    public int AttachedToLineNo { get; set; }

    public string ExitPoint { get; set; } = null!;

    public string Area { get; set; } = null!;

    public string TransactionSpecification { get; set; } = null!;

    public string TaxCategory { get; set; } = null!;

    public string TaxAreaCode { get; set; } = null!;

    public byte TaxLiable { get; set; }

    public string TaxGroupCode { get; set; } = null!;

    public string VatClauseCode { get; set; } = null!;

    public string VatBusPostingGroup { get; set; } = null!;

    public string VatProdPostingGroup { get; set; } = null!;

    public string CurrencyCode { get; set; } = null!;

    public decimal OutstandingAmountLcy { get; set; }

    public decimal ShippedNotInvoicedLcy { get; set; }

    public int Reserve { get; set; }

    public string BlanketOrderNo { get; set; } = null!;

    public int BlanketOrderLineNo { get; set; }

    public decimal VatBaseAmount { get; set; }

    public decimal UnitCost { get; set; }

    public byte SystemCreatedEntry { get; set; }

    public decimal LineAmount { get; set; }

    public decimal VatDifference { get; set; }

    public decimal InvDiscAmountToInvoice { get; set; }

    public string VatIdentifier { get; set; } = null!;

    public int IcPartnerRefType { get; set; }

    public string IcPartnerReference { get; set; } = null!;

    public decimal Prepayment { get; set; }

    public decimal PrepmtLineAmount { get; set; }

    public decimal PrepmtAmtInv { get; set; }

    public decimal PrepmtAmtInclVat { get; set; }

    public decimal PrepaymentAmount { get; set; }

    public decimal PrepmtVatBaseAmt { get; set; }

    public decimal PrepaymentVat { get; set; }

    public int PrepmtVatCalcType { get; set; }

    public string PrepaymentVatIdentifier { get; set; } = null!;

    public string PrepaymentTaxAreaCode { get; set; } = null!;

    public byte PrepaymentTaxLiable { get; set; }

    public string PrepaymentTaxGroupCode { get; set; } = null!;

    public decimal PrepmtAmtToDeduct { get; set; }

    public decimal PrepmtAmtDeducted { get; set; }

    public byte PrepaymentLine { get; set; }

    public decimal PrepmtAmountInvInclVat { get; set; }

    public decimal PrepmtAmountInvLcy { get; set; }

    public string IcPartnerCode { get; set; } = null!;

    public decimal PrepmtVatAmountInvLcy { get; set; }

    public decimal PrepaymentVatDifference { get; set; }

    public decimal PrepmtVatDiffToDeduct { get; set; }

    public decimal PrepmtVatDiffDeducted { get; set; }

    public int DimensionSetId { get; set; }

    public decimal QtyToAssembleToOrder { get; set; }

    public decimal QtyToAsmToOrderBase { get; set; }

    public string JobTaskNo { get; set; } = null!;

    public int JobContractEntryNo { get; set; }

    public string DeferralCode { get; set; } = null!;

    public DateTime ReturnsDeferralStartDate { get; set; }

    //etc...
}

Модель переменной (из dbcontext)

public partial class Variable
{
    public string SourceNo { get; set; } = null!;

    public string Type { get; set; } = null!;

    public int PiecePerPallett { get; set; }

    public int Lotsize { get; set; }

    public int MoldPerDay { get; set; }

    public int FormAmount { get; set; }

    public int CavityAmount { get; set; }

    public decimal FormTime { get; set; }

    public decimal FillTime { get; set; }

    public decimal CarouselRotation { get; set; }

    public decimal CompletionTime { get; set; }

    public decimal Cycle { get; set; }
}

Очевидно, это не работает, поскольку я получаю исключение InvalidOperationException, но я имею в виду, что я заранее загружаю все данные и сохраняю их в виде списка, так почему я не могу получить/сравнить данные, например, из «spewePoList»?

Было бы неплохо узнать некоторые мысли по этому поводу, и заранее благодарю вас за помощь.

Не размещайте код в виде изображений, текст изображения невозможно скопировать или найти.

Svyatoslav Danyliv 19.03.2024 09:07

@SvyatlavDanyliv Ой, извини, сразу поменяю.

Gogo Dev 19.03.2024 09:23
I've got two dbcontexts, to get data from various tables это ошибка терминала, то есть ее невозможно исправить. DbContext — это единица работы с несколькими сущностями, а не соединение с базой данных, такое как SqlConnection, или коллекция таблиц. Однако вы не сможете создавать JOIN, даже если бы это было соединение. DbContext сам создает соединения JOIN, когда это необходимо, на основе отношений между сущностями. Если вы напишете ctx.Customer.Where(c=>c.Id=23).Select(c=>new {c.Name,Orders=c.Orders.ToList()});, DbContext создаст СОЕДИНЕНИЯ между Клиентами и Заказами.
Panagiotis Kanavos 19.03.2024 09:34

Что вы на самом деле пытаетесь сделать? Опишите исходную проблему, а не то, как, по вашему мнению, выглядит ее решение. NAV_FS_DBContext является подозрительным и предполагает, что вы пытаетесь объединить данные из базы данных Navision с вашей собственной базой данных, возможно, на отдельном сервере. Во-первых, вы не можете создавать межсерверные соединения. ORM, такие как EF, пытаются создать впечатление, что работают с объектами в памяти, а не с таблицами и строками, но они не могут изменить работу базы данных. Вы можете загружать объекты (не строки) из разных баз данных и работать с ними на клиенте, но не можете объединять запросы.

Panagiotis Kanavos 19.03.2024 09:38

В коде есть и другие критические проблемы. _navContext.SpeweSalesLines.ToListAsync(); и подобные строки загружают всю таблицу в память. Если в таблице 100 000 строк продаж, все 100 000 будут загружены в память. И пока выполняется этот очень медленный запрос, другие экземпляры приложения не смогут изменить таблицу — вы заблокируете продажи для всех, когда вам действительно нужны строки продаж только для одного заказа.

Panagiotis Kanavos 19.03.2024 09:40

@PanagiotisKanavos По сути, мне просто нужны некоторые данные из Navision, чтобы сохранить их в модели, чтобы я мог отобразить их в представлении. Я знаю, что загружать всю таблицу в память - не лучший вариант, потому что, как вы сказали, это, во-первых, займет вечность, а во-вторых, я заблокирую таблицу на сервере. Я просто пробую это в небольшой базе данных, чтобы посмотреть, работает ли это, но думаю, что для продуктивного сайта мое «решение» было бы глупым. Я не совсем уверен, как объединить несколько таблиц, поэтому задал этот вопрос.

Gogo Dev 19.03.2024 09:47

@GogoDev, мы можем упростить это, но покажите свои NAV_FS_DBContext классы, используемые в этом запросе, меня интересуют свойства навигации.

Svyatoslav Danyliv 19.03.2024 09:52

Похоже, вы удалили свойства навигации. Вы можете удалить другие свойства, но не свойства навигации. Это помогает создать запрос LINQ.

Svyatoslav Danyliv 19.03.2024 10:08

Действительно, если бы свойства навигации все еще были там, запрос был бы почти тривиальным: ShipmentDate = x.ShipmentDate, Priority = x.Priority. Никаких дополнительных JOINS не требуется. Variables выглядит как справочные данные, которые можно загрузить заранее и даже кэшировать

Panagiotis Kanavos 19.03.2024 10:46

Таким образом, в основном свойства навигации являются решением. Думаю, тогда мне придется их добавить.

Gogo Dev 19.03.2024 10:51
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
10
70
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Сначала удалите ненужную мериализацию speweSalesList и spewePoList. Все, что мы можем сделать с одним контекстом, мы должны использовать повторно.

Подготовленный пример, как это сделать без свойств навигации. Было бы проще и подвержено ошибкам использовать свойства навигации вместо явных подзапросов.

var baseDataList = await _baseDataContext.Variables
    .Select(v => new { v.SourceNo, v.MoldPerDay } )
    .ToListAsync();

// Just IQueryable shortucts
var speweSalesList = _navContext.SpeweSalesLines;
var spewePoList = _navContext.SpeweProdOrderLines;

var poItems = await _navContext.SpeweProductionOrder
    .Select(x => new PoItemViewModel
    {
        Status = x.Status,
        PONr = x.No,
        ItemNr = x.SourceNo,
        FormType = x.Description,
        Quantity = x.Quantity,
        StartingDate = x.StartingDate,
        EndingDate = x.EndingDate,
        ShipmentDate = speweSalesList.Where(y => y.No == x.No).Select(y => y.ShipmentDate).FirstOrDefault(),
        Priority = spewePoList.Where(y => y.ProdOrderNo == x.No).Select(y => y.Priority).FirstOrDefault(),
        Molds = baseDataList.Where(y => y.SourceNo == x.SourceNo).Select(y => y.MoldPerDay).FirstOrDefault()
    }).ToListAsync();

Пожалуйста, проверьте, работает ли он, Molds могут возникнуть проблемы и может потребоваться постобработка. Также пересмотрите свою бизнес-логику, вы загружаете в память целые таблицы.

Я подозреваю, что await _navContext.SpeweSalesLines должно быть _navContext.SpeweSalesLines.AsQueryable(), но результирующие N запросов, сгенерированные speweSalesList.Where(y => y.No == x.No).Select(y => y.ShipmentDate) и еще N с помощью `baseDataList.Where`, будут очень медленными. Было бы лучше сначала получить заказы, извлечь номера заказов, а затем загрузить отдельные части в словарь по номеру заказа с помощью orderNumbers.Oontains(y.No). Для этого потребуется всего 4 запроса

Panagiotis Kanavos 19.03.2024 10:26

@PanagiotisKanavos, исправил опечатки. Очень медленно может быть Molds, поэтому, вероятно, может понадобиться клиентский словарь. Другие могут быть медленными, если нет индексов. Пожалуйста, дайте мне шанс закончить этот ответ. Я также хорош в базах данных.

Svyatoslav Danyliv 19.03.2024 10:28

Я знаю, что да, поэтому я знал, что это опечатка. Я не знаю, можно ли решить основную проблему с помощью одного ответа - ОП поспешил использовать ORM, не разбираясь в SQL или базах данных. Это как броситься водить Теслу, не умея водить. То, что мы продолжаем видеть снова и снова в последнее время.

Panagiotis Kanavos 19.03.2024 10:48
Ответ принят как подходящий

IEnumerable против IQueryable

Вы должны знать разницу между IEnumerable и IQueryable.

IEnumerable представляет собой последовательность похожих элементов. Вы можете запросить первый элемент последовательности, и пока у вас есть элемент последовательности, вы можете запросить следующий элемент последовательности, пока элементов не останется. IEnumerable содержит все необходимое для извлечения элементов последовательности.

IQueryable выглядит как IEnumerable, однако он не представляет последовательность Enumerable, а представляет собой возможность создания последовательности Enumerable.

Для этого IQueryable содержит Expression и Provider. Выражение содержит в некотором общем формате то, что должно быть получено, поставщик знает, откуда будут получены данные (обычно система управления базой данных) и какой язык используется для связи с этой СУБД (обычно что-то вроде SQL).

IQueryable также реализует IEnumerable. Когда вы запрашиваете IQueryable GetEnumerator, выражение отправляется поставщику, который преобразует выражение в SQL и выполняет запрос. Извлеченные данные представлены в виде IEnumerable, из которого вызывается GetEnumerator. Некоторые умные поставщики будут получать данные только при перечислении первого элемента.

Но какое это имеет отношение к моему вопросу?

Два ваших DbContexts представляют две разные системы управления базами данных. СУБД чрезвычайно оптимизирована для объединения таблиц и выбора данных из результата. Более медленная часть запроса — это транспортировка выбранных данных из СУБД в локальный процесс. Следовательно, разумно ограничить объем передаваемых данных.

Используя await ...ToListAsync(), вы извлекаете данные из _navContext в свой локальный процесс. Аналогичным образом вы извлекаете некоторые данные из _baseDataContext в свой локальный процесс.

Ваш последний запрос отправляет все полученные данные из вашего локального процесса в базу данных _navContext для выполнения ваших Select и Where. Выбранные записи затем отправляются обратно из СУБД в ваш локальный процесс. Хотя это, вероятно, даст вам необходимые результаты, это не очень эффективно.

Было бы гораздо эффективнее получить данные только из _baseDataContext, а затем отправить выбранные данные в _navContext СУБД, которая объединит эти данные с данными из таблиц SpeweSalesLines, SpeweProdOrderLines и SpeweProductionOrder для создания требуемого результата.

Преимущества:

  • вы не отправляете полное содержимое таблиц SpeweSalesLines и SpeweProdOrderLines в свой локальный процесс и не возвращаете эти данные в _navContext СУБД.
  • из _navContext будут извлечены только те данные, которые вы планируете использовать.

Улучшенный запрос будет (маленькими шагами):

Следующее не выполнит запрос, а только создаст запрос. Нет связи с _navContext, никакие данные не будут выбраны.

IQueryable<SpeweSalesLine> sales = _navContext.SpeweSalesLines;
IQueryable<SpeweProdOrderLine> prodOrders = _navContext.SpeweProdOrderLines;

Запрос ниже будет выполнен. Данные будут переданы из _baseDataContext. в ваш локальный процесс:

List<Variable> baseDataVariables = await _baseDataContext.Variables.ToListAsync();

Используйте полученные данные и используйте два других запроса для создания нового запроса. Опять же, связи с _navContext пока нет.

IQueryable<PoItemViewModel> poItemQuery = _navContext.SpeweProductionOrder
    .Select(x => new PoItemViewModel
    {
        Status = x.Status,
        PONr = x.No,
        ItemNr = x.SourceNo,
        FormType = x.Description,
        Quantity = x.Quantity,
        StartingDate = x.StartingDate,
        EndingDate = x.EndingDate,

        // Select the first ShipmentDate from the spewSales
        // that has a value for No that equals this x.No
        ShipmentDate = sales
           .Where(sale => sale.No == x.No)
           .Select(sale => sale.ShipmentDate)
           .FirstOrDefault(),

        // Select the first Priority from the speweProdOrders
        // that has a value for 
        Priority = speweProdOrders
            .Where(prodOrder => prodOrder.ProdOrderNo == x.No)
            .Select(prodOrder => prodOrder.Priority)
            .FirstOrDefault(),

        // Select the first MoldPerDay from the baseDataVariables that was fetched
        // from the _baseDataContext that has a SourceNo that equals this SourceNo
        Molds = baseDataList
            .Where(baseDataVariable => baseDataValues.SourceNo == x.SourceNo)
            .Select(baseDataVariable => baseDataVariable.MoldPerDay)
            .FirstOrDefault(),
        });

Теперь выполните запрос, чтобы получить желаемый результат. Полученные baseDataVariables отправляются в СУБД _navContext. Необходимые таблицы объединяются и выбираются запрошенные данные. Наконец, в локальный процесс будут отправлены только выбранные данные:

List<<PoItemViewModel> poItems = await poItemQuery.ToListAsync().

Результат:

  • только те данные, которые вы используете, будут переданы из _baseDataContext в ваш локальный процесс, а затем в _navContext.
  • SpeweSalesLines и SpeweOrderLines не передаются в ваш локальный процесс.
  • Из _navContext в ваш локальный процесс передаются только те данные, которые вы планируете использовать.

Конечно, при желании вы можете объединить все это в один большой оператор LINQ. Это не повысит эффективность, однако я уверен, что это ухудшит читабельность.

Другие вопросы по теме