What is the best way to create EF DbContext instan

2019-04-17 09:14发布

问题:

In order to support lazy loading feature in EF, what is the best way to instantiate DbContext?

I know HttpContext's current item is good place to create DbContext via Application_BeginRequest method and Application_EndRequest method, but in some sample codes of MSDN and official asp.net mvc site, they just create DbContext in Controller's constructor and dispose it in controller's Dispose() method.

I think the both ways are not too different because all of those all implement session per request pattern.

I just want to make sure that my understanding is correct or not.

回答1:

The Dispose() method in the controller isn't always reliable. By the same token, Session is probably not a good idea either. "Best" is probably subjective, but we've had the best success by using dependency injection (Castle Windsor) and following a Unit of Work Repository pattern.

Setup the unit of work along the following lines:

public class UnitOfWork : IUnitOfWork
{
    public UnitOfWork()
    {
        this.Context = new MyEFEntities();
        this.Context.ContextOptions.LazyLoadingEnabled = true;
    }

    public void Dispose()
    {
        this.Context.Dispose();
    }

    public ObjectContext Context { get; internal set; }
}

Setup your repository:

public class Repository<TEntity> : IRepository<TEntity>
    where TEntity : class
{
    public Repository(IUnitOfWork unitOfWork)
    {
        Context = unitOfWork.Context;
        ObjectSet = Context.CreateObjectSet<TEntity>();
    }
    public ObjectContext Context { get; set; }
    public IObjectSet<TEntity> ObjectSet { get; set; }
}

Register with Castle in Global.asax:

void Application_Start()
{
    this.Container.Register(
        Component.For<IUnitOfWork>()
            .UsingFactoryMethod(() => new UnitOfWork())
            .LifeStyle
            .Is(LifestyleType.PerWebRequest)
        );

    ControllerBuilder.Current.SetControllerFactory(
        new WindsorControllerFactory(this.Container));
}

And use in your controller (or wherever you're using it, as long as it's injectable):

public class SomeController
{
    public SomeController(IRepository<MyEntity> repository)
    {
        this.Repository = repository;
    }

    public IRepository<MyEntity> Repository { get; set; }

    public ActionResult MyAction()
    {
        ViewData.Model = this.Repository.ObjectSet.Single(x => x.Condition); //or something...
    }
}


回答2:

Any lazy loading here could potentially be a trap for a future issue. Without DI, without a repository - its hard to see anything working without it being a hack for lazy loading. Also do you you plan on passing your entities to your view. If so this is going to create a bad overlap. The controller should package data for your view, not have things evaluated later in your view.

For MVC best practices, you should flatten out your domain model as much as possible into a viewmodel (if flattening makes sense) and use the view model. Since you would ideally then know what would be lazy loaded, it may make more sense to take the hit up front and use .Include() in your query to eager load, otherwise you can issue many many queries to the database.



回答3:

I've used a session factory pattern and saved the DBContext in the session object. It will stay open per session. I haven't had problems with it so far.