views:

135

answers:

3

I am getting a Cannot access object: DataContext after it's been disposed in the below DAL method. I thought that I would be okay calling dispose there. result is an IEnumurable and I thought it was IQueryable that caused these kinds of problems.

What am I doing wrong? How SHOULD I be disposing of my DataContext. Is there something better to be returning then a DataTable? This is a Desktop app that points at SQL 2005.

Example method that causes this error -->


    public static DataTable GetEnrolledMembers(Guid workerID)
    {
        var DB = CmoDataContext.Create();

        var AllEnrollees = from enrollment in DB.tblCMOEnrollments
                           where enrollment.CMOSocialWorkerID == workerID || enrollment.CMONurseID == workerID
                           join supportWorker in DB.tblSupportWorkers on enrollment.EconomicSupportWorkerID
                               equals supportWorker.SupportWorkerID into workerGroup
                           from worker in workerGroup.DefaultIfEmpty()
                           select
                               new
                               {
                                   enrollment.ClientID,
                                   enrollment.CMONurseID,
                                   enrollment.CMOSocialWorkerID,
                                   enrollment.EnrollmentDate,
                                   enrollment.DisenrollmentDate,
                                   ESFirstName = worker.FirstName,
                                   ESLastName = worker.LastName,
                                   ESPhone = worker.Phone
                               };

        var result = from enrollee in AllEnrollees.AsEnumerable()
                     where (enrollee.DisenrollmentDate == null || enrollee.DisenrollmentDate > DateTime.Now)
                     //let memberName = BLLConnect.MemberName(enrollee.ClientID)
                     let lastName = BLLConnect.MemberLastName(enrollee.ClientID)
                     let firstName = BLLConnect.MemberFirstName(enrollee.ClientID)
                     orderby enrollee.DisenrollmentDate ascending, lastName ascending
                     select
                         new
                         {
                             enrollee.ClientID,
                             //MemberName = memberName,
                             LastName = lastName,
                             FirstName = firstName,
                             NurseName = BLLAspnetdb.NurseName(enrollee.CMONurseID),
                             SocialWorkerName = BLLAspnetdb.SocialWorkerName(enrollee.CMOSocialWorkerID),
                             enrollee.EnrollmentDate,
                             enrollee.DisenrollmentDate,
                             ESWorkerName = enrollee.ESFirstName + " " + enrollee.ESLastName,
                             enrollee.ESPhone
                         };
        DB.Dispose();

        return result.CopyLinqToDataTable();
    }

partial class where I create the DataContext -->


partial class CmoDataContext
{
    public static bool IsDisconnectedUser
    {
        get
        {
            return Settings.Default.IsDisconnectedUser;
        }
    }

    public static CmoDataContext Create()
    {
        var cs = IsDisconnectedUser ? Settings.Default.CMOConnectionString : Settings.Default.Central_CMOConnectionString;
        return       new CmoDataContext(cs);
    }
+4  A: 

You're calling CopyLinqToDataTable after disposing the DataContext. Since LINQ uses deferred execution, it only executes the query when result is enumerated, after the DataContext is disposed.

You should put your DataContext in a using block that contains the return statement.
This will dispose the DataContext after calling CopyLinqToDataTable, avoiding the problem.

For example:

using(var DB = CmoDataContext.Create()) {
    //Execute queries
    return result.CopyLinqToDataTable();
}

The using statement generates a finally block that disposes the DataContext at the end of the using block. (Even if an exception is thrown)

SLaks
as long as the return is in the `using` I will be okay?? Thanks
Refracted Paladin
Yes; you'll be fine. (Unless you return an `IEnumerable`)
SLaks
Okay, out of curiosity, `IEnumerable` will still fail here? How do you return it then?
Refracted Paladin
By calling `ToArray`. Because (again) of deferred execution, if you return an `IEnumerable`, the query won't run until the client (that uses your return value) enumerates it. By calling `ToArray` and returning that (perhaps wrapped in a `ReadOnlyCollection`), you force the enumerable to be enumerated immediately.
SLaks
+3  A: 

You should use something like ToList in queries before calling Dispose

Nagg
Can `List` bind to `DataGrids` and the like as well as `DataTable`? If so I will look into it. Is the term I am looking for "concrete" as far as my return type?
Refracted Paladin
+1  A: 

Can't you just do this instead?

...
DataTable dt = result.CopyLinqToDataTable(); 
DB.Dispose(); 
return dt;
Mike Mooney