Intercept all EF6 Linq queries

entity-framework-6 expression-trees interceptor linq

Question

Every time a Linq query is conducted on a DbContext, I want to call a function that modifies the expression tree before execution. The IDbCommandTreeInterceptor interface has caught my attention, however it doesn't seem to provide an expression tree (which I suppose is understandable since it may not have been a Linq query by the time it gets to this point).

Is there a way for me to stop all expressions before they run and alter them?

Note: Since I have created a framework for altering Linq trees—originally for Linq to SQL—this must be Linq tree modification.

1
8
8/26/2014 3:35:51 PM

Accepted Answer

The answer is still to build a proxy for the LINQ provider to intercept each execution of a LINQ expression, as proposed in the comments. In actuality, I'm experimenting with inside of this project, which specifically supports EF6 as well as EF6 async queries. A typical.NET application may be made.ExpressionVisitor to intercept a pass:

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

The problematic thing, though, is that the question also specifies that it "must run on every performed Linq query on a DbContext." One strategy is to isolate some aspect ofDbContext / DbSet as a result, your code doesn't accessDbSet objects. And the possibility of interception exists inside the implementation of this abstraction...

Another strategy would be a proxy for, which, in my opinion, better addresses this issue.DbSet , which permits eavesdropping by using the LINQ proxy for queries. Prior to it, we must descend fromDbSet :

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

Then, in order to invoke the real member, all the members must be overwritten.DbSet for unrelated matters:

Unfortunately, it is required to rewrite everyDbSet as a result of inheritingDbSet is only intended for test stubs. So, just inheritingDbSet destroys theDbSet .)

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

Last but not least, we must use the interception object:

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

Finally, we may utilize a typicalDbContext We merely need to replace itsSet technique for injecting our proxy:

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

    public override DbSet<TEntity> Set<TEntity>()
    {
        return new DbSetProxy<TEntity>(base.Set<TEntity>());
    }
}
8
2/27/2020 6:42:34 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow