Detaching an Entity in LINQ to SQL
LINQ to SQL, shamelesly, does not have a Detach method, like most other O/RMs. In one particular case, I needed one - or, at least, I thought I did - so I went to write one, which wouldn't require me to use a base class. Thanks to Reflector, here's what I came up with (only tested it in version 1, not 4):
public static void DisableAllEventHandlers(Object control)
{
EventDescriptorCollection ec = TypeDescriptor.GetEvents(control);
for (Int32 i = 0; i < ec.Count; ++i)
{
DisableEventHandlers(control, ec[i].Name);
}
}
private static FieldInfo FindField(Object obj, String fieldName)
{
FieldInfo fi = null;
Type type = obj.GetType();
while (type != null)
{
fi = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
if (fi != null)
{
break;
}
else
{
type = type.BaseType;
}
}
return (fi);
}
private static Object FindProperty(Object obj, String propertyName)
{
Type type = obj.GetType();
PropertyInfo pi = null;
Object value = null;
while (type != null)
{
pi = type.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance);
if (pi != null)
{
value = pi.GetValue(obj, null);
break;
}
else
{
type = type.BaseType;
}
}
return (value);
}
public static EventHandler DisableEventHandlers(Object control, String eventName)
{
FieldInfo eventFieldInfo = FindField(control, String.Concat("Event", eventName));
EventHandler oldEventHandler = null;
EventHandlerList eventsHandlerList = null;
Object eventKeyObject = null;
if (eventFieldInfo != null)
{
//standard process for .NET controls
eventKeyObject = eventFieldInfo.GetValue(control);
eventsHandlerList = FindProperty(control, "Events") as EventHandlerList;
if ((eventKeyObject != null) && (eventsHandlerList != null))
{
oldEventHandler = eventsHandlerList[eventKeyObject] as EventHandler;
eventsHandlerList[eventKeyObject] = null;
}
}
else
{
//alternative process, for events other than the standard .NET ones
eventFieldInfo = FindField(control, eventName);
if (eventFieldInfo != null)
{
oldEventHandler = eventFieldInfo.GetValue(control) as EventHandler;
eventFieldInfo.SetValue(control, null);
}
}
return (oldEventHandler);
}
public Boolean IsAttached<TEntity>(TEntity entity)
{
Object services = typeof(DataContext).GetProperty("Services", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetProperty).GetValue(this, null);
Object changeTracker = services.GetType().GetProperty("ChangeTracker", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetProperty).GetValue(services, null);
Boolean isTracked = (Boolean)changeTracker.GetType().BaseType.GetMethod("IsTracked", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(changeTracker, new Object[] { entity });
return (isTracked);
}
public void Detach<TEntity>(TEntity entity)
{
DisableAllEventHandlers(entity);
Object services = typeof(DataContext).GetProperty("Services", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetProperty).GetValue(this, null);
Object changeTracker = services.GetType().GetProperty("ChangeTracker", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetProperty).GetValue(services, null);
Object identityManager = services.GetType().GetField("identifier", BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic).GetValue(services);
Object caches = identityManager.GetType().GetField("caches", BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic).GetValue(identityManager);
Object items = changeTracker.GetType().GetField("items", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(changeTracker);
MetaType type = this.Mapping.GetMetaType(entity.GetType());
Object existingEntity = identityManager.GetType().GetMethod("FindLike", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(identityManager, new Object[] { type, entity });
Object trackedObject = changeTracker.GetType().GetMethod("GetTrackedObject", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(changeTracker, new Object[] { entity });
Boolean result = (Boolean) identityManager.GetType().GetMethod("RemoveLike", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(identityManager, new Object[] { type , entity });
Boolean isTracked = (Boolean)changeTracker.GetType().BaseType.GetMethod("IsTracked", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(changeTracker, new Object[] { entity });
if (isTracked == true)
{
changeTracker.GetType().GetMethod("StopTracking", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(changeTracker, new Object[] { entity });
}
}