Abfangen aller EF6 Linq-Abfragen

entity-framework-6 expression-trees interceptor linq

Frage

Ich habe eine Funktion, die ich auf jeder ausgeführten Linq-Abfrage auf einem DbContext ausführen möchte, um den Ausdrucksbaum vor der Ausführung zu ändern. Ich habe mir die IDbCommandTreeInterceptor-Schnittstelle angeschaut, aber diese scheint keinen Ausdrucksbaum zu bieten (was wohl verständlich ist, da es zu diesem Zeitpunkt noch keine Linq-Abfrage war).

Gibt es eine Möglichkeit, alle Ausdrücke vor der Ausführung abzufangen und zu ändern?

anz. Dies muss eine Linq-Baum-Modifikation sein, da ich bereits ein Framework zum Modifizieren von Linq-Bäumen erstellt habe, das ursprünglich für Linq to SQL gedacht war.

Akzeptierte Antwort

Das Erstellen eines Proxys für den LINQ-Provider zum Abfangen jeder Ausführung von LINQ-Ausdrücken (wie in den Kommentaren empfohlen) ist immer noch eine gute Lösung. In der Tat spiele ich mit diesem Zeug in diesem Projekt , das EF6 einschließlich EF6 async Abfragen ausdrücklich unterstützt. Sie können einen standardmäßigen .NET ExpressionVisitor erstellen, um eine Überwachung durchzuführen:

intercepted = query.Rewrite(new MyInterceptor());

Aber die Frage verlangt auch, "auf jeder ausgeführten Linq-Abfrage auf einem DbContext zu laufen", und das wird der schwierige Teil sein. Ein Ansatz kann eine Art von Abstraktion von DbContext / DbSet , so dass Ihr Code nicht direkt auf DbSet Objekte DbSet . Und innerhalb der Umsetzung dieser Abstraktion kann das Abfangen passieren ...

Ein anderer Ansatz (und ich denke, dass diese Frage am besten beantwortet) wäre ein Proxy für DbSet , das den LINQ-Proxy für Abfragen aufruft, der das Abfangen ermöglicht. Zuerst müssen wir von DbSet erben:

public class DbSetProxy<TEntity> : DbSet<TEntity>,
                                   IQueryable<TEntity>,
                                   IDbAsyncEnumerable<TEntity>
    where TEntity : class
{
    private readonly DbSet<TEntity> set;
    private readonly DbQuery<TEntity> query;

    private readonly IQueryable<TEntity> intercepted;

    public DbSetProxy(DbSet<TEntity> set)
        : this(set, set)
    {
    }

    public DbSetProxy(DbSet<TEntity> set, DbQuery<TEntity> query)
    {
        this.set = set;
        this.query = query;

        // use NeinLinq or any other LINQ proxy library
        intercepted = query.Rewrite(new MyInterceptor());
    }
}

Dann ist es notwendig, alle Mitglieder zu überschreiben, um das tatsächliche DbSet für Nicht-Abfrage-Stuff DbSet (das ist ein bisschen ausführlich):

public override DbQuery<TEntity> AsNoTracking()
{
    return new DbSetProxy<TEntity>(set, query.AsNoTracking());
}

public override DbQuery<TEntity> AsStreaming()
{
    return new DbSetProxy<TEntity>(set, query.AsStreaming());
}

public override DbQuery<TEntity> Include(string path)
{
    return new DbSetProxy<TEntity>(set, query.Include(path));
}

public override TEntity Add(TEntity entity)
{
    return set.Add(entity);
}

public override IEnumerable<TEntity> AddRange(IEnumerable<TEntity> entities)
{
    return set.AddRange(entities);
}

public override TEntity Attach(TEntity entity)
{
    return set.Attach(entity);
}

public override TEntity Create()
{
    return set.Create();
}

public override TDerivedEntity Create<TDerivedEntity>()
{
    return set.Create<TDerivedEntity>();
}

public override TEntity Find(params object[] keyValues)
{
    return set.Find(keyValues);
}

public override Task<TEntity> FindAsync(params object[] keyValues)
{
    return set.FindAsync(keyValues);
}

public override Task<TEntity> FindAsync(CancellationToken cancellationToken, params object[] keyValues)
{
    return set.FindAsync(cancellationToken, keyValues);
}

public override TEntity Remove(TEntity entity)
{
    return set.Remove(entity);
}

public override IEnumerable<TEntity> RemoveRange(IEnumerable<TEntity> entities)
{
    return set.RemoveRange(entities);
}

public override DbSqlQuery<TEntity> SqlQuery(string sql, params object[] parameters)
{
    return set.SqlQuery(sql, parameters);
}

public override ObservableCollection<TEntity> Local
{
    get { return set.Local; }
}

public override bool Equals(object obj)
{
    return set.Equals(obj);
}

public override int GetHashCode()
{
    return set.GetHashCode();
}

public override string ToString()
{
    return set.ToString();
}

Schließlich müssen wir das Interception-Objekt nutzen:

IEnumerator<TEntity> IEnumerable<TEntity>.GetEnumerator()
{
    return intercepted.GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
    return intercepted.GetEnumerator();
}

Type IQueryable.ElementType
{
    get { return intercepted.ElementType; }
}

Expression IQueryable.Expression
{
    get { return intercepted.Expression; }
}

IQueryProvider IQueryable.Provider
{
    get { return intercepted.Provider; }
}

IDbAsyncEnumerator<TEntity> IDbAsyncEnumerable<TEntity>.GetAsyncEnumerator()
{
    return ((IDbAsyncEnumerable<TEntity>)intercepted).GetAsyncEnumerator();
}

IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
{
    return ((IDbAsyncEnumerable<TEntity>)intercepted).GetAsyncEnumerator();
}

Und schließlich können wir einen gewöhnlichen DbContext :

public class MyContext : DbContext
{
    public DbSet<Entity> Entities { get; set; }

    public override DbSet<TEntity> Set<TEntity>()
    {
        return new DbSetProxy<TEntity>(base.Set<TEntity>());
    }
}

Wir müssen nur die Set Methode überschreiben, um unseren Proxy zu injizieren.

Hinweis : Falls sich jemand wundert, ist es leider notwendig, jedes DbSet Mitglied zu überschreiben, da DbSet nur für Test-Stubs DbSet wird. Wenn man also DbSet erbt, bricht das DbSet ...



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