Избегайте ошибки оценки клиента запроса в запросе с определением метода внутри класса сущности - PullRequest
0 голосов
/ 22 февраля 2019

В проекте .NET Core 2.1 я использую EF Core с шаблоном Command (используя библиотеку MediatR ) в базе данных SQL Server.

Iнастроить проект, чтобы избежать оценки запросов клиента, используя следующие параметры:

var phaseOptions = new DbContextOptionsBuilder<PhaseDbContext>().UseSqlServer(configuration.GetConnectionString("PhaseDbContext"),
        sqlServerOptions => sqlServerOptions
            .EnableRetryOnFailure(
                maxRetryCount: 5,
                maxRetryDelay: TimeSpan.FromSeconds(30),
                errorNumbersToAdd: null))
    .ConfigureWarnings(warnings => warnings
        .Throw(RelationalEventId.QueryClientEvaluationWarning)) // Disable Client query evaluation
    .Options;

Теперь я получаю QueryClientEvaluationException с этим запросом:

var articleCodes = await PhaseContext.PhaseArticles
    .Where(a => !request.ArticleFamily.HasValue || a.GetArticleFamily() == request.ArticleFamily.Value)
    .ToListAsync(cancellationToken);

Проблема в a.GetArticleFamily() вызов метода, потому что этот метод теперь определяется следующим образом внутри класса сущности PhaseArticle:

public class PhaseArticle
{
    public int Id { get; set; }
    public string Code { get; set; }
    public string Description { get; set; }
    public string UnitOfMeasure { get; set; }
    public string Category { get; set; }
    public string Group { get; set; }
    public string Family { get; set; }
    public double UnitCost { get; set; }
    public string AdditionalDescription { get; set; }
    public string ExternalCode { get; set;}
    public string ColorCode { get; set;}
    public string Note { get; set; }

    public ArticleFamily GetArticleFamily()
    {
        switch (Family)
        {
            case "CEL":
                return ArticleFamily.Cell;
            case "STR":
                return ArticleFamily.String;
            case "RAW":
                return ArticleFamily.OtherRawMaterial;
            case "SFP":
                return ArticleFamily.SemiFinishedPanel;
            case "FP":
                return ArticleFamily.FinishedPanel;
            default:
                return ArticleFamily.Other;
        }
    }
}

Теперь я хочу узнать, возможно ли сохранить параметр QueryClientEvaluationWarning путем рефакторинга(и, вероятно, удаляясь от класса сущности) метод GetArticleFamily ().

Обновление 2019/02/26

@ StriplingWarrior Я снова обновил код с вашим предложением о ValueConverter(), но теперь выдает эту ошибку:

Невозможно преобразовать лямбда-выражение в дерево выражений.

Обновление 2019/02/25

ПодписанПредложение @StriplingWarrior, я пытаюсь написать собственный конвертер, но не могучтобы компилировать мой код.

Ошибка с кодом ниже составляет примерно возвращаемое значение первого switch блока (это string, но ожидается, что оно будет enum) и об ожидаемом входном значении второго блока переключателей (это string, но ожидается, что оно будет enum).

Это код:

public static void ApplyPhaseConversions<T>(this ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<PhaseArticle>()
        .Property(e => e.Family)
        .HasConversion(new ValueConverter<ArticleFamily, string> {
            v =>
            {
                switch (v)
                {
                    case ArticleFamily.Cell:
                        return "CEL";
                    case ArticleFamily.String:
                        return "STR";
                    case ArticleFamily.OtherRawMaterial:
                        return "RAW";
                    case ArticleFamily.SemiFinishedPanel:
                        return "SFP";
                    case ArticleFamily.FinishedPanel:
                        return "FP";
                    default:
                        return "";
                }
            },
            v =>
            {
                switch (v)
                {
                    case "CEL":
                        return ArticleFamily.Cell;
                    case "STR":
                        return ArticleFamily.String;
                    case "RAW":
                        return ArticleFamily.OtherRawMaterial;
                    case "SFP":
                        return ArticleFamily.SemiFinishedPanel;
                    case "FP":
                        return ArticleFamily.FinishedPanel;
                    default:
                        return ArticleFamily.Other;
                }
            }});
}

