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 }); } }