Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Entity Framework CascadeDelete using Data Annotations
using System;
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class CascadeDeleteAttribute : Attribute { }
using System;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Linq;
using System.Reflection;
public class CascadeDeleteAttributeConvention : IConceptualModelConvention<AssociationType>
{
private static readonly Func<AssociationType, bool> IsSelfReferencing;
private static readonly Func<AssociationType, bool> IsRequiredToMany;
private static readonly Func<AssociationType, bool> IsManyToRequired;
private static readonly Func<AssociationType, object> GetConfiguration;
private static readonly Func<object, OperationAction?> NavigationPropertyConfigurationGetDeleteAction;
static CascadeDeleteAttributeConvention()
{
var associationTypeExtensionsType = typeof(AssociationType).Assembly.GetType("System.Data.Entity.ModelConfiguration.Edm.AssociationTypeExtensions");
var navigationPropertyConfigurationType = typeof(AssociationType).Assembly.GetType("System.Data.Entity.ModelConfiguration.Configuration.Properties.Navigation.NavigationPropertyConfiguration");
var isSelfRefencingMethod = associationTypeExtensionsType.GetMethod("IsSelfReferencing", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
IsSelfReferencing = associationType => (bool)isSelfRefencingMethod.Invoke(null, new object[] { associationType });
var isRequiredToManyMethod = associationTypeExtensionsType.GetMethod("IsRequiredToMany", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
IsRequiredToMany = associationType => (bool)isRequiredToManyMethod.Invoke(null, new object[] { associationType });
var isManyToRequiredMethod = associationTypeExtensionsType.GetMethod("IsManyToRequired", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
IsManyToRequired = associationType => (bool)isManyToRequiredMethod.Invoke(null, new object[] { associationType });
var getConfigurationMethod = associationTypeExtensionsType.GetMethod("GetConfiguration", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
GetConfiguration = associationType => getConfigurationMethod.Invoke(null, new object[] { associationType });
var deleteActionProperty = navigationPropertyConfigurationType.GetProperty("DeleteAction", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
NavigationPropertyConfigurationGetDeleteAction = navProperty => (OperationAction?)deleteActionProperty.GetValue(navProperty);
}
public virtual void Apply(AssociationType item, DbModel model)
{
if (IsSelfReferencing(item))
return;
var propertyConfiguration = GetConfiguration(item);
if (propertyConfiguration != null && NavigationPropertyConfigurationGetDeleteAction(propertyConfiguration).HasValue)
return;
AssociationEndMember collectionEndMember = null;
AssociationEndMember singleNavigationEndMember = null;
if (IsRequiredToMany(item))
{
collectionEndMember = GetSourceEnd(item);
singleNavigationEndMember = GetTargetEnd(item);
}
else if (IsManyToRequired(item))
{
collectionEndMember = GetTargetEnd(item);
singleNavigationEndMember = GetSourceEnd(item);
}
if (collectionEndMember == null || singleNavigationEndMember == null)
return;
var collectionCascadeDeleteAttribute = GetCascadeDeleteAttribute(collectionEndMember);
var singleCascadeDeleteAttribute = GetCascadeDeleteAttribute(singleNavigationEndMember);
if (collectionCascadeDeleteAttribute != null || singleCascadeDeleteAttribute != null)
collectionEndMember.DeleteBehavior = OperationAction.Cascade;
}
private static AssociationEndMember GetSourceEnd(AssociationType item)
{
return item.KeyMembers.FirstOrDefault() as AssociationEndMember;
}
private static AssociationEndMember GetTargetEnd(AssociationType item)
{
return item.KeyMembers.ElementAtOrDefault(1) as AssociationEndMember;
}
private static CascadeDeleteAttribute GetCascadeDeleteAttribute(EdmMember edmMember)
{
var clrProperties = edmMember.MetadataProperties.FirstOrDefault(m => m.Name == "ClrPropertyInfo");
if (clrProperties == null)
return null;
var property = clrProperties.Value as PropertyInfo;
if (property == null)
return null;
return property.GetCustomAttribute<CascadeDeleteAttribute>();
}
}
@jmichas

This comment has been minimized.

Copy link

jmichas commented Dec 13, 2015

Thanks for this, it is really great, makes life so much easier than having to redo all my annotations as fluent code.

@nmvuong92

This comment has been minimized.

Copy link

nmvuong92 commented Jan 25, 2017

Thanks a lot!

@lantirn

This comment has been minimized.

Copy link

lantirn commented Jan 8, 2018

Thanks for cascade wizard. Vital info for entityframework.very Easy <3

@philippfx

This comment has been minimized.

Copy link

philippfx commented Apr 4, 2018

How can this be applied to Entity Framework Core?

@jsancho

This comment has been minimized.

Copy link

jsancho commented Jul 25, 2018

a repeat of that last question. Is it feasible to adapt this logic to EF Core?
It seems as the new way is to rely on fluent mappings

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.