Как я могу извлечь вызов DbFunctions / SqlFunctions в расширяемое расширение / метод с EntityFramework 6.2? - PullRequest
0 голосов
/ 26 декабря 2018

У меня есть приложение, написанное с использованием C# поверх платформы ASP.NET MVC 5.Кроме того, я использую EntityFramework 6.2 в качестве ORM для взаимодействия с моими данными.

Я написал следующий оператор соединения, используя Fluent LINQ

List<string> names = new List<string>{"", ....};
query = TopLevelQuery.Where(x => x.EndedAt.HasValue && x.StartedAt >= startedAt && x.EndedAt.Value <= endedAt)
                     .Join(UserService.QueryUniqueRecords(),
                     entry => entry.UserId,
                     rec => rec.UserId,
                     (entry, rec) => new { entry, rec })
                     .Where(result => result.entry.IsEqualDateOf(result.rec.DayOf) 
                         && names.Contains(result.rec.Name))
                     .Select(x => x.entry);

Однако во время выполнения я получаю следующую ошибку

LINQ to Entities не распознает метод IsEqualDateOf, и этот метод нельзя преобразовать в выражение хранилища.

Вот мой IsEqualDateOf метод расширения

public static class MyModelExtensions
{
    public static DateTime GetLocalDate(this MyModel entry)
    {
        var local = DbFunctions.TruncateTime(SqlFunctions.DateAdd("ss", entry.UtcOffset, entry.StartedAt));

        return local.Value;
    }

    public static bool IsEqualDateOf(this MyModel entry, DateTime dateOf)
    {
        bool isEqual = entry.GetLocalDate().Equals(dateOf.Date);

        return isEqual;
    }
}

Однако, если я преобразую свое выражение LINQ в следующее псевдо, оно будет работать как ожидалось

query = TopLevelQuery.Where(x => x.EndedAt.HasValue && x.StartedAt >= startedAt && x.EndedAt.Value <= endedAt)
                     .Join(UserService.QueryUniqueRecords(),
                     entry => entry.UserId,
                     rec => rec.UserId,
                     (entry, rec) => new { entry, rec })
                     .Where(result => DbFunctions.TruncateTime(SqlFunctions.DateAdd("ss", entry.UtcOffset, entry.StartedAt)) == result.rec.DayOf 
                         && names.Contains(result.rec.Name))
                     .Select(x => x.entry);

Но я хочу иметь возможность повторно использовать одну и ту же логику в нескольких местахмой проект, поэтому я хочу извлечь его в какое-то расширение или метод.

Как можно извлечь вызовы DbFunctions и SqlFunctions в метод многократного использования, который можно использовать в LINQдо вызова AsEnumerable()?

ОБНОВЛЕНО

Я также пытался извлечь логику в лямбда-выражение, добавив следующий код в MyModel class

public class MyModel
{
    public DateTime StartedAt { get; set; }
    public int UtcOffset { get; set; }
    // ...

    public Expression<Func<MyModel, bool>> IsDateOf(DateTime dayOf)
    {
        return p => p.StartedAt.AddSeconds(p.UtcOffset) == dayOf.Date;
    }
}

тысCommon crawl ru Я пытался использовать его следующим образом:

query = TopLevelQuery.Where(x => x.EndedAt.HasValue && x.StartedAt >= startedAt && x.EndedAt.Value <= endedAt)
                     .Join(UserService.QueryUniqueRecords(),
                     entry => entry.UserId,
                     rec => rec.UserId,
                     (entry, rec) => new { entry, rec })
                     .Where(result => result.entry.IsDateOf(result.rec.DayOf) 
                         && names.Contains(result.rec.Name))
                     .Select(x => x.entry);

Но это приводит к следующей синтаксической ошибке при попытке использовать его

Оператор '&&' нельзя применить к операндам типа 'Expression<Func<MyModel,bool>> 'и bool.

Более того, я попытался сделать выражение IsDateOf статическим и назвал его так

query = TopLevelQuery.Where(x => x.EndedAt.HasValue && x.StartedAt >= startedAt && x.EndedAt.Value <= endedAt)
                     .Join(UserService.QueryUniqueRecords(),
                     entry => entry.UserId,
                     rec => rec.UserId,
                     (entry, rec) => new { entry, rec })
                     .Where(result => result.entry.Where(MyModel.IsDateOf(result.rec.DayOf))
                         && names.Contains(result.rec.Name))
                     .Select(x => x.entry);

Но это дает мне следующую синтаксическую ошибку

«MyMode» не содержит определения для Where, а для наилучшей перегрузки метода расширения Queryable.Where<MyModel>IQueryable<MyModel>, Expression<Func<MyModel, bool>>) требуется приемник типа IQueryable

1 Ответ

0 голосов
/ 27 декабря 2018

Удалите параметр из вашего метода и заставьте выражение принимать два параметра:

public static Expression<Func<MyModel, DateTime, bool>> IsDateOf 
    = (MyModel p, DateTime d) => p.StartedAt.AddSeconds(p.UtcOffset) == d.Date;

Примечание. Я сделал это статическим полем, а не методом.Затем в вашем выражении LINQ вам нужно вызвать его:

MyModel.IsDateOf(result.entry, result.rec.DayOf)

Если вы не сделаете его полем (или свойством), вы должны сначала вызвать метод, чтобы получить выражение;тогда вам нужно вызвать выражение:

MyModel.IsDateOf()(result.entry, result.rec.DayOf)

, что, на мой взгляд, выглядит странно.

...