Dynamic MemberExpression

c# dynamic expression-trees linq

Domanda

Sto volendo creare un membroExpression conoscendo solo il nome del campo; per esempio:

public static Expression<Func<TModel, T>> GenerateMemberExpression<TModel, T>(string fieldName)
    {
        PropertyInfo fieldPropertyInfo;

        fieldPropertyInfo = typeof(TModel).GetProperty(fieldName);

        var entityParam = Expression.Parameter(typeof(TModel), "e"); // {e}
        var columnExpr = Expression.MakeMemberAccess(entityParam, fieldPropertyInfo); // {e.fieldName}
        var lambda = Expression.Lambda(columnExpr, entityParam) as Expression<Func<TModel, T>>; // {e => e.column}

        return lambda;
    }

Il problema con quanto sopra è che il tipo di campo deve essere fortemente digitato. Passando "oggetto" come tipo di campo non funziona. C'è un modo per generare questo? Anche Dynamic LINQ non sembra funzionare.

Risposta accettata

Esistono numerosi problemi con il tuo codice:

  1. Il parametro del tuo metodo è chiamato fieldName , ma stai ottenendo una proprietà con esso.
  2. Si sta utilizzando il metodo Expression.Lambda non generico per generare l'espressione, che può scegliere un tipo di delegato inappropriato se l'argomento tipo T passato al metodo non è uguale al tipo di proprietà. In questo caso, il as gettato dall'espressione di ritorno di tipo del metodo non riuscirà e restituire null . Soluzione: utilizzare il metodo Lambda generico con gli argomenti di tipo appropriati. Nessuna fusione richiesta
  3. Se risolvi il secondo problema, le cose funzioneranno correttamente quando una conversione di riferimento sicura è disponibile dal tipo di proprietà a T , ma non quando sono richieste conversioni più complicate come il pugilato / il sollevamento. Soluzione: utilizzare il metodo Expression.Convert dove necessario.

Ecco un aggiornamento del tuo esempio che risolve questi problemi:

public static Expression<Func<TModel, T>> GenerateMemberExpression<TModel, T>
   (string propertyName)
{
    var propertyInfo = typeof(TModel).GetProperty(propertyName);

    var entityParam = Expression.Parameter(typeof(TModel), "e"); 
    Expression columnExpr = Expression.Property(entityParam, propertyInfo);

    if (propertyInfo.PropertyType != typeof(T))
        columnExpr = Expression.Convert(columnExpr, typeof(T));

    return Expression.Lambda<Func<TModel, T>>(columnExpr, entityParam);
}

Ciò renderà tutte le seguenti chiamate riuscite:

GenerateMemberExpression<FileInfo, string>("Name");
GenerateMemberExpression<string, int>("Length");

// Reference conversion
GenerateMemberExpression<FileInfo, object>("Name");          

//Boxing conversion
GenerateMemberExpression<string, object>("Length");

//Lifted conversion
GenerateMemberExpression<string, int?>("Length");

Risposta popolare

Prova a convertire manualmente il valore del campo in caso di passaggio di un "oggetto". Per esempio:

var columnExpr = Expression.MakeMemberAccess(entityParam, fieldPropertyInfo); // {e.fieldName}
if (T.GetType().Equals(typeof(object)))
{
    columnExpr = Expression.Convert(columnExpr, typeof(object));
}

Spero che questo ti possa aiutare.



Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché