开发者

Working with Anonymous Types After Closing DB Connection

开发者 https://www.devze.com 2023-03-02 16:29 出处:网络
I have code similar to the following. using (MyEntities context = new MyEntities()) { var activities = from act in context.Activities

I have code similar to the following.

using (MyEntities context = new MyEntities())
{
    var activities = from act in context.Activities
                     where act.ActTwittered == false
                     select new { act.ActID, act.ActTitle, act.Category, act.ActDateTime, act.Location };

    foreach (var activity in activities)
    {
        /* ... */
    }
}

This seems to work fine but my loop has a lot of processing. I'm concerned that I'm leaving a database connection or other resources open during this processing.

I tried declaring var activities before the using statement so I could process the data after the using statement, but this variable must be initialized where it is declared.

Could someone who understands the inner workings of EF tell me if开发者_运维知识库 there are problems doing lengthy processing while an EF context is "alive", and how I make alleviate these problems.

While I'm at it, perhaps you could also comment on the fact that I use act.Category.CatName within the loop. This is a value from a related table. Am I better to use a join in my EF query so I get the data all at once rather than forcing another (?) database access to get the related data?


You can have the compiler infer the result type by calling a generic method on it:

    public static T CallFunc<T>( Func<T> theFunc )
    {
        return theFunc();
    }

    ... 

    var activities = CallFunc( () =>
    {
        using( var context = new MyEntities() )
        {
            return 
                (
                    from act in context.Activities
                    where act.ActTwittered == false
                    select new { act.ActID, act.ActTitle, act.Category, act.ActDateTime, act.Location };
                )
                .ToList();
        }
    } );

    foreach( var a in activities ) ...

Don't forget the .ToList() at the end, or your query won't be actually executed until you enumerate over it, which will happen after the context is closed.


Your problem is in wanting the anonymous type to be accessible outside of its context. You can use a specific type for your results and get the flexibility to access the values anywhere.

public class ActivitySummary
{
    public int ActID { get; set; }
    public string ActTitle { get; set; }
    public string Category { get; set; }
    public DateTime ActDateTime { get; set; }
    public string Location { get; set; }
}

. . .

List<ActivitySummary> activities;

using (MyEntities context = new MyEntities())
{
    activities = from act in context.Activities
                 where act.ActTwittered == false
                 select new ActivitySummary { act.ActID, act.ActTitle, act.Category, act.ActDateTime, act.Location }.ToList();

}

foreach (var activity in activities)
{
    /* ... */
}


The biggest problem with separating the reading and processing, is that your code doesn't do that at all.

The first statement only creates an expression that is capable of fetching the data, but it doesn't actually fetch anything at all. It's not until you start reading from it that it actually gets any data.

There are two things that you have to do to be able to close the database connection before processing the data with that code. You have to actually get the data instead of just setting up an expression, by using the ToList method, and you have to use Dispose instead of using so that you don't isolate the anonymous type inside it's scope:

MyEntities context = new MyEntities();

var activities = (
  from act in context.Activities
  where act.ActTwittered == false
  select new { act.ActID, act.ActTitle, act.Category, act.ActDateTime, act.Location }
).ToList();

context.Dispose();

foreach (var activity in activities) {
    /* ... */
}

One drawback with this approach is that you don't get the implicit try...finally around the code that the using provides, so if there is an error when fetching the data, the context won't be disposed.

Another alternative is to declare a class that can hold the data that you read, so that you can declare the variable using a known class, outside the using block:

List<Activity> activities;
using (MyEntities context = new MyEntities()) {
  activities = (
    from act in context.Activities
    where act.ActTwittered == false
    select new Activity(act.ActID, act.ActTitle, act.Category, act.ActDateTime, act.Location)
  ).ToList();
}


In cases when I need to work with result from DB for a long time I usually create that specific type, just declare a class with needed fields, not using anonymous types, after I get data from database, I spawn a thread where I do the needed work. This way the connection to database gets closed and application remains responsive.


You could manually dispose your DataContext.

In addition, you will need to call ToArray on the query so that it gets executed immidiately (and not after disposing the context):

MyEntities context = new MyEntities();

var activities = (from act in context.Activities
                  where act.ActTwittered == false
                  select new { act.ActID, act.ActTitle, act.Category, act.ActDateTime, act.Location })
                 .ToArray();

context.Dispose();

//Do something with activities

However, I would recommend switching to non-anonymous types so you can continue using using to dispose the Context.
For example, using takes care to call Dispose even if an exception occurs.

0

精彩评论

暂无评论...
验证码 换一张
取 消