Не могу добавить вычисленное значение в IQueryable - PullRequest
0 голосов
/ 27 ноября 2018

Я использую оператор EF, в котором мне нужно вычислить франшизы.После долгих попыток я не могу добавить пользовательскую функцию в оператор .Select().Вместо этого я пытаюсь добавить значения после моего .Select() заявления.

Проблема здесь в том, что в моем CalculateDeductibles() я не могу добавить какие-либо значения к item.Deductibles.

GetDeductibles(item.RequestId) - довольно сложная функция, которая выполняет несколько дополнительных запросов, поэтому я пытаюсь предотвратить преобразование моего IQueryable в IList объект.

Итак, на самом деле есть 2 вопроса:

  1. Могу ли я использовать функцию GetDeductibles() непосредственно в моем операторе .Select()?
  2. Могу ли я каким-то образом (следя за производительностью) добавить значение после того, как я сделал .Select()

код:

public IQueryable<ReinsuranceSlip> GetReinsuranceSlipsOverview(int userId, int companyId, string owner, string ownerCompany)
{
    IQueryable<ReinsuranceSlip> model = null;

    model = _context.Request
        .Where(w => w.RequestGroup.ProgramData.MCContactId == userId)
        .Select(x => new ReinsuranceSlip()
        {
            Id = x.Id,
            RequestId = x.Id,
            LocalPolicyNumber = x.LocalPolicyNumber,
            BusinessLine = x.RequestGroup.ProgramData.BusinessLine.DisplayName,
            BusinessLineId = x.RequestGroup.ProgramData.BusinessLine.Id,
            ParentBroker = x.RequestGroup.ProgramData.Broker.Name,
            LocalBroker = x.Broker.Name,
            InceptionDate = x.InceptionDate,
            RenewDate = x.RenewDate,
            //Deductibles = CalculateDeductibles(x)
        });

    CalculateDeductibles(model);

    return model;
}


private void CalculateDeductibles(IQueryable<ReinsuranceSlip> model)
{
    //model.ForEach(m => m.Deductibles = GetDeductibles(m.RequestId));
    foreach (var item in model)
    {
        item.Deductibles = GetDeductibles(item.RequestId);
    }
}

Ответы [ 2 ]

0 голосов
/ 29 ноября 2018

Вам необходимо понять различия между IEnumerable и IQueryable.

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

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

Существует два типа функций LINQ.: те, которые возвращают IQueryable<TResult> и те, которые возвращают TResult.Функции первого типа не выполняют запрос, они только меняют выражение.Они используют отложенное исполнение.Функции второй группы будут выполнять запрос.

Когда запрос должен быть выполнен, Provider принимает Expression и пытается преобразовать его в формат, понятный процессу, выполняющему запрос.Если этот процесс представляет собой систему управления реляционными базами данных, это обычно будет SQL.

Этот перевод является причиной того, что вы не можете добавить свою собственную функциональность: Expression должен быть переведен в SQL, и единственноечто ваши функции могут делать - это вызывать функции, которые заменят Expression на что-то, что может быть переведено в SQL.

На самом деле, даже структура сущностей не поддерживает все функции LINQ.Существует список поддерживаемых и неподдерживаемых методов LINQ

Вернуться к вашим вопросам

Могу ли я получить GetDeductibles непосредственно в моем запросе?

Нет, вы не можете, если вы не можете сделать это таким простым, что он изменит только Expression, используя только методы поддержки LINQ.Вы должны будете написать это в формате функции расширения.См. Расширение методов расширения:

Ваш GetDeductibles должен иметь IQueryable<TSource> в качестве ввода и возвращать IQueryable<TResult> в качестве вывода:

static class QueryableExtensions
{
    public static IQueryable<TResult> ToDeductibles<TSource, TResult, ...>(
       this IQueryable<TSource> source,
       ... other input parameters, keySelectors, resultSelectors, etc)
    {
         IQueryable<TResult> result = source... // use only supported LINQ methods
         return result;
    } 
}

Если вам действительно нужновызовите другие локальные функции, рассмотрите возможность вызова AsEnumerable непосредственно перед вызовом локальных функций.Преимущество над ToList состоит в том, что умные IQueryable провайдеры, такие как тот, что в Entity Framework, будут получать не все элементы, а элементы на страницу .Поэтому, если вам нужно всего несколько, вы не перенесете все данные в локальный процесс.Убедитесь, что вы отбрасываете все данные, которые вам больше не нужны, перед вызовом AsEnumerable, тем самым ограничивая количество транспортируемых данных.

Могу ли я каким-то образом добавить значение после того, как я сделал .Select ()

LINQ предназначен для запроса данных, а не для их изменения.Прежде чем вы сможете изменить данные, вам придется их материализовать, прежде чем изменять.В случае запроса к базе данных это означает, что у вас есть копия заархивированных данных, а не оригинал.Поэтому, если вы вносите изменения, вы изменяете копии, а не оригиналы.

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

НЕ:

var schoolToUpdate = schoolDbContext.Schools.Where(schoolId = 10)
   .Select(school = new
   {
       ... // you get a copy of the values: fast, but not suitable for updates
   })
   .FirstOrDefault();

НО:

School schoolToUpdate = schoolDbContext.Schools.Where(schoolId = 10)
   .FirstOrDefault()

Теперь ваш DbContext имеет исходную школу вего ChangeTracker.Если вы измените SchoolToUpdate и вызовете SaveChanges, ваш SchoolToUpdate сравнивается с исходной школой, чтобы проверить, нужно ли обновлять школу.

Если вы хотите, вы можете обойти этот механизм, напрямую подключив новую школу.в ChangeTracker или вызов хранимой процедуры.

0 голосов
/ 27 ноября 2018

Обновлено и Извините за первую версию этого ответа.Я не совсем понял. Ответ 1: IQueryable использует для создания полного оператора SQL для вызова в SQL Server.Так что если вы хотите использовать IQueryable, ваши методы должны генерировать операторы и возвращать их.Ваш метод GetDetuctibles получает аргумент Id запроса, но ваш запрашиваемый объект модели еще не собирал никаких данных из БД и не знает значения x.Id.Более того, ваши GetCarearDetuctiples получают аргумент, и с этим аргументом генерирует запрашиваемый объект, а после некоторых вычислений возвращает десятичное число.Я имею в виду да, вы можете использовать свои методы в операторе выбора , но это действительно сложно.Вы можете использовать метод LINQ AsExpendable () и переписать ваши методы, возвращающие тип Expression или Iqueryable.
Для получения подробной информации вы должны проверить.Это:

Свойство Entity Navigation IQueryable не может быть преобразовано в выражение хранилища и это: http://www.albahari.com/nutshell/predicatebuilder.aspx

И вы также должны проверить эту статью, чтобы понять интерфейс IQueryable: https://samueleresca.net/2015/03/the-difference-between-iqueryable-and-ienumerable/

Ответ 2: Вы можете использовать интерфейс IEnumerable вместо интерфейса IQueryable для достиженияэтот.Это будет легко использовать в этом случае.Вы можете проводить тесты производительности и улучшать свои методы по времени.

Но на вашем месте я хотел бы использовать хранимые процедуры для повышения производительности.

...