0

I have the following exception when saving database context : The relationship could not be changed because one or more of the foreign-key properties is non-nullable.

As stated here, this is probably due to missing cascade delete. However, this isn't my code and I do not know which table(s) could contain orphans records. The error message doesn't say so.

Is there a way to retrieve those orphans records. (At least know in which table they are)

Then I will be able to pinpoint which part of the code I need to adjust.

Community
  • 1
  • 1
AXMIM
  • 2,424
  • 1
  • 20
  • 38
  • This question is similar and doesn't have an answer. http://stackoverflow.com/q/3037761/4625305 – AXMIM Sep 15 '15 at 19:33

2 Answers2

0

In the Entity Framework when you have relation many to many, and you are trying to delete from an object like parent.Children.Remove(child) this will only detach the child from the middle connection table. So you have to find the child and remove it from DbContext ChildrenToParent entity like so DbContext.ChildrenToParent.Remove(child). If you give a some code sample and/or database diagram I think I can explain it more precise.

Ivelin Ivanov
  • 148
  • 3
  • 14
  • The database doesn't seem to have Many to Many relationship. Only one to many. My question is more on how to find the faulting table then how to fix it, cause I'm pretty sure I will be able to fix the error once I know which table have the faulting data. – AXMIM Sep 15 '15 at 18:09
  • Found where it was manually. Now I know what you where trying to explains. Forget Many to Many, it does happen with one to many also. Also, a shorter answer would simply be that records must be removed from the context instead of removing it from the parentEntity owning it. – AXMIM Sep 16 '15 at 13:13
  • Yes, for other problems you can try to find Entity Profiler, it shows that queries is generated in the database or LinqPad also is a good tool for it. – Ivelin Ivanov Sep 16 '15 at 19:45
0

Can you try the following solution? The DeleteOrphans extension method must be called between DetectChanges and SaveChanges methods.

public static class DbContextExtensions
{
    private static readonly ConcurrentDictionary< EntityType, ReadOnlyDictionary< string, NavigationProperty>> s_navPropMappings = new ConcurrentDictionary< EntityType, ReadOnlyDictionary< string, NavigationProperty>>();

    public static void DeleteOrphans( this DbContext source )
    {
        var context = ((IObjectContextAdapter)source).ObjectContext;
        foreach (var entry in context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified))
        {
            var entityType = entry.EntitySet.ElementType as EntityType;
            if (entityType == null)
                continue;

            var navPropMap = s_navPropMappings.GetOrAdd(entityType, CreateNavigationPropertyMap);
            var props = entry.GetModifiedProperties().ToArray();
            foreach (var prop in props)
            {
                NavigationProperty navProp;
                if (!navPropMap.TryGetValue(prop, out navProp))
                    continue;

                var related = entry.RelationshipManager.GetRelatedEnd(navProp.RelationshipType.FullName, navProp.ToEndMember.Name);
                var enumerator = related.GetEnumerator();
                if (enumerator.MoveNext() && enumerator.Current != null)
                    continue;

                entry.Delete();
                break;
            }
        }
    }

    private static ReadOnlyDictionary<string, NavigationProperty> CreateNavigationPropertyMap( EntityType type )
    {
        var result = type.NavigationProperties
            .Where(v => v.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
            .Where(v => v.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One || (v.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne && v.FromEndMember.GetEntityType() == v.ToEndMember.GetEntityType()))
            .Select(v => new { NavigationProperty = v, DependentProperties = v.GetDependentProperties().Take(2).ToArray() })
            .Where(v => v.DependentProperties.Length == 1)
            .ToDictionary(v => v.DependentProperties[0].Name, v => v.NavigationProperty);

        return new ReadOnlyDictionary<string, NavigationProperty>(result);
    }
}
Sat
  • 1,240
  • 1
  • 10
  • 8