Skip to content

Instantly share code, notes, and snippets.

@marisks
Created February 27, 2016 06:46
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save marisks/3cc2e4f18048e2f0b7a5 to your computer and use it in GitHub Desktop.
Save marisks/3cc2e4f18048e2f0b7a5 to your computer and use it in GitHub Desktop.
Entity Framework soft delete, modiefied and created date interceptors
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);
}
}
using System.Data.Entity;
public class EntityFrameworkConfiguration : DbConfiguration
{
public EntityFrameworkConfiguration()
{
AddInterceptor(new SoftDeleteInterceptor());
AddInterceptor(new CreatedAndModifiedDateInterceptor());
}
}
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;
}
}
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)));
}
}
}
@mtozlu
Copy link

mtozlu commented Jul 7, 2016

This is so easy to implement and it handles the functionality in a very elegant and simple way. Thanks.

@jtolar
Copy link

jtolar commented Feb 15, 2017

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