Comment réfléchir sur T pour construire un arbre d'expression pour une requête?

c# entity-framework expression-trees linq reflection

Question

J'essaie de construire une classe générique pour travailler avec des entités de EF. Cette classe parle aux référentiels, mais c'est cette classe qui crée les expressions envoyées aux référentiels. Quoi qu'il en soit, j'essaie simplement d'implémenter une méthode virtuelle qui servira de base pour les requêtes courantes. Spécifiquement, il acceptera un int et il lui suffira d'exécuter une requête sur la clé primaire de l'entité en question.

Je me suis moqué de lui et j'ai construit un reflet qui pourrait ne pas fonctionner. Je dis cela parce que je reçois une NotSupportedException avec un message de LINQ to Entities ne reconnaît pas la méthode 'System.Object GetValue (System.Object, System.Object [])', et cette méthode ne peut pas être traduite en expression de magasin. J'ai alors essayé une autre approche et produit la même exception, mais avec l'erreur suivante: Le type de noeud d'expression LINQ 'ArrayIndex' n'est pas pris en charge dans LINQ to Entities. Je sais que c'est parce qu'EF n'analysera pas l'expression comme le fera L2S.

Quoi qu'il en soit, j'espère que quelqu'un avec un peu plus d'expérience peut me diriger dans la bonne direction à cet égard. Je poste toute la classe avec les deux tentatives que j'ai faites.

public class Provider<T> where T : class {
    protected readonly Repository<T> Repository = null;

    private readonly string TEntityName = typeof(T).Name;

    [Inject]
    public Provider(
        Repository<T> Repository) {
        this.Repository = Repository;
    }

    public virtual void Add(
        T TEntity) {
        this.Repository.Insert(TEntity);
    }

    public virtual T Get(
        int PrimaryKey) {
        //  The LINQ expression node type 'ArrayIndex' is not supported in
        //  LINQ to Entities.
        return this.Repository.Select(
            t =>
                (((int)(t as EntityObject).EntityKey.EntityKeyValues[0].Value) == PrimaryKey)).Single();

        //  LINQ to Entities does not recognize the method
        //  'System.Object GetValue(System.Object, System.Object[])' method,
        //  and this method cannot be translated into a store expression.
        return this.Repository.Select(
            t =>
                (((int)t.GetType().GetProperties().Single(
                    p =>
                        (p.Name == (this.TEntityName + "Id"))).GetValue(t, null)) == PrimaryKey)).Single();
    }

    public virtual IList<T> GetAll() {
        return this.Repository.Select().ToList();
    }

    protected virtual void Save() {
        this.Repository.Update();
    }
}

MISE À JOUR pour @Gabe

Voici à quoi ressemble ma classe de référentiel:

public class Provider<T> where T : class {
    protected readonly Repository<T> Repository = null;

    private readonly string TEntityName = typeof(T).Name;

    [Inject]
    public Provider(
        Repository<T> Repository) {
        this.Repository = Repository;
    }

    public virtual void Add(
        T TEntity) {
        this.Repository.Insert(TEntity);
    }

    public virtual T Get(
        int PrimaryKey) {
        //  The LINQ expression node type 'ArrayIndex' is not supported in
        //  LINQ to Entities.
        return this.Repository.Select(
            t =>
                (((int)(t as EntityObject).EntityKey.EntityKeyValues[0].Value) == PrimaryKey)).Single();

        //  LINQ to Entities does not recognize the method
        //  'System.Object GetValue(System.Object, System.Object[])' method,
        //  and this method cannot be translated into a store expression.
        return this.Repository.Select(
            t =>
                (((int)t.GetType().GetProperties().Single(
                    p =>
                        (p.Name == (this.TEntityName + "Id"))).GetValue(t, null)) == PrimaryKey)).Single();
    }

    public virtual IList<T> GetAll() {
        return this.Repository.Select().ToList();
    }

    protected virtual void Save() {
        this.Repository.Update();
    }
}

Les noms des méthodes sont basés sur les fonctions SQL, et non sur les méthodes LINQ, ce qui explique pourquoi je pense que vous êtes en train de confondre le fonctionnement de mon référentiel.

Réponse acceptée

Comme Pauli le dit, vous devez créer manuellement Expression arbres d' Expression , bien que la réflexion ne soit pas nécessaire dans ce cas. Voici comment vous pouvez écrire votre fonction Get :

public virtual T Get(
    int PrimaryKey)
{
    var param = Expression.Parameter(typeof(T));
    // create expression for param => param.TEntityNameId == PrimaryKey
    var lambda = Expression.Lambda<Func<T, bool>>(
        Expression.Equal(
            Expression.Property(param, TEntityName + "Id"),
            Expression.Constant(PrimaryKey)),
        param);
    return this.Repository.Single(lambda);
}

Notez également que votre fonction GetAll n’a pas besoin de Select - return Repository.ToList(); fonctionnera aussi bien.




Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi