ILikeFritos
ILikeFritos

Reputation: 21

Do I need to have a using statement with Entity Framework?

Can I use with Entity Framework:

private MyDbContext _context;

public ControllerName(MyDbContext context)
{
    this._context = context;
}

or should I use something like:

public async Task<ActionResult> GetSomething() 
{
    using (var db = new MyDbContext())
    {
        //Some code
    }
}

When I have a using statement it wants me to pass in options for the DbContext. I am not exactly sure what options it wants me to pass in.

Upvotes: 2

Views: 2734

Answers (2)

Harald Coppoolse
Harald Coppoolse

Reputation: 30474

It depends...

Should I create my own DbContext?

The reason for the using statement when using the DbContext is because you want to keep the DbContext alive for a short period. The DbContext holds an open connection with the database and it remembers the fetched items, so you can change the properties of the fetched items one by one, without having to fetch the data from the database again.

This is a powerful optimization. The disadvantage however is, that if the database changes while your dbContext is still alive, the data in your dbContext gets out of sync with the actual data in your database.

Hence it is wise not to keep your dbContext alive for a longer time. The using block makes sure that whatever happens, Dispose() is called when leaving the block, even after exceptions. It is similar to the destructor in C++, which is automatically called as soon as the variable gets out of scope.

Whether you should create a class that has the DbContext as input or whether you should create your own DbContext depends on your class.

Do you want that your user (= software, not the operator) decides which database should provide the data that your class needs to query: let your user provide the dbContext, and let him Dispose it. Note: your user must be aware of this, and not keep your class alive for a longer period (think of seconds).

User:

using (var dbContext = new MyDbContext(...))
{
    MyClass myObject = new MyClass(dbContext);

    var fetchedData = myObject.FetchData();
    ProcessFethedData(fetchedData);
}

Usually you don't want to burden your users with this. Consider to create a Disposable class that uses the ... in the constructor. You can create the dbContext:

class MyDbContext: IDisposable
{
    private readonly MyDbContext dbContext;

    public MyClass(string nameOrConnectionString)
    {
        this.dbContext = new MyDbContext(nameOrConnectionString);
    }

    public void Dispose()
    {
        this.dbContext?.Dispose();
    }

    public Customer FetchCustomer(int customerId)
    {
        return dbContext.Customers.Find(customerId);
    }
}

Usage:

using (var myDbContext = new MyDbContext(...))
{
    Customer customer1 = myDbContext.FetchCustomer(2);
    Customer customer2 = myDbContext.FetchCustomer(3);
    ...
}

Advantage: your customer won't have to know anything about your database, and about the method you are using to access it: is it entity framework? or are you using plain old SQL? Or maybe you are not using a database at all, but a CSV file!

The problem is: your user still has to remember your nameOrConnectionString, and he has to Dispose you after usage. He also has to know not to keep you open for a longer time.

On the other hand: it might be handy not to have to recreate the database connection again between fetching the two customers.

A third solution, which is mostly used is creating a class that will create and dispose the dbContext per method. If users forget to Dispose you, then the DbContext won't be kept open longer than needed.

Note: since you've hidden that you are using entity framework, or even that it is a database, give it a name that says: I am a class that can store data for a longer period of time:

class MyRepository
{
    private readonly string nameOrConnectionString;

    public MyClass(string nameOrConnectionString)
    {
        this.nameOrConnectionString = nameOrConnectionString);
    }

    public Customer FetchCustomer(int customerId)
    {
        using (var dbContext = new MyDbContext(this.nameOrConnectionString)
        {
            return dbContext.Customers.Find(customerId);
        }
    }
}

Usage:

var repository = new MyRepository(...)
Customer customer1 = repository.FetchCustomer(2);
Customer customer2 = repository.FetchCustomer(3);

Users can keep your repository alive for a longer period of time. The disadvantage is that per call the DbContext is created. Usually creating a dbContext is not time consuming, but keep it in mind: if you regularly need to fetch several items, consider creating a method to do this in one go. But then again: shouldn't you make methods for everything that you do "regularly"?

The constructor of DbContext

DbContext has two constructors. An Empty one, that takes its connection string from the config file, or one with a connection string.

If everyone in your application should use the same database (and thus the same connection string), put the connection string in the app.config. Microsoft about connection strings in DbContext

If there are several databases that are to be controlled by the same DbContext, your creators must tell you which connectionstring they should use:

class MyDbContext : DbContext
{
    // Default constructor uses DbConnectionString from app.config
    public MyDbContext() : base()
    {
       ...
    }

    // only provide non-default constructor if users want to tell you which database to use
    public MyDbContext(string nameOrConnectionString) : base(nameOrConnectionString)
    {
        ...
    }
    

Upvotes: 2

Sh.Imran
Sh.Imran

Reputation: 1033

You can use your context with both approaches. 1st option is with Dependency Injection for IoC. I have used it along with Repository in the following way:

Repository

public class ProductRepository : RepositoryBase<product>, IProductRepository
{
        public ProductRepository(RepositoryContext repositoryContext) : 
           base(repositoryContext)
        {
        }
        public IEnumerable<product> GetAllProducts()
        {
            return FindAll().ToList();
        }
 }

RepositoryBase

public abstract class RepositoryBase<T> : IRepositoryBase<T> where T : class
{
        protected RepositoryContext repositoryContext { get; set; }
        public RepositoryBase(RepositoryContext repositoryContext)
        {
            this.repositoryContext = repositoryContext;
        }
        public IQueryable<T> FindAll()
        {
            return this.repositoryContext.Set<T>().AsNoTracking();
        }
}

Controller

public class ProductController : ControllerBase
{
        private IRepositoryWrapper _repository;
        public ProductController(IRepositoryWrapper repository)
        {
            _repository = repository;
        }
        
        public IActionResult ProductList()
        {
            var products = _repository.products.GetAllProducts();
        }
}

Upvotes: 1

Related Questions