Ho una classe di entità che ha una proprietà DateTime
, LastModified
, che vorrei impostare il suo valore utilizzando la funzione Interceptor in Entity Framework 6.1
Ho creato un intercettore EntityFramework che compila il comando di inserimento e corregge il valore nell'istruzione INSERT.
Il mio codice pseudo sembra così
Semplice classe di entità con un ID generato e il campo LastModified
public class Item {
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id{get;set;}
public DateTime LastModified{get;set;}
}
L'intercettore (che viene aggiunto utilizzando una DbConfiguration, non coperto qui)
public class TestInterceptor : IDbCommandTreeInterceptor
{
void IDbCommandTreeInterceptor.TreeCreated(DbCommandTreeInterceptionContext interceptionContext)
{
if (interceptionContext.OriginalResult.DataSpace != DataSpace.SSpace)
return;
var insertCommand = interceptionContext.Result as DbInsertCommandTree;
if (insertCommand != null)
{
//get setClauses of insert command
var setClauses = insertCommand.SetClauses.ToList();
//this index is hardcoded to simplify the example
//that we change the value of one of the setClauses to a custom value.
var clause = setClauses[0];
clause = DbExpressionBuilder.SetClause(clause.Property(),DbExpression.FromDateTime(DateTime.UtcNow));
setClauses[0] = clause;
interceptionContext.Result = DbInsertCommandTree(
insertCommand.MetadataWorkspace,
insertCommand.DataSpace,
insertCommand.Target,
setClauses.AsReadOnly(),
insertCommand.Returning);
}
}
}
Il codice per creare un oggetto istanza
using(var ctx = new MyDbContext()){
var item = new Item();
ctx.Items.Add(item);
ctx.SaveChanges();
}
Il problema è che il database ha il valore corretto per la colonna LastModified, ma l'istanza item
non lo ha. Ha impostato correttamente l'ID. Immagino di dover modificare l' insertCommand.Returning
ma come?
AGGIORNAMENTO: Mi piace chiarire che sono consapevole del fatto che ci sono modi molto più semplici per farlo, ma lo scopo di questo post è utilizzare Interceptors, l'esempio sopra è ridotto per chiarezza. Il risultato finale utilizzerà gli attributi per contrassegnare le proprietà dell'entità che dovrebbero essere influenzate da questo.
Come spiegato qui http://marisks.net/2016/02/27/entity-framework-soft-delete-and-automatic-created-modified-dates/
public class LastChangeInterceptor : IDbCommandTreeInterceptor
{
public const string LastChangeColumnName = "LastChange";
public const string LastChangeByColumnName = "LastChangeBy";
public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext)
{
if (interceptionContext.OriginalResult.DataSpace != DataSpace.SSpace)
{
return;
}
var lastChange = DateTime.Now;
var lastChangeBy = HttpContext.Current.User.Identity.Name;
var insertCommand = interceptionContext.Result as DbInsertCommandTree;
var updateCommand = interceptionContext.OriginalResult as DbUpdateCommandTree;
if (insertCommand != null)
{
var setClauses = insertCommand.SetClauses
.Select(clause => clause.UpdateIfMatch(LastChangeColumnName, DbExpression.FromDateTime(lastChange)))
.Select(clause => clause.UpdateIfMatch(LastChangeByColumnName, DbExpression.FromString(lastChangeBy)))
.ToList();
interceptionContext.Result = new DbInsertCommandTree(insertCommand.MetadataWorkspace, insertCommand.DataSpace, insertCommand.Target, setClauses.AsReadOnly(), insertCommand.Returning);
}
else if (updateCommand != null)
{
var setClauses = updateCommand.SetClauses
.Select(clause => clause.UpdateIfMatch(LastChangeColumnName, DbExpression.FromDateTime(lastChange)))
.Select(clause => clause.UpdateIfMatch(LastChangeByColumnName, DbExpression.FromString(lastChangeBy)))
.ToList();
interceptionContext.Result = new DbUpdateCommandTree(updateCommand.MetadataWorkspace, updateCommand.DataSpace, updateCommand.Target, updateCommand.Predicate, setClauses.AsReadOnly(), null);
}
}
}
public static class Extensions
{
public static DbModificationClause UpdateIfMatch(this DbModificationClause clause, string property, DbExpression value)
{
var propertyExpression = (DbPropertyExpression)((DbSetClause)clause).Property;
if (propertyExpression.Property.Name == property)
{
return DbExpressionBuilder.SetClause(propertyExpression, value);
}
return clause;
}
}