I've written a method to recursively detaching my objects in entity framework. Actually it works, but the problem is, after execute this method, the one to many properties (the Collections of the object) are removed. Below my Code:
public void DetachRec(object objectToDetach)
{
DetachRec(objectToDetach, new HashSet<object>());
}
// context is my ObjectContext
private void DetachRec(object objectToDetach, HashSet<object> visitedObjects)
{
visitedObjects.Add(objectToDetach);
if (objectToDetach == null)
{
return;
}
Type type = objectToDetach.GetType();
PropertyInfo[] propertyInfos = type.GetProperties();
foreach (PropertyInfo propertyInfo in propertyInfos)
{
// on to many
if (propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>))
{
if (context.Entry(objectToDetach).Collection(propertyInfo.Name).IsLoaded)
{
var propValue = (IEnumerable) propertyInfo.GetValue(objectToDetach, null);
if (propValue != null)
{
var subcontext = new List<object>();
foreach (var subObject in propValue)
{
subcontext.Add(subObject);
}
foreach (var subObject in subcontext)
{
if (!visitedObjects.Contains(subObject))
{
context.DetachRecursive(subObject, visitedObjects);
}
}
}
}
}
// (many to one)
else if (propertyInfo.PropertyType.Assembly == type.Assembly)
{
//the part to detach many-to-one objects
}
}
context.Detach(objectToDetach);
}
Before detaching the collection elements, you could store the collection value and set the collection property to null
. This way you'll prevent EF from removing the collection elements when the elements and/or the parent entity is detached. At the end of the process, you will simply restore those values back.
Start by adding the following using
to the source code file:
using System.Data.Entity.Infrastructure;
Change the public method implementation as follows:
public void DetachRec(object objectToDetach)
{
var visitedObjects = new HashSet<object>();
var collectionsToRestore = new List<Tuple<DbCollectionEntry, object>>();
DetachRec(objectToDetach, visitedObjects, collectionsToRestore);
foreach (var item in collectionsToRestore)
item.Item1.CurrentValue = item.Item2;
}
the private recursive method signature to:
private void DetachRec(object objectToDetach, HashSet<object> visitedObjects, List<DbCollectionEntry, object>> collectionsToRestore)
and the one to many if
block body to:
var collectionEntry = context.Entry(objectToDetach).Collection(propertyInfo.Name);
if (collectionEntry.IsLoaded)
{
var collection = collectionEntry.CurrentValue;
if (collection != null)
{
collectionsToRestore.Add(Tuple.Create(collectionEntry, collection));
collectionEntry.CurrentValue = null;
foreach (var item in (IEnumerable)collection)
{
if (!visitedObjects.Contains(item))
{
DetachRec(item, visitedObjects, collectionsToRestore);
}
}
}
}
and you are done.