I've just had to do exactly the same thing. Here's my solution (albeit probably not the best approach, but is at least quite elegant):
Firstly, Create an interface for all your entities to implement that inherits from INotifyPropertyChanging. This is used to hook up some extension methods and keep our implementation nice seperate. In my case the interface is called ISandboxObject:
public interface ISandboxObject : INotifyPropertyChanging
{
// This is just a marker interface for Extension Methods
}
Then Create a new static class to contain an extension method to obtain the DataContext. This is achieved by looking for an event handler on the LINQ Change Tracker attached to the INotifyPropertyChanging.PropertyChanging event. Once we've found the change tracker, we can obtain the DataContext from there:
/// <summary>
/// Obtain the DataContext providing this entity
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static DataContext GetContext(this ISandboxObject obj)
{
FieldInfo fEvent = obj.GetType().GetField("PropertyChanging", BindingFlags.NonPublic | BindingFlags.Instance);
MulticastDelegate dEvent = (MulticastDelegate)fEvent.GetValue(obj);
Delegate[] onChangingHandlers = dEvent.GetInvocationList();
// Obtain the ChangeTracker
foreach (Delegate handler in onChangingHandlers)
{
if (handler.Target.GetType().Name == "StandardChangeTracker")
{
// Obtain the 'services' private field of the 'tracker'
object tracker = handler.Target;
object services = tracker.GetType().GetField("services", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(tracker);
// Get the Context
DataContext context = services.GetType().GetProperty("Context").GetValue(services, null) as DataContext;
return context;
}
}
// Not found
throw new Exception("Error reflecting object");
}
Now you have a nice extension method that will provide you with a DataContext from any object implementing ISandboxObject. Please do put some more error checking in this before using it in anger!