Créer une expression lambda avec un nouveau type anonyme à l'exécution

anonymous-types c# entity-framework expression-trees reflection

Question

Je veux invoquer une méthode qui attend un paramètre comme celui-ci:

Expression<Func<sometype, 'a>> expr

J'ai besoin de construire ce paramètre à l'exécution, car je ne saurai pas à quoi ressemblera le type anonyme auparavant; il pourrait avoir n'importe quelle quantité de champs:

x => new { a=x.a, b=x.b, c=x.c, etc... }

Au moment de l'exécution, je peux créer un type qui a la même "signature" (est-ce le bon mot pour cela?) Que le type anonyme souhaité, mais la question est: comment construire cette expression lambda à l'exécution à partir de cela? En particulier, Expression.New me dérange, car je dois lui transmettre un constructorInfo que je dois obtenir à partir d'un type existant (qui peut effectivement être un type anonyme, mais je ne peux pas créer de type anonyme à l'exécution. Ou existe-t-il une façon de faire ça?).

Mise à jour (un peu de contexte comme demandé dans les commentaires)

La méthode que je veux invoquer est la suivante:

DependentNavigationPropertyConfiguration.HasForeignKey<TKey>(Expression<Func<TDependentEntityType, TKey>> foreignKeyExpression)

La raison pour laquelle je souhaite effectuer cette opération est de créer automatiquement une propriété de navigation pour une entité qui hérite d'une certaine classe de base incluant la clé de cette classe de base dans la clé étrangère. Puisqu'une entité peut avoir plusieurs champs de clé de n'importe quel type, le type TKey ne m'est connu qu'au moment de l'exécution.

Réponse acceptée

Utilisez une méthode séparée:

public static void Main()
{
    var myExpression = Express(str => new { 
        String = str, 
        Length = str.Length 
    });

    // We can compile/use it as well...
    var compiledExpression = myExpression.Compile();
    var anonymousOutput = compiledExpression("Input String");

    Console.WriteLine(anonymousOutput.String); // Output: Input String
    Console.WriteLine(anonymousOutput.Length); // Output: 12

    Debug.WriteLine(myExpression); // Output: "str => new <>f__AnonymousType0`2(String = str, Length = str.Length)"
    Console.ReadLine();
}


static Expression<Func<String, T>> Express<T>(Expression<Func<String, T>> expression)
{
    return expression;
}

Notez cependant que le type de départ (dans l'exemple de String ) doit être connu à l'avance.

Mise à jour :

Étant donné que vous essayez de créer dynamiquement un type, je vais vous donner un exemple simple.

public static void Main()
{
        // Create an anonymous type with two fields
    Type myAnonymousType = CreateNewType<String, Int32>();
    dynamic myAnon = Activator.CreateInstance(myAnonymousType);

    myAnon.FieldA = "A String";
    myAnon.FieldB = 1234;


    Console.WriteLine(myAnon.FieldA); // Output : "AString"
    Console.WriteLine(myAnon.FieldB); // Output : 1234
    Console.ReadLine();
}

public static Type CreateNewType<TFieldTypeA, TFieldTypeB>()
{
    // Let's start by creating a new assembly
    AssemblyName dynamicAssemblyName = new AssemblyName("MyAsm");
    AssemblyBuilder dynamicAssembly = AssemblyBuilder.DefineDynamicAssembly(dynamicAssemblyName, AssemblyBuilderAccess.Run);
    ModuleBuilder dynamicModule = dynamicAssembly.DefineDynamicModule("MyAsm");

    // Now let's build a new type
    TypeBuilder dynamicAnonymousType = dynamicModule.DefineType("MyAnon", TypeAttributes.Public);

    // Let's add some fields to the type.
    FieldInfo dynamicFieldA = dynamicAnonymousType.DefineField("FieldA", typeof(TFieldTypeA), FieldAttributes.Public);
    FieldInfo dynamicFieldB = dynamicAnonymousType.DefineField("FieldB", typeof(TFieldTypeB), FieldAttributes.Public);

    // Return the type to the caller
    return dynamicAnonymousType.CreateType();
}

Comme vous pouvez le constater, ceci est un peu plus compliqué. Si vous souhaitez approfondir le sujet, référez-vous définitivement à Reflectoin.Emit .


Réponse d'expert

Les types anonymes sont une fonctionnalité du compilateur. Si vous ne demandez pas au compilateur de les créer au moment de la compilation, vous devrez alors utiliser la méta-programmation - TypeBuilder ou peut-être CSharpCodeProvider . Vous feriez peut-être mieux d'utiliser des n-uplets - au moins, ils sont faciles à créer (vous pouvez utiliser Tuple.Create assez facilement).

Quant à l'expression; Je suggérerais de le taper comme Expression<Func<sometype, object>> - ce qui fonctionnera pour n'importe quelle formulation. Le code inspectant l' Expression peut bien sûr déterminer le type réel.



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow