Questo è puramente un esperimento di apprendimento (yay scienza!). Questo non è pensato per essere effettivamente utilizzato ovunque. Voglio imparare come funzionano gli intercettori dell'albero dei comandi di EF6.
Sto cercando di modificare l'albero dei comandi intercettato per aggiungere un filtro "IsActive = 1" a tutte le query. Ho notato una grave carenza di documentazione su questo genere di cose. Due domande:
Come posso intercettare selettivamente gli alberi di comando per dire, entità che implementano un'interfaccia (come IHasAnActiveProperty
)? In questo momento, noto che l'intercettore intercetta le query per il contesto della cronologia, che non ha nulla a che fare con il mio MyEntity
.
Come posso aggiungere il mio filtro a tutte le domande?
public class MyEntity
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
}
public class MyDbContext : DbContext
{
public DbSet<MyEntity> Entities { get; set; }
public MyDbContext() : base("default") { }
public MyDbContext(string nameOrConnectionString)
: base(nameOrConnectionString) {}
}
public class MyConfiguration : DbConfiguration
{
public MyConfiguration()
{
AddInterceptor(new MyInterceptor());
}
}
public class MyInterceptor : IDbCommandTreeInterceptor
{
public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext)
{
var query = interceptionContext.Result as DbQueryCommandTree;
if (query != null)
{
Debug.WriteLine("##################");
Debug.WriteLine("DataSpace: {0}", interceptionContext.Result.DataSpace);
Debug.WriteLine(query.ToString());
query.Query.Accept(new MyVisitor());
}
}
}
Questo è dove si trova la carne della domanda. Come puoi vedere ho identificato alcune cose che devono essere incluse, ma non ho idea di come mettere insieme queste cose.
public class MyVisitor : BasicExpressionVisitor
{
public override void Visit(DbFilterExpression expression)
{
// add WHERE [IsActive] = 1
/* Building blocks:
* DbExpressionBuilder.Equal
* DbExpressionBuilder.True
* DbExpressionBuilder.And --- only when a filter expression already exists
*/
var isActiveProperty = expression.Property("IsActive");
var equalExp = DbExpressionBuilder.Equal(isActiveProperty, DbExpressionBuilder.True);
}
}
Supponiamo di avere questo:
class Program
{
static void Main(string[] args)
{
using (var ctx = new MyDbContext())
{
var entities = ctx.Entities.FirstOrDefault(x => x.Name == "amy");
Console.WriteLine(entities);
}
}
}
Questa query genera questo albero dei comandi:
DbQueryCommandTree
|_Parameters
|_Query : Collection{Record['Id'=Edm.Int32, 'Name'=Edm.String, 'IsActive'=Edm.Boolean]}
|_Project
|_Input : 'Limit1'
| |_Limit
| |_Filter
| | |_Input : 'Extent1'
| | | |_Scan : CodeFirstDatabase.MyEntity
| | |_Predicate
| | |_
| | |_'amy'
| | |_=
| | |_Var(Extent1).Name
| |_1
|_Projection
|_NewInstance : Record['Id'=Edm.Int32, 'Name'=Edm.String, 'IsActive'=Edm.Boolean]
|_Column : 'Id'
| |_Var(Limit1).Id
|_Column : 'Name'
| |_Var(Limit1).Name
|_Column : 'IsActive'
|_Var(Limit1).IsActive
Ma non capisco da dove andare.
Ho usato i seguenti riferimenti per aiutarmi a implementare un meccanismo di intercettazione "soft delete" personalizzato:
SCAN
che dovrebbe rispondere alla tua domanda) Spero che questo ti sia d'aiuto