Created
February 27, 2016 06:46
-
-
Save marisks/3cc2e4f18048e2f0b7a5 to your computer and use it in GitHub Desktop.
Entity Framework soft delete, modiefied and created date interceptors
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Data.Entity.Core.Common.CommandTrees; | |
using System.Data.Entity.Core.Metadata.Edm; | |
using System.Data.Entity.Infrastructure.Interception; | |
using System.Linq; | |
public class CreatedAndModifiedDateInterceptor : IDbCommandTreeInterceptor | |
{ | |
public const string CreatedColumnName = "Created"; | |
public const string ModifiedColumnName = "Modified"; | |
public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext) | |
{ | |
if (interceptionContext.OriginalResult.DataSpace != DataSpace.SSpace) | |
{ | |
return; | |
} | |
var insertCommand = interceptionContext.Result as DbInsertCommandTree; | |
if (insertCommand != null) | |
{ | |
interceptionContext.Result = HandleInsertCommand(insertCommand); | |
} | |
var updateCommand = interceptionContext.OriginalResult as DbUpdateCommandTree; | |
if (updateCommand != null) | |
{ | |
interceptionContext.Result = HandleUpdateCommand(updateCommand); | |
} | |
} | |
private static DbCommandTree HandleInsertCommand(DbInsertCommandTree insertCommand) | |
{ | |
var now = DateTime.Now; | |
var setClauses = insertCommand.SetClauses | |
.Select(clause => clause.UpdateIfMatch(CreatedColumnName, DbExpression.FromDateTime(now))) | |
.Select(clause => clause.UpdateIfMatch(ModifiedColumnName, DbExpression.FromDateTime(now))) | |
.ToList(); | |
return new DbInsertCommandTree( | |
insertCommand.MetadataWorkspace, | |
insertCommand.DataSpace, | |
insertCommand.Target, | |
setClauses.AsReadOnly(), | |
insertCommand.Returning); | |
} | |
private static DbCommandTree HandleUpdateCommand(DbUpdateCommandTree updateCommand) | |
{ | |
var now = DateTime.Now; | |
var setClauses = updateCommand.SetClauses | |
.Select(clause => clause.UpdateIfMatch(ModifiedColumnName, DbExpression.FromDateTime(now))) | |
.ToList(); | |
return new DbUpdateCommandTree( | |
updateCommand.MetadataWorkspace, | |
updateCommand.DataSpace, | |
updateCommand.Target, | |
updateCommand.Predicate, | |
setClauses.AsReadOnly(), null); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Data.Entity; | |
public class EntityFrameworkConfiguration : DbConfiguration | |
{ | |
public EntityFrameworkConfiguration() | |
{ | |
AddInterceptor(new SoftDeleteInterceptor()); | |
AddInterceptor(new CreatedAndModifiedDateInterceptor()); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Data.Entity.Core.Common.CommandTrees; | |
using System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder; | |
public static class Extensions | |
{ | |
public static DbModificationClause UpdateIfMatch( | |
this DbModificationClause clause, | |
string property, | |
DbExpression value) | |
{ | |
return clause.IsFor(property) | |
? DbExpressionBuilder.SetClause(clause.Property(), value) | |
: clause; | |
} | |
public static bool IsFor( | |
this DbModificationClause clause, | |
string property) | |
{ | |
return clause.HasPropertyExpression() | |
&& clause.Property().Property.Name == property; | |
} | |
public static DbPropertyExpression Property( | |
this DbModificationClause clause) | |
{ | |
if (clause.HasPropertyExpression()) | |
{ | |
var setClause = (DbSetClause) clause; | |
return (DbPropertyExpression) setClause.Property; | |
} | |
var message = | |
"clause does not contain property expression. " + | |
"Use HasPropertyExpression method to check if it has property expression."; | |
throw new Exception(message); | |
} | |
public static bool HasPropertyExpression( | |
this DbModificationClause modificationClause) | |
{ | |
var setClause = modificationClause as DbSetClause; | |
return setClause?.Property is DbPropertyExpression; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Generic; | |
using System.Data.Entity.Core.Common.CommandTrees; | |
using System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder; | |
using System.Data.Entity.Core.Metadata.Edm; | |
using System.Data.Entity.Infrastructure.Interception; | |
using System.Linq; | |
public class SoftDeleteInterceptor : IDbCommandTreeInterceptor | |
{ | |
public const string IsDeletedColumnName = "IsDeleted"; | |
public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext) | |
{ | |
if (interceptionContext.OriginalResult.DataSpace != DataSpace.SSpace) | |
{ | |
return; | |
} | |
var queryCommand = interceptionContext.Result as DbQueryCommandTree; | |
if (queryCommand != null) | |
{ | |
interceptionContext.Result = HandleQueryCommand(queryCommand); | |
} | |
var deleteCommand = interceptionContext.OriginalResult as DbDeleteCommandTree; | |
if (deleteCommand != null) | |
{ | |
interceptionContext.Result = HandleDeleteCommand(deleteCommand); | |
} | |
} | |
private static DbCommandTree HandleQueryCommand(DbQueryCommandTree queryCommand) | |
{ | |
var newQuery = queryCommand.Query.Accept(new SoftDeleteQueryVisitor()); | |
return new DbQueryCommandTree( | |
queryCommand.MetadataWorkspace, | |
queryCommand.DataSpace, | |
newQuery); | |
} | |
private static DbCommandTree HandleDeleteCommand(DbDeleteCommandTree deleteCommand) | |
{ | |
var setClauses = new List<DbModificationClause>(); | |
var table = (EntityType) deleteCommand.Target.VariableType.EdmType; | |
if (table.Properties.All(p => p.Name != IsDeletedColumnName)) | |
{ | |
return deleteCommand; | |
} | |
setClauses.Add(DbExpressionBuilder.SetClause( | |
deleteCommand.Target.VariableType.Variable(deleteCommand.Target.VariableName).Property(IsDeletedColumnName), | |
DbExpression.FromBoolean(true))); | |
return new DbUpdateCommandTree( | |
deleteCommand.MetadataWorkspace, | |
deleteCommand.DataSpace, | |
deleteCommand.Target, | |
deleteCommand.Predicate, | |
setClauses.AsReadOnly(), null); | |
} | |
public class SoftDeleteQueryVisitor : DefaultExpressionVisitor | |
{ | |
public override DbExpression Visit(DbScanExpression expression) | |
{ | |
var table = (EntityType)expression.Target.ElementType; | |
if (table.Properties.All(p => p.Name != IsDeletedColumnName)) | |
{ | |
return base.Visit(expression); | |
} | |
var binding = expression.Bind(); | |
return binding.Filter( | |
binding.VariableType | |
.Variable(binding.VariableName) | |
.Property(IsDeletedColumnName) | |
.NotEqual(DbExpression.FromBoolean(true))); | |
} | |
} | |
} |
This implementation is great, the only question I have is how would I check the where clause in the SoftDeleteQueryVisitor to see if I am trying to get records where IsDeleted = 1. The implementation works great to delete them, but you can no longer show those deleted items in a history view.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is so easy to implement and it handles the functionality in a very elegant and simple way. Thanks.