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

3
如何使用表达式在动态选择中创建“内联if语句”以进行空值检查?
我编写了一个针对对象的嵌套属性的动态选择LINQ表达式,但当该属性为空时,它会抛出异常。因此,我想检查该属性是否为空,这很简单!
以下是我的意思:
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: '参数类型不匹配'

Select属性的类型为String

是否有办法创建像X.Titles == null ? "" : [Linq]这样的表达式?

1个回答

2
C#条件运算符?:的表达式等效形式是Expression.Condition。而你正在使用的Expression.IfThenElse相当于C#的if then else块。
两种方法都返回带有TestIfTrueIfFalse属性填充的ConditionalExpression。区别在于Condition的结果Type是操作数的类型,而对于IfThenElse则为void,因此不能在查询表达式树中使用。
因此,你具体问题的答案是:
var result = Expression.Condition(nullCheck, Expression.Constant(""), 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 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;
}

谢谢,你知道我为什么会得到这个错误吗?System.ArgumentException: '类型为'APILocalizedString'的表达式不能用于方法'System.Collections.Generic.IEnumerable``1[System.String] DefaultIfEmpty[String](System.Collections.Generic.IEnumerable``1[System.String], System.String)'的类型为'System.String'的参数。' - Mohammad
请每个帖子只提一个问题。但是,如果您要求代码片段,则是因为first的类型为APILocalizedString,而selectdefaultIfEmpty的类型为IEnumerable<string>。这就是为什么我重新排列了调用顺序,使DefaultIfEmptyWhere之后,SelectDefaultIfEmpty之后。 - Ivan Stoev
你们有没有时间看一下我的问题:如何在EFCore Select中编写内联If语句(SQL IIF)? - ttugates

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接