c# - 避免使用实体类内的方法定义进行查询时出现查询客户端评估错误

标签 c# sql-server entity-framework entity-framework-core lazy-evaluation

.NET Core 2.1 项目中,我在 SQL Server 数据库上使用带有命令模式的 EF Core(使用 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;

现在我通过此查询得到一个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;
        }
    }
}

现在,我想知道是否可以通过某种方式重构(并且可能远离实体类)GetArticleFamily() 方法来保留 QueryClientEvaluationWarning 选项。

更新2019/02/26

@StriplingWarrior 我已根据您关于 ValueConverter() 的建议再次更新了代码,但现在出现此错误:

Cannot convert Lambda expression into a tree of expressions.

更新2019/02/25

按照@StriplingWarrior的建议,我正在尝试编写一个自定义转换器,但我无法编译我的代码。

以下代码的错误与第一个 switch block 的返回值有关(它是 string,但预计它是 enum)以及第二个开关 block 的预期输入值(它是一个字符串,但预计是一个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;
                }
            }});
}

最佳答案

您似乎正在使用 GetArticleFamily() 在数据库值和 C# 枚举之间进行转换。 EF Core 有一个名为“值转换”的内置功能,旨在解决此问题:https://learn.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);

关于c# - 避免使用实体类内的方法定义进行查询时出现查询客户端评估错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54830603/

相关文章:

c# - Windows 服务和桌面应用程序之间的交互

sql - 为查询字符串声明变量

c# - EF - 在不使用 foreach 循环的情况下更新数据库中的多行

asp.net - 连接到实体数据源的标准 ASP.NET 控件是否提供内置验证?

c# - SQLite C# 简单数据库查找

c# - 如何在 Windows 服务或控制台应用程序中更改有问题的系统 IP

c# - 如何让文本框超过 20 像素高?

sql-server - 将所有表和存储过程下载到文件

sql-server - SQL Server 中单行 MERGE/upsert 的语法

entity-framework - 是否可以从未保存的 EF4 上下文中查看添加的实体?