Wie über T nachdenken, um einen Ausdrucksbaum für eine Abfrage zu erstellen?

c# entity-framework expression-trees linq reflection

Frage

Ich versuche eine generische Klasse zu erstellen, um mit Entitäten von EF zu arbeiten. Diese Klasse spricht mit Repositorys, aber diese Klasse erstellt die an die Repositorys gesendeten Ausdrücke. Wie auch immer, ich versuche nur, eine virtuelle Methode zu implementieren, die als Grundlage für häufige Abfragen dient. Insbesondere wird ein int akzeptiert und es muss nur eine Abfrage über den Primärschlüssel der betreffenden Entität durchgeführt werden.

Ich habe damit herumgewirbelt und ich habe eine Reflektion gebaut, die funktionieren kann oder auch nicht. Ich sage das, weil ich eine NotSupportedException mit einer Nachricht von LINQ to Entities nicht die Methode 'System.Object GetValue (System.Object, System.Object [])' Methode erhält, und diese Methode kann nicht in einen Ausdruck Speicher übersetzt werden. Also habe ich einen anderen Ansatz versucht und es wurde die gleiche Ausnahme erzeugt, aber mit dem Fehler von LINQ expression wird der Knotentyp 'ArrayIndex' in LINQ to Entities nicht unterstützt. Ich weiß, das liegt daran, dass EF den Ausdruck nicht so analysieren wird wie L2S.

Wie auch immer, ich hüpfe mit ein bisschen mehr Erfahrung kann mich in die richtige Richtung auf diesem zeigen. Ich poste die gesamte Klasse mit beiden Versuchen, die ich gemacht habe.

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

UPDATE für @Gabe

So sieht meine Repository-Klasse aus:

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

Die Namen der Methoden basieren auf den SQL-Funktionen, nicht auf den LINQ-Methoden. Ich denke, Sie sind verwirrt, wie mein Repository funktioniert.

Akzeptierte Antwort

Wie Pauli darauf anspielt, müssen Sie Expression Bäume manuell erstellen, obwohl in diesem Fall keine Reflexion erforderlich ist. So können Sie Ihre Get Funktion schreiben:

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

Beachten Sie auch, dass Ihre GetAll Funktion Select nicht benötigt - return Repository.ToList(); wird genauso gut funktionieren.



Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum