Search code examples
linqgarbage-collectiondisposelinqdatasource

Should I trust LinqDataSource to clean up properly?


Taking my first stab as using the OnSelecting method of LinqDataSource so that I can specify a more complex query, I wrote this:

protected void CategoriesDataSource_OnSelecting(object sender, LinqDataSourceSelectEventArgs e)
    {
        using (DataLayerDataContext db = new DataLayerDataContext())
        {
            e.Result = (from feed in db.Feeds
                        where feed.FeedName.StartsWith("Google")
                        select feed.MainCategory).Distinct();
        }
    }

The problem, of course, is that the using clause will Dispose the DataLayerDataContext. The 'solution' is to write it without it, but I'm afraid then that the context won't be disposed of in a timely fashion, that it will leave a bunch of connections open until garbage collection runs, and so on.

I'm no expert in this area, so any comments on whether this is a real problem, or am I worried for naught?


Solution

  • Ah, another person burned by the unfortunate side effects of deferred loading...

    protected void CategoriesDataSource_OnSelecting(object sender, LinqDataSourceSelectEventArgs e)
    {
        using (DataLayerDataContext db = new DataLayerDataContext())
        {
            e.Result = (from feed in db.Feeds
                        where feed.FeedName.StartsWith("Google")
                        select feed.MainCategory).Distinct().ToList();
            //                                              ^^^^^^^^^
        }
    }
    

    This will force the DataLayerDataContext to immediately run the query and create an in-memory list that's not dependent on the context or connection. That way you get your results immediately and you can dispose the context whenever you like.

    The only problem (per Steven's comment) is the lazy-loaded navigation properties; even if you force the query to evaluate, if you try to query any properties that are references to other LINQ objects (or lists of LINQ objects) they will fail to load unless you specify what properties are immediately required in the DataLoadOptions of the DataContext. See the below for an example:

    protected void CategoriesDataSource_OnSelecting(object sender, LinqDataSourceSelectEventArgs e)
    {
        using (DataLayerDataContext db = new DataLayerDataContext())
        {
            DataLoadOptions loadOptions = new DataLoadOptions();
            loadOptions.LoadWith<Category>(c => c.SomeReference);
            loadOptions.LoadWith<Category>(c => c.SomeOtherReferences);
    
            db.LoadOptions = loadOptions;
    
            e.Result = (from feed in db.Feeds
                        where feed.FeedName.StartsWith("Google")
                        select feed.MainCategory).Distinct().ToList();
        }
    }
    

    Once you manually specify these associations, LINQ to SQL will eagerly load them into memory when the query executes, instead of leaving them to deferred loading (which will cause an exception when the properties are used after the DataContext is disposed.)