Search code examples
c#linq-to-sqlusingdatacontexteager-loading

Using statement return reference type


I'm using Linq to SQL and for optimization I want to set DataContext.ObjectTrackingEnabled to false for queries where I don't need to change or add objects. In my application I have a DataContext that's used all over, so in order for me to set ObjectTrackingEnabled to false I need to create a new DataContext. So I want to do something like this:

    public static MyObj GetMyObj(long id)
    {
        using (MyDataContext dc = new MyDataDataContext("connectionstring"))
        {
            dc.ObjectTrackingEnabled = false;
            return dc.MyObjs.FirstOrDefault(x => x.ID == id);
        }
    }

But it seems the MyObj that I'm returning gets "half disposed" because when I try to access it's members they all turn out to be null. Like so:

    var myObj = GetMyObj(10);
    var test = myObj.MyMember.Count(); // MyMember is now null, but shouldn't be

However, if I don't do the using, then I get the correct Count():

    public static MyObj GetMyObj(long id)
    {
        var dc = DCManager.Get("myDataContext") // gets the normal DC that's used throughout the application
        return dc.MyObjs.FirstOrDefault(x => x.ID == id);
    }

    var myObj = GetMyObj(10);
    var test = myObj.MyMember.Count(); // MyMember is now not null and its Count() is not 0

Is there a way that I can use ObjectTrackingEnabled = false without losing data on my return value or can I only use this "pattern" when returning value types? I guess I need to force eager loading on the member objects I want to use after the return, so I tried adding DataLoadOptions.LoadWith but I'm still not getting the underlying objects:

    public static MyObj GetMyObj(long id)
    {
        using (MyDataContext dc = new MyDataDataContext("connectionstring"))
        {
            dc.ObjectTrackingEnabled = false;
            DataLoadOptions load = new DataLoadOptions();
            load.LoadWith<MyObj>(d => d.MyMember);
            return dc.MyObjs.FirstOrDefault(x => x.ID == id);
        }
    }

    var myObj = GetMyObj(10);
    var test = myObj.MyMember.Count(); // MyMember is null which it shouldn't be

Solution

  • You've got very close with your latest updates. You're right that this is about lazy/eager loading. But you've failed to tell that specific context object to use the options you've configured:

    public static MyObj GetMyObj(long id)
    {
        using (MyDataContext dc = new MyDataDataContext("connectionstring"))
        {
            dc.ObjectTrackingEnabled = false;
            DataLoadOptions load = new DataLoadOptions();
            load.LoadWith<MyObj>(d => d.MyMember);
    
            dc.LoadOptions = load; //<-- New line
    
            return dc.MyObjs.FirstOrDefault(x => x.ID == id);
        }
    }
    
    var myObj = GetMyObj(10);
    var test = myObj.MyMember.Count(); // MyMember is null which it shouldn't be
    

    (As a preference, I'd also rename load as options because I think that better describes what that object is)