Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to scope out Dbcontexts (to prevent singleton context for entire application)

I was wondering how do you scope out your Dbcontexts in Entity Framework so you don't use a single Dbcontext for your entire application. I am new to Entity Framework and have been reading tutorials, but they all used a single Dbcontext as an example, so EF is pretty much a blackbox for me right now.

Let's say for example I have 3 models:

  • Post
  • User
  • Comment

Each model is related to each other (A Post belongs to User, Comment belongs to User and Post). Do I make a Dbcontext for each one individually? But that wouldn't be correct since they are all related, or would I make a Dbcontext for each scenario that I need? For example, if I only need to query for Post and Comments and not user, that would be a PostCommentsContext. And then we would have a PostUserCommentContext...

like image 468
Alex Avatar asked Jan 27 '11 21:01

Alex


2 Answers

The best solution would be to use a Unit of Work to wrap the Data Context, as well as managing the connection lifetime and allowing you to work with multiple Repositories (if you were so inclined to go down that path).

Summary of implementation:

  • Create an interface (IUnitOfWork) which exposes properties for your DbSet's, as well as a single method called Commit
  • Create an implementation (EntityFrameworkUnitOfWork), implementing as required. Commit simply calls SaveChanges on the base class (DbContext), and also provides a good hook-in for last minute logic.
  • Your controller accepts a IUnitOfWork, use DI (preferably) to resolve a EntityFrameworkUnitOfWork, with a HTTP-context scoped lifetime setting (StructureMap is good for this)
  • (optional, but recommended) create a Repository which also takes the IUnitOfWork, and work off that via your Controller.

HTH

EDIT - In Response to Comments

Oh, how can you do work that involves creating records in multiple models then? i.e., create a new user and a new post in the same transaction.

Given your using ASP.NET MVC, your controllers should accept an IUnitOfWork in their constructor.

Here's an example, based on what you asked

public SomeController : Controller
{
   private IUnitOfWork _unitOfWork;
   private IUserRepo _userRepo;
   private IPostRepo _postRepo;

   public SomeController(IUnitOfWork unitOfWork, IUserRepo userRepo, IPostRepo postRepo)
   {
      _unitOfWork = unitOfWork; // use DI to resolve EntityFrameworkUnitOfWork
      _userRepo = userRepo;
      _postRepo = postRepo;
   }

   [HttpPost]
   public ActionResult CreateUserAndPost(User user, Post post)
   {
      // at this stage, a HTTP request has come in, been resolved to be this Controller
      // your DI container would then see this Controller needs a IUnitOfWork, as well
      // as two Repositories. DI smarts will resolve each dependency.
      // The end result is a single DataContext (wrapped by UoW) shared by all Repos.
      try
      {
         userRepo.Add(user);
         postRepo.Add(post);
         // nothing has been sent to DB yet, only two objects in EF graph set to EntityState.Added
         _unitOfWork.Commit(); // two INSERT's pushed to DB
      }
      catch (Exception exc)
      {
          ModelState.AddError("UhOh", exc.ToString());
      }
   }
}

And one more question, what does the HTTP-context scoped lifetime do?

Objects in DI-talk have scope management settings that include per thread, per session, per http request, singleton, etc.

HTTP-context scoped is the recommended setting for web apps. It means "new up a context when a HTTP request comes in, and get rid of it when the request is finished".

like image 56
RPM1984 Avatar answered Oct 15 '22 05:10

RPM1984


Use 1 DbContext! That will make life easier for you. Don't worry about performance, data that isn't needed or queried won't be loaded and won't consume any resources.

public class UserContext : DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<Post> Posts { get; set; }
    public DbSet<Comment> Comments { get; set; }
}

For some scenarios you might want 2 or more contexts.

A context like the one above to hold all the front-end data needed for your application to work and another context for - as an example - to store reports generated from that front-end data, and which is only used in the back-end of you application.

like image 6
Steven K. Avatar answered Oct 15 '22 05:10

Steven K.