Ответы [ 3 ]

0 голосов
/ 22 февраля 2019

Похоже, вы используете GetArticleFamily() для преобразования между значениями базы данных и вашими перечислениями C #.EF Core имеет встроенную функцию, называемую «Преобразование значений», которая предназначена для решения этой проблемы: https://docs.microsoft.com/en-us/ef/core/modeling/value-conversions

Вы должны иметь возможность определить ValueConverter для преобразования в и из ArticleFamily значений, а затем изменить типиз свойства Family для ArticleFamily и используйте это свойство в своем запросе:

var articleCodes = await PhaseContext.PhaseArticles
    .Where(a => !request.ArticleFamily.HasValue || a.Family == request.ArticleFamily.Value)
    .ToListAsync(cancellationToken);

PS - я не уверен, какой запрос произведет приведенный выше код, но это может бытьлучше составить ваш запрос так:

var articleQuery = PhaseContext.PhaseArticles.AsQueryable();
if(request.ArticleFamily.HasValue)
{
    articleQuery = articleQuery.Where(a => a.Family == request.ArticleFamily.Value);
}
var articleCodes = await articleQuery.ToListAsync(cancellationToken);
0 голосов
/ 27 февраля 2019

Наконец, решение было почти готово, как сказал @StriplingWarrior.

Из-за ограничений в компиляторе C #, из-за которых он не может создавать деревья выражений для этого кода, решение заключается в фабрикациипреобразовать код в методы и затем вызвать их в HasConversion.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<PhaseArticle>()
        .Property(e => e.Family)
        .HasConversion(new ValueConverter<ArticleFamily, string>(
            v => StringFromArticleFamily(v),
            v => ArticleFamilyFromString(v));
}

private static ArticleFamily ArticleFamilyFromString(string family)
{
    switch (family)
    {
        case "CEL":
            return ArticleFamily.Cell;
        case "STR":
            return ArticleFamily.String;
        case "RAW":
            return ArticleFamily.OtherRawMaterial;
        case "SFP":
            return ArticleFamily.SemiFinishedPanel;
        case "FP":
            return ArticleFamily.FinishedPanel;
        default:
            return ArticleFamily.Other;
    }
}

private static string StringFromArticleFamily(ArticleFamily articleFamily)
{
    switch (articleFamily)
    {
        case ArticleFamily.Cell:
            return "CEL";
        case ArticleFamily.String:
            return "STR";
        case ArticleFamily.OtherRawMaterial:
            return "RAW";
        case ArticleFamily.SemiFinishedPanel:
            return "SFP";
        case ArticleFamily.FinishedPanel:
            return "FP";
        default:
            return "";
    }
}
0 голосов
/ 22 февраля 2019

Вы можете создать новую переменную и передать результат request.ArticleFamily.Value, чтобы он мог вернуть ArticleFamily.Cell или ArticleFamily.String и затем выполнить запрос

, например,

if(request != null && !request.ArticleFamily.HasValue)
// or throw an exception here
 return ...;

ArticleFamily newVariable = (ArticleFamily)Enum.Parse(typeof(ArticleFamily), request.ArticleFamily);
var articleCodes = await PhaseContext.PhaseArticles
    .Where(a => a.Family == newVariable)
    .ToListAsync(cancellationToken);

Проверка параметров метода должна быть выполнена до выполнения запроса.Другое дело, что произойдет, если запрос будет null?

Редактировать

Также необходимо проверить объект request.Может быть случай, когда вы делаете опечатку или ошибку в структуре (вы забываете добавить запятую после определения значения поля) объекта JSON, который вы отправляете в API.В таком случае объект запроса будет иметь значение null, поэтому необходимо проверить такое поведение.Например, вы можете добавить

if (!ModelState.IsValid)
{
   return BadRequest(ModelState);
}

в действие вашего контроллера для проверки всего тела запроса.Клиент получит правильное сообщение об ошибке.

...