简体   繁体   中英

How to create "inline if statement" with expressions in dynamic select for null checking

How to create "inline if statement" with expressions, in dynamic select, for null checking ?

I wrote a dynamic select LINQ expression, for a nested property of an object, but it throws an exception when it is null. so i want to check whether that property is null or not, that simple !

here is what i mean:

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

here is what i wrote

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;
    }

here is what GetLocalizedString generated:

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

Select expression

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

and here is the error message:

System.ArgumentException: 'Argument types do not match'

Select property is of type String

is there any way to create an expresion like X.Titles == null ? "" : [Linq] X.Titles == null ? "" : [Linq] ?

The expression equivalent of the C# conditional ?: operator is Expression.Condition . While Expression.IfThenElse you are using is the equivalent of C# if then else block.

Both methods return ConditionalExpression with Test , IfTrue and IfFalse properties populated. The difference is that the result Type of the Condition is the type of the operands, while for IfThenElse it is void , hence cannot be used in query expression trees.

So the answer to your concrete question is:

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

PS As a side node, I'm getting several errors from your code snippet, so I had to rearrange it like this in order to get w/o error to the above line:

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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM