Come riflettere su T per costruire un albero di espressioni per una query?

c# entity-framework expression-trees linq reflection

Domanda

Sto cercando di creare una classe generica per lavorare con entità di EF. Questa classe parla con gli archivi, ma è questa classe che crea le espressioni inviate ai repository. Ad ogni modo, sto solo cercando di implementare un metodo virtuale che fungerà da base per le query più comuni. In particolare, accetta un int e deve solo eseguire una query sulla chiave primaria dell'entità in questione.

Sono andato in giro e ho costruito un riflesso che potrebbe funzionare o meno. Dico questo perché ottengo NotSupportedException con un messaggio LINQ su Entities che non riconosce il metodo "System.Object GetValue (System.Object, System.Object [])" e questo metodo non può essere tradotto in un'espressione di archivio. Quindi ho provato un altro approccio e ha prodotto la stessa eccezione, ma con l'errore del tipo di nodo di espressione LINQ "ArrayIndex" non è supportato in LINQ alle entità. So che è perché EF non analizzerà l'espressione come farà L2S.

Ad ogni modo, sto saltando qualcuno con un po 'più di esperienza in grado di indicarmi la direzione giusta su questo. Sto postando l'intera classe con entrambi i tentativi che ho fatto.

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

AGGIORNAMENTO per @Gabe

Ecco come si presenta la mia classe di repository:

public class Repository<T> where T : class {
    protected readonly ObjectContext ObjectContext = null;
    private readonly IObjectSet<T> ObjectSet = null;

    [Inject]
    public Repository(
        ObjectContext ObjectContext) {
        this.ObjectContext = ObjectContext;

        this.ObjectSet = this.ObjectContext.CreateObjectSet<T>();
    }

    public virtual void Delete(
        T Entity) {
        this.ObjectSet.DeleteObject(Entity);
    }

    public virtual void Insert(
        T Entity) {
        this.ObjectSet.AddObject(Entity);
    }

    public virtual IQueryable<T> Select() {
        return this.ObjectSet;
    }

    public virtual IQueryable<T> Select(
        Expression<Func<T, bool>> Selector) {
        return this.ObjectSet.Where(Selector);
    }

    public virtual void Update() {
        this.ObjectContext.SaveChanges();
    }
}

I nomi dei metodi si basano sulle funzioni SQL, non sui metodi LINQ, che è dove penso che tu ti stia confondendo su come funziona il mio repository.

Risposta accettata

Come allude a Pauli, è necessario creare manualmente gli alberi di Expression , sebbene in questo caso non sia necessario eseguire la riflessione. Ecco come potresti scrivere la tua funzione 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);
}

Inoltre, GetAll presente che la funzione GetAll non ha bisogno di Select - return Repository.ToList(); funzionerà altrettanto bene



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é