简体   繁体   中英

Proper use of “Using” statement for datacontext

I'm using Linq to Entities and lately, I found that a lot of folks recommending wrapping the datacontext in a using statement like this:

Using(DataContext db = new DataContext) {
    var xx = db.customers;
}

This makes sense. However, I'm not sure how to incorporate this practice in my model. For example: I have an interface (let's call it customer) and it is implemented by a repository like this:

namespace Models
{
    public class rCustomer : iCustomer
    {

        readonly DataContext db = new DataContext();

        public customer getCustomer(Guid id)
        {
            return db.customers.SingleOrDefault(por => por.id == id);
        }

        public iQueryable<customer> getTopCustomers()
        {
            return db.customers.Take(10);
        }

        //*******************************************
        //more methods using db, including add, update, delete, etc.
        //*******************************************

    }
}

Then, to take the advantage of using, I will need to change the methods to look like this:

namespace Models
{
    public class rCustomer : iCustomer
    {
        public customer getCustomer(Guid id)
        {
            using(DataContext db = new DataContext()) {       
                return db.customers.SingleOrDefault(por => por.id == id);
            } 
        }

        public iQueryable<customer> getTopCustomers()
        {
            using(DataContext db = new DataContext()) {       
                 return db.customers.Take(10);
            } 
        }

        //*******************************************
        //more methods using db
        //*******************************************

    }
}

My question is: the recommendation of using “Using” is really that good? Please take in consideration that this change will be a major one, I have about 25 interfaces/repository combos, and each has about 20-25 methods, not to mention the need to re-test everything after finish. Is there other way?

Thanks!

Edgar.

You can implement a Database factory which will cause your DbContext is being reused .

You can achieve this as follows:

DatabaseFactory class:

public class DatabaseFactory : Disposable, IDatabaseFactory
{
    private YourEntities _dataContext;
    public YourEntities Get()
    {
        return _dataContext ?? (_dataContext = new YourEntities());
    }
    protected override void DisposeCore()
    {
        if (_dataContext != null)
            _dataContext.Dispose();
    }
}

Excerpt of the Repository base class:

 public abstract class Repository<T> : IRepository<T> where T : class
{
    private YourEntities _dataContext;
    private readonly IDbSet<T> _dbset;
    protected Repository(IDatabaseFactory databaseFactory)
    {
        DatabaseFactory = databaseFactory;
        _dbset = DataContext.Set<T>();
    }

    protected IDatabaseFactory DatabaseFactory
    {
        get;
        private set;
    }

    protected YourEntities DataContext
    {
        get { return _dataContext ?? (_dataContext = DatabaseFactory.Get()); }
    }

Your table's repository class:

public class ApplicationRepository : Repository<YourTable>, IYourTableRepository
{
    private YourEntities _dataContext;

    protected new IDatabaseFactory DatabaseFactory
    {
        get;
        private set;
    }

    public YourTableRepository(IDatabaseFactory databaseFactory)
        : base(databaseFactory)
    {
        DatabaseFactory = databaseFactory;
    }

    protected new YourEntities DataContext
    {
        get { return _dataContext ?? (_dataContext = DatabaseFactory.Get()); }
    }

   }
    public interface IYourTableRepository : IRepository<YourTable>
   {
   }
}

This works perfectly together with AutoFac constructor injection as well.

Considering the code provided I see, you esplicitly use readonly DataContext db = new DataContext(); like a global variable, so you consider to have that object lifetime along with your rCustomer class instance lifetime.

If this is true, what you can do, instead of rewriting everything, you can implement IDisposable and inside Dispose() code something like

private void Dispose()
{
    if(db  != null) 
        db.Dispose();
}

Hope this helps.

As others have mentioned, it's important for the data contexts to be disposed. I won't go into that further.

I see three possible designs for the class that ensure that the contexts are disposed:

  1. The second solution you provide in which you create a data context within the scope of each method of rCustomer that needs it so that each datacontext is in a using block.
  2. Keep the data context as an instance variable and have rCustomer implement IDisposable so that when rCustomer is disposed you can dispose of it's data context. This means that all rCustomer instances will need to be wrapped in using blocks.
  3. Pass an instance of an existing data context into rCustomer through its constructor. If you do this then rCustomer won't be responsible for disposing of it, the user of the class will. This would allow you to use a single data context across several instances of rCustomer , or with several different classes that need access to the data context. This has advantages (less overhead involved in creating new data contexts) and disadvantages (larger memory footprint as data contexts tend to hold onto quite a lot of memory through caches and the like).

I honestly think option #1 is a pretty good one, as long as you don't notice it performing too slowly (I'd time/profile it if you think it's causing problems). Due to connection pooling it shouldn't be all that bad. If it is, I'd go with #3 as my next choice. #2 isn't that far behind, but it would likely be a bit awkward and unexpected for other members of your team (if any).

The DataContext class is wrapped in a Using statement because it implements the IDisposable interface.

Internal to the DataContext it is using SqlConnection objects and SqlCommand objects. In order to correctly release these connection back to the Sql Connection Pool, they need to be disposed of.

The garbage collector will eventually do this, but it will take two passes due to the way IDisposable objects are managed.

It's strongly encouraged that Dispose is called and the Using statement is a nice way to do this.

Read these links for more indepth explanation:

http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/2625b105-2cff-45ad-ba29-abdd763f74fe/

http://www.c-sharpcorner.com/UploadFile/DipalChoksi/UnderstandingGarbageCollectioninNETFramework11292005051110AM/UnderstandingGarbageCollectioninNETFramework.aspx

An alternative would be to make your rCustomer class implement IDisposable , and then in your Dispose method, you can call Dispose on your DataContext if it is not null. However, this just pushes the Disposable pattern out of your rCustomer class, into whatever types are using rCustomer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM