c# - 如何在动态选择中使用表达式创建 "inline if statement"以进行空检查

标签 c# linq lambda expression entity-framework-core

如何在动态选择中使用表达式创建“内联 if 语句”以进行空值检查?

我为一个对象的嵌套属性写了一个动态选择 LINQ 表达式,但是当它为 null 时抛出异常。所以我想检查该属性是否为空,就这么简单!

我的意思是:

X.Where(...)
 .Select(X => new Y{
    ...
    Z = X.Titles == null ? "" : [Linq]
    ...
}).FirstOrDefault();

这是我写的

private static Expression GetLocalizedString(Expression stringExpression, SupportedCulture supportedCulture)
    {
        var expression = Expression.Parameter(typeof(APILocalizedString), nameof(APILocalizedString));
        
        var prop = Expression.Property(expression, nameof(APILocalizedString.SupportedCulture));
        var value = Expression.Constant(supportedCulture);
        var condition = Expression.Equal(prop, value);

        var where = Expression.Call(
            typeof (Enumerable),
            nameof(Enumerable.Where),
            new Type[] { typeof(APILocalizedString) },
            stringExpression,
            Expression.Lambda<Func<APILocalizedString, bool>>(condition, expression));

        var select = Expression.Call(
            typeof(Enumerable),
            nameof(Enumerable.Select),
            new Type[] { typeof(APILocalizedString), typeof(string) },
            where,
            Expression.Lambda<Func<APILocalizedString, string>>(
                Expression.Property(expression, nameof(APILocalizedString.Text)),
                expression
            ));

        var first = Expression.Call(
            typeof(Enumerable),
            nameof(Enumerable.First),
            new Type[] { typeof(APILocalizedString) },
            stringExpression);

        var defaultIfEmpty = Expression.Call(
            typeof(Enumerable),
            nameof(Enumerable.DefaultIfEmpty),
            new Type[] { typeof(string) },
            select,
            first);

        var firstOrDefault =
            Expression.Call(
            typeof(Enumerable),
            nameof(Enumerable.FirstOrDefault),
            new Type[] { typeof(string) },
            defaultIfEmpty);


        var nullCheck = Expression.Equal(stringExpression, Expression.Constant(null, stringExpression.Type));
        var result = Expression.IfThenElse(nullCheck, Expression.Constant(""), firstOrDefault);
            
        return result;
    }

这是 GetLocalizedString 生成的内容:

{IIF((X.Titles == null), "", X.Titles.Where(APILocalizedString => (APILocalizedString.SupportedCulture == EN)).DefaultIfEmpty(X.Titles.First()) .Select(APILocalizedString => APILocalizedString.Text).FirstOrDefault())}

选择表达式

... bindings.Add(Expression.Bind(property, GetLocalizedString(Expression.Property(parameter, "Titles"), SupportedCulture.EN))); ...

这是错误信息:

System.ArgumentException: '参数类型不匹配'

选择属性是字符串类型

有什么方法可以创建像 X.Titles == null 这样的表达式吗? "": [Linq] ?

最佳答案

C# 条件 ?: 运算符的等效表达式是 Expression.Condition .同时Expression.IfThenElse您使用的是 C# if then else block 的等价物。

两种方法都返回 ConditionalExpression填充了 TestIfTrueIfFalse 属性。区别在于Condition的结果Type是操作数的类型,而IfThenElsevoid,因此不能用于查询表达式树。

所以你的具体问题的答案是:

var result = Expression.Condition(nullCheck, Expression.Constant(""), firstOrDefault);

附言作为一个侧节点,我从你的代码片段中得到了几个错误,所以我不得不像这样重新排列它以便在上面的行中得到 w/o 错误:

private static Expression GetLocalizedString(Expression stringExpression, SupportedCulture supportedCulture)
{
    var expression = Expression.Parameter(typeof(APILocalizedString), nameof(APILocalizedString));

    var prop = Expression.Property(expression, nameof(APILocalizedString.SupportedCulture));
    var value = Expression.Constant(supportedCulture);
    var condition = Expression.Equal(prop, value);

    var where = Expression.Call(
        typeof(Enumerable),
        nameof(Enumerable.Where),
        new Type[] { typeof(APILocalizedString) },
        stringExpression,
        Expression.Lambda<Func<APILocalizedString, bool>>(condition, expression));

    var first = Expression.Call(
        typeof(Enumerable),
        nameof(Enumerable.First),
        new Type[] { typeof(APILocalizedString) },
        stringExpression);

    var defaultIfEmpty = Expression.Call(
        typeof(Enumerable),
        nameof(Enumerable.DefaultIfEmpty),
        new Type[] { typeof(APILocalizedString) },
        where,
        first);

    var select = Expression.Call(
        typeof(Enumerable),
        nameof(Enumerable.Select),
        new Type[] { typeof(APILocalizedString), typeof(string) },
        defaultIfEmpty,
        Expression.Lambda<Func<APILocalizedString, string>>(
            Expression.Property(expression, nameof(APILocalizedString.Text)),
            expression
        ));

    var firstOrDefault =
        Expression.Call(
        typeof(Enumerable),
        nameof(Enumerable.FirstOrDefault),
        new Type[] { typeof(string) },
        select);


    var nullCheck = Expression.Equal(stringExpression, Expression.Constant(null, stringExpression.Type));
    var result = Expression.Condition(nullCheck, Expression.Constant(""), firstOrDefault);

    return result;
}

关于c# - 如何在动态选择中使用表达式创建 "inline if statement"以进行空检查,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53873765/

相关文章:

c++ - 参数包扩展在 lambda 中不起作用

c# - 如何确定可以连接到给定远程 IP/DNS 地址的本地 IP 地址

c# - 无法创建类型的常量值在此上下文中仅支持原始类型或枚举类型

C#字典以列表为值,可以直接对列表进行操作吗?

c# - 保持对象实例在整个应用程序中可用

c# - LINQ 是如何包含工作的

c# - 如何删除 lambda 表达式的 Where 条件?

c# - 使用 linq 在列表中搜索字符串

c++ - 如何创建一个充满 C 风格函数指针和 lambda 的 vector (有和没有捕获)

c# - 使用 LINQ 拆分后获取字符串数组的索引