Come creare "inline if statement" con espressioni nella selezione dinamica per il controllo nullo
Ho scritto un'espressione di selezione dinamica linq per una proprietà nidificata di un oggetto, ma genera un'eccezione quando è nullo. quindi voglio verificare se quella proprietà è nullo o no, così semplice!
ecco cosa intendo:
X.Where(...)
.Select(X => new Y{
...
Z = X.Titles == null ? "" : [Linq]
...
}).FirstOrDefault();
ecco cosa ho scritto
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;
}
ecco cosa ha generato GetLocalizedString:
{IIF((X.Titles == null), "", X.Titles.Where(APILocalizedString => (APILocalizedString.SupportedCulture == EN)).DefaultIfEmpty(X.Titles.First()).Select(APILocalizedString => APILocalizedString.Text).FirstOrDefault())}
Seleziona espressione
... bindings.Add(Expression.Bind(property, GetLocalizedString(Expression.Property(parameter, "Titles"), SupportedCulture.EN))); ...
e qui è il messaggio di errore:
System.ArgumentException: 'Argument types do not match'
La proprietà Select è di tipo String
c'è un modo per creare X.Titles == null ? "" : [Linq]
come X.Titles == null ? "" : [Linq]
?
L'espressione equivalente dell'operatore condizionale C # ?:
è Expression.Condition
. Mentre Expression.IfThenElse
si sta utilizzando è l'equivalente di C # if then else
blocco.
Entrambi i metodi restituiscono ConditionalExpression
con le proprietà Test
, IfTrue
e IfFalse
popolate. La differenza è che il risultato Type
della Condition
è il tipo di operandi, mentre per IfThenElse
è void
, quindi non può essere utilizzato negli alberi di espressione della query.
Quindi la risposta alla tua domanda concreta è:
var result = Expression.Condition(nullCheck, Expression.Constant(""), firstOrDefault);
PS Come nodo laterale, sto ricevendo diversi errori dal tuo snippet di codice, quindi ho dovuto riorganizzarlo in questo modo per ottenere un errore nella riga sopra:
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;
}