开发者

Is there a way to reset a DbContext without disposing and reinstantiating it?

I recently refactored a WPF app so that it no longer wraps each use of the DbContext in a using clause (see this question). Instead, my app just uses the same DbContext singleton throughout.

This works great except for one small problem. I have a routine that rebuilds the database from scratch and inserts some default data. This routine uses ADO.NET directly (not the DbContext), so the DbContext is unaware that the database is now completely different.

Is there a method to reset the DbContext without disposing it? I'd like to avoid disposing if possible because this would break several references to the original singleton throug开发者_如何学Pythonhout the app.


I was not able to come up with a method to reset the global DbContext. I was able to solve my problem, however, by injecting a DbContextLocator into any class that needs a DbContext instead of passing the DbContext itself.

My goal was to maintain a global DbContext, but allow it to be reset whenever needed (such as after a database rebuild or import).

My solution uses an abstract base class and a concrete class.

Base Class

using System.Data.Entity;

namespace CommonLibrary.Database
{
    public abstract class DbContextLocator
    {
        private DbContext _dbContext;

        public DbContext Current
        {
            get { return _dbContext; }
        }

        public DbContextLocator()
        {
            _dbContext = GetNew();
        }

        public virtual void Reset()
        {
            _dbContext.Dispose();
            _dbContext = GetNew();
        }

        protected abstract DbContext GetNew();
    }
}

Concrete Class

using System.Data.Entity;
using CommonLibrary.Database;
using ExperimentBase.EntityModels;

namespace MainProject.Models    
{
    public class MainDbContextLocator : DbContextLocator
    {
        public new MainDbContext Current
        {
            get { return (MainDbContext)base.Current; }
        }

        protected override DbContext GetNew()
        {
            return new MainDbContext();
        }
    }
}

Usage

Get the current DbContext:

var dbContext = dbContextLocator.Current;

Reset the DbContext:

dbContextLocator.Reset();
//Note: normally followed by code that re-initializes app data

Edit

Based on Shimmy's feedback, I made DbContextLocatorBase into a generic. (I'm also now implementing IDisposable.)

Base Class

public class DbContextLocator<TContext> : IDisposable
    where TContext : DbContext, new()
{
    private TContext _dbContext;

    public TContext Current
    {
        get { return _dbContext; }
    }

    public DbContextLocator()
    {
        _dbContext = GetNew();
    }

    public virtual void Reset()
    {
        _dbContext.Dispose();
        _dbContext = GetNew();
    }

    protected virtual TContext GetNew()
    {
        return new TContext();
    }

    public void Dispose()
    {
        _dbContext.Dispose();
    }
}

Concrete Class (optional, since the base class is no longer abstract)

public class MainDbContextLocator : DbContextLocator<MainDbContext> { }


Keeping an ObjectContext open for the lifetime of the application is generally a bad idea.

ObjectContext (or DbContext in this case) is for a Unit of Work.

See Entity Framework and Connection Pooling


Entity Framework Core 5.0 now has a ChangeTracker.Clear() method which does this. As mentioned in the documentation, it's still best to create and dispose DbContext instances as needed, but sometimes that's not practical.

DbContext is designed to have a short lifetime where a new instance is created for each unit-of-work. This manner means all tracked entities are discarded when the context is disposed at the end of each unit-of-work. However, clearing all tracked entities using this method may be useful in situations where creating a new context instance is not practical.

0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