C# – Access LINQ-2-SQL DataContext in entity class

clinqlinq-to-sqlnet

Is there any simple way to access the DataContext in a linq2sql entity class.

I'm trying to create something like EntitySet but I cannot figure out how the EntitySet has access to the context that created the entity object in the first place.

I want to have a regular linq2sql entity class with a way for the class to access the DataContext that created it. I know it's possible because when you have an entity class with a primary key linq2sql gives you the option to load all children without creating a new DataContext.

Best Answer

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!