C# 避免在实体类内具有方法定义的查询上出现查询客户端计算错误

C# 避免在实体类内具有方法定义的查询上出现查询客户端计算错误,c#,sql-server,entity-framework,entity-framework-core,lazy-evaluation,C#,Sql Server,Entity Framework,Entity Framework Core,Lazy Evaluation,在一个.NETCore2.1项目中,我在SQLServer数据库上使用带有命令模式的EFCore(使用MediatR库) 我使用以下设置设置项目以避免客户端查询评估: var phaseOptions = new DbContextOptionsBuilder<PhaseDbContext>().UseSqlServer(configuration.GetConnectionString("PhaseDbContext"), sqlServerOp

在一个.NETCore2.1项目中,我在SQLServer数据库上使用带有命令模式的EFCore(使用MediatR库)

我使用以下设置设置项目以避免客户端查询评估:

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;
问题出在
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;
        }
    }
}
现在,我想知道是否可以通过某种方式重构GetArticleFamily()方法(并可能从实体类中移除),来保留
QueryClientEvaluationWarning
选项

更新2019/02/26 @StriplingWarrior我已经用您关于
ValueConverter()
的建议再次更新了代码,但现在它给出了以下错误:

无法将Lambda表达式转换为表达式树

更新2019/02/25 根据@StriplingWarrior的建议,我正试图编写一个自定义转换器,但我无法使代码编译

下面代码的错误是关于第一个
开关
块的返回值(它是
字符串
,但预期是
枚举
),以及关于第二个开关块的预期输入值(它是
字符串
,但预期是
枚举

代码如下:

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;
                }
            }});
}
公共静态void ApplyPhaseConversions(此ModelBuilder ModelBuilder)
{
建模者
.实体()
.Property(e=>e.Family)
.HasConversion(新值转换器{
v=>
{
开关(v)
{
案例1.家庭单元:
返回“CEL”;
case ArticleFamily.String:
返回“STR”;
案例ArticleFamily.Other原材料:
返回“原始”;
案例ArticleFamily.SemiFinishedPanel:
返回“SFP”;
案例ArticleFamily.FinishedPanel:
返回“FP”;
违约:
返回“”;
}
},
v=>
{
开关(v)
{
案例“CEL”:
返回家庭。单元格;
“STR”案:
返回ArticleFamily.String;
案例“原始”:
返回ArticleFamily.OtherRawMaterial;
案例“SFP”:
返回ArticleFamily.SemiFinishedPanel;
案例“FP”:
返回ArticleFamily.FinishedPanel;
违约:
返回家庭。其他;
}
}});
}

您可以创建一个新变量并传输request.ArticleFamily.Value结果,这样它就可以返回ArticleFamily.Cell或ArticleFamily.String,然后运行查询

e、 g

应该在运行查询之前验证方法的参数。另一件事是,如果请求为
null
,会发生什么

编辑

还需要验证
请求
对象。当您发送给API的JSON对象在结构上出错(定义字段值后忘记添加逗号)时,可能会出现这种情况。在这种情况下,请求对象将具有
null
值,因此需要验证这种行为。例如,您可以添加

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

在控制器验证整个请求主体的操作中。客户端将收到正确的错误消息。

看起来您正在使用
GetArticleFamily()
在数据库值和C#枚举之间进行转换。EF Core有一个名为值转换的内置功能,旨在解决以下问题:

您应该能够定义一个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);

最后,正如@StriplingWarrior所说的,解决方案几乎就在那里

由于C#编译器的限制,它无法为此代码创建表达式树,因此解决方案是将转换代码工厂化为方法,然后在
hascoveration
中调用这些方法

模型创建时受保护的覆盖无效(ModelBuilder ModelBuilder)
{
建模者
.实体()
.Property(e=>e.Family)
.HasConversion(新值转换器(
v=>StringFromArticleFamily(v),
v=>ArticleFamilyFromString(v));
}
私有静态ArticleFamily ArticleFamilyFromString(字符串族)
{
开关(系列)
{
案例“CEL”:
返回家庭。单元格;
“STR”案:
返回ArticleFamily.String;
案例“原始”:
返回ArticleFamily.OtherRawMaterial;
案例“SFP”:
返回ArticleFamily.SemiFinishedPanel;
案例“FP”:
返回ArticleFamily.FinishedPanel;
违约:
返回家庭。其他;
}
}
私有静态字符串StringFromArticleFamily(ArticleFamily ArticleFamily)
{
交换机(articleFamily)
{
案例1.家庭单元:
返回“CEL”;
case ArticleFamily.String:
返回“STR”;
案例ArticleFamily.Other原材料:
返回“原始”;
案例ArticleFamily.SemiFinishedPanel:
返回“SFP”;
案例ArticleFamily.FinishedPanel:
返回“FP”;
违约:
返回“”;
}
}

@GoldenAge:看起来像是请求。ArticleFamily是一个选项
var articleCodes = await PhaseContext.PhaseArticles
    .Where(a => !request.ArticleFamily.HasValue || a.Family == request.ArticleFamily.Value)
    .ToListAsync(cancellationToken);
var articleQuery = PhaseContext.PhaseArticles.AsQueryable();
if(request.ArticleFamily.HasValue)
{
    articleQuery = articleQuery.Where(a => a.Family == request.ArticleFamily.Value);
}
var articleCodes = await articleQuery.ToListAsync(cancellationToken);