You've actually got a potentially premature disposal, rather than a late one, as entities being involved in the closure assigned to the datasource means it leaves the scope.
You've then got an entities out in the wild that won't be disposed in its real scope, which is what the analysis is complaining about, but that's unlikely to be a problem compared with the fact that it is disposed before it is last used.
Options:
Leave it as it is. If the above works then it is probably because disposal doesn't affect the necessary state for the closure to work. This is dicey. Betting on "probably" isn't a good idea, and may also change down the road. (I can think of one case where using an object after disposal makes sense, but it's obscure and not what you have here anyway).
Force the query to execute eagerly. Calling ToList()
or ToArray()
on the query will run it and create an in-memory result that is then used as the datasource. At best though this will be less efficient in both space and time. At worse it could be cripplingly so (depending on the size of results you are dealing with).
Make sure the control completes using its datasource before the scope is left. Then clear the datasource. Depending on the control in question and some other matters (in particular, if it has an explicit DataBind()
method) it can be trivial, impossible or somewhere in between to do this.
Put entity into an instance variable. Implement IDisposable
. In your Dispose()
method, call it's Dispose()
method. Do not add a finaliser for this, as you are only disposing a managed object.
Create an enumerable method that wraps the query (and the using) and then does a yield return
on each item the query returns. Use this as the datasource.
5 seems the best bet for most cases. It has the advantage of not changing the code much while not adding the (potentially large, depending on data) overhead of number 2. Note that just calling AsEnumerable
(which has almost the same effect on execution order) would not have the same effect, as the closure would still be leaving the block unexecuted.
Edit: An enumerable that wraps the query would be like:
private IEnumerable GetSessions()
{
using (var entities = new DbEntities(Properties.Settings.Default.UserConnectionString))
{
entities.CommandTimeout = 7200;
var sessions = from t in entities.TableName
where t.UserSession.Id == _id && t.Parent == 0
group t by new { t.UserSession, t.UserSession.SessionId } into sessionGroup
select new
{
Id = sessionGroup.Key.UserSession,
Session = sessionGroup.Key.SessionId
};
foreach(var sess in sessions.Where(x => x.Time > 0.00))
yield return sess;
}
}
Then you would set change SessionSummary_Load to:
private void SessionSummary_Load(object sender, EventArgs e)
{
summaryDataGridView.DataSource = GetSessions();
summaryDataGridView.Columns[4].DefaultCellStyle.Format = "N2";
summaryDataGridView.Columns[4].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
}
}
Hopefully this will solve the problem, because entities
never leaves the scope of the using
.