namespace
Domain.Attributes
{
using
System;
using
System.Collections.Generic;
using
System.Data.Entity;
using
System.Linq;
using
System.Linq.Expressions;
/// <summary>
/// Apply the "on cascade delete" behaviour on the has many mapping should be applyed only on ICollection<> property that has one to many mapping.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public
class
OnDeleteCascadeAttribute : Attribute
{
public
static
void
ApplyAttributeBehaviour(Type dbContextType, DbModelBuilder modelBuilder)
{
Type thisType = dbContextType;
Type modelBuilderType =
typeof
(DbModelBuilder);
var genericEntityMethod = modelBuilderType.GetMethod(
"Entity"
);
var repositoryTypes =
thisType
.GetProperties()
.Where(x =>
x.PropertyType.IsGenericType &&
x.PropertyType.GetGenericTypeDefinition() ==
typeof
(DbSet<>))
.Select(x => x.PropertyType.GetGenericArguments()[0]);
foreach
(var repositoryType
in
repositoryTypes)
{
var repositoryProperties =
repositoryType
.GetProperties()
.Where(x =>
IsDerivedFrom(
typeof
(ICollection<>), x.PropertyType) &&
(x.GetCustomAttributes(
typeof
(OnDeleteCascadeAttribute),
true
).Count() > 0));
foreach
(var property
in
repositoryProperties)
{
Type collectionType = property.PropertyType.GetGenericArguments()[0];
var entityMethod = genericEntityMethod.MakeGenericMethod(repositoryType);
object
entity = entityMethod.Invoke(modelBuilder,
null
);
var entityHasManyMethod = entity.GetType().GetMethod(
"HasMany"
).MakeGenericMethod(collectionType);
ParameterExpression rParameter = Expression.Parameter(repositoryType,
"r"
);
Expression lambda = Expression.Lambda(
Expression.MakeMemberAccess(
rParameter,
property),
rParameter);
object
hasManyResult = entityHasManyMethod.Invoke(entity,
new
[] { lambda });
object
withOptionalResult = hasManyResult.GetType().GetMethod(
"WithOptional"
, Type.EmptyTypes).Invoke(hasManyResult,
null
);
withOptionalResult.GetType().GetMethod(
"WillCascadeOnDelete"
,
new
[] {
typeof
(
bool
) }).Invoke(withOptionalResult,
new
object
[] {
true
});
}
}
}
private
static
bool
IsDerivedFrom(Type baseGeneric, Type generic)
{
if
(!baseGeneric.IsGenericTypeDefinition)
throw
new
ArgumentException(
"baseGeneric must be a generic type definition"
);
if
(!generic.IsGenericType)
return
false
;
return
baseGeneric.IsAssignableFrom(generic.GetGenericTypeDefinition());
}
}
}