views:

1872

answers:

5

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.

+2  A: 

Basically, no.

The EntitySet<T> class has an internal Source property that is assigned by the data-context, which is how it gets at the data on-demand. However, there is nothing similar for the data classes themselves.

However, I believe that Entity Framework has a lot more access to this, at the cost of the enforced object hierarchy.

Unlike Entity Framework, LINQ-to-SQL (by design) can be used with regular, persistence-ignorant classes - so it doesn't assume that it has access to this type of data.

Marc Gravell
A: 

An Entity class should not be aware of the data context as its just a mapping of the table but the data context has the knowledge of all the entities and the connection properties

You are able to link to the child table through the parent entity class because of the entity relationship and not through the data context

The data context class will be used at the end where the entities are consumed, I don't see a need for the entities to be aware of the context,

If you can tell the specific scenario we can try another approach.

Rony
i have the same issue and though I understand your argument for this (believe me, i am in favor of it), I have a real-world need for this.In some of our entity partial classes, we added some properties that must query the database based on a property of their entity, to grab some collection of other entities, that are not directly associated in the SQL Server. This is really ugly because we are creating a new data context when calling these properties (some of these in a loop).
Thiago Silva
A: 

I know exactly what you mean. We're supposed to do our calculations/validation within the entity's partial class, but if the entity doesn't have access to the datacontext then how much can we do? For instance, in my SalesOrder object, whenever the "Ship To" address gets changed, the SalesOrder needs to query the database to find out if a tax applies to that state/zip. I've been fighting this for a while, but today I broke down and went with the ugly method, but so far so good. Essentially all I do is create a "Context" property in my partial class and set it with the datacontext whenever an entity is created.

Partial Class SalesOrder
    Private moContext As L2S_SalesOrdersDataContext

    Friend Property Context() As L2S_SalesOrdersDataContext
        Get
            Return moContext
        End Get
        Set(ByVal value As L2S_SalesOrdersDataContext)
            moContext = value
        End Set
    End Property
...

YMMV, especially if you're detaching your entities.

Monty
A: 

Basically, you can do this with a bit of hack. DataCOntext attaches a StandardChangeTracker to your entity:

            DataContext context = null;
            object changeTracker = (from i in o1.GetInvocationList() where i.Target.GetType().FullName == "System.Data.Linq.ChangeTracker+StandardChangeTracker" select i.Target).FirstOrDefault();
            if (changeTracker != null) // DataCOntext tracks our changes through StandardChangeTracker
            {
                object services = Reflector.GetFieldValue(changeTracker, "services");
                context = (DataContext)Reflector.GetFieldValue(services, "context");
            }

Where Reflector.GetFieldValue equals to

        public static object GetFieldValue(object instance, string propertyName)
        {
            return instance.GetType().GetField(propertyName, BindingFlags.Instance | BindingFlags.NonPublic).GetValue(instance);
        }
matra
+1  A: 

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!

Dan