How to use DbContext in separate class library .net core?

.net-core asp.net-core-mvc c# dependency-injection entity-framework-core

Question

I'm trying to access my dbcontext from my .net core 3.1 MVC project in a class library. Currently I inject my database into the service collection in startup.cs

public class AppDbContext : DbContext
{
    public DbSet<User> Users {get; set;}
    public AppDbContext(DbContextOptions<AppDbContext> options)
        : base(options)
    { }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        // Handles duplicate entry for email in user table
        builder.Entity<User>()
            .HasIndex(u => u.Email)
            .IsUnique();
    }
}

However, I'm unsure of how to access this AppDbContext in my class library. I tried accessing it like i would a controller, but obviously it doesn't know about the DI container.

More information: This library is used for common requests that are made. It must always be separate from the main web application and the user should never have to code in this class library. Therefore, I need to be able to access the dbcontext thats in the main web project from the class library.

1
0
1/15/2020 2:23:00 PM

Accepted Answer

As you said you are developing the class library to use any DbConext passing by the client of the library then you have to do as follows:

First considering your class library has following interfaces and classes where your DbCoenxt will be used:

public interface IUnitOfWork 
{
    IRepository<T> Repository<T>() where T : class;
    Task SaveChangesAsync();
}

internal class UnitOfWork : IUnitOfWork
{
    private readonly DbContext _dbContext;
    private Hashtable _repositories;
    public UnitOfWork(DbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public IRepository<T> Repository<T>() where T : class
    {
        if (_repositories == null)
            _repositories = new Hashtable();

        var type = typeof(T).Name;

        if (!_repositories.ContainsKey(type))
        {
            var repositoryType = typeof(Repository<>);

            var repositoryInstance =
                Activator.CreateInstance(repositoryType.MakeGenericType(typeof(T)), _dbContext);

            _repositories.Add(type, repositoryInstance);
        }

        return (IRepository<T>)_repositories[type];
    }

    public async Task SaveChangesAsync()
    {
        await _dbContext.SaveChangesAsync();
    }
}

public interface IRepository<TEntity> where TEntity : class
{
     Task InsertEntityAsync(TEntity entity);
}

internal class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
    private readonly DbContext _dbContext;
    public Repository(DbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task InsertEntityAsync(TEntity entity)
    {
        await _dbContext.Set<TEntity>().AddAsync(entity);
    }
 }

Now write a a service collection extension method in your class library as follows:

public static class ServiceCollectionExtensions
{

    public static void RegisterYourLibrary(this IServiceCollection services, DbContext dbContext)
    {
        if (dbContext == null)
        {
            throw new ArgumentNullException(nameof(dbContext));
        }

        services.AddScoped<IUnitOfWork, UnitOfWork>(uow => new UnitOfWork(dbContext));
    }
}

Now in the Startup.ConfigureServices of your client application as follows:

public void ConfigureServices(IServiceCollection services)
{
    string connectionString = Configuration.GetConnectionString("ConnectionStringName");
    services.AddDbContext<AppDbContext>(option => option.UseSqlServer(connectionString));

    ServiceProvider serviceProvider = services.BuildServiceProvider();
    AppDbContext appDbContext = serviceProvider.GetService<AppDbContext>();

    services.RegisterYourLibrary(appDbContext); // <-- Here passing the DbConext instance to the class library

    .......
}

Usage:

public class EmployeeController : Controller
{
    private readonly IUnitOfWork _unitOfWork;

    public EmployeeController(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

    public async Task<IActionResult> Insert()
    {
        Employee employee = new Employee();
        await _unitOfWork.Repository<Employee>().InsertEntityAsync(employee);
        await _unitOfWork.SaveChangesAsync();
        return View();
    }
}
1
1/15/2020 3:34:35 PM

Popular Answer

if you created your dbcontext in another project/lib you first need to migrated it, And update it. EF uses IDesignTimeDbContextFactory notify entity framwork about the lib.

 public class ContextFactoryNeededForMigrations : IDesignTimeDbContextFactory<AppDbContext >
    {
        private const string ConnectionString =
            "Server=(localdb)\\mssqllocaldb;Database=EfCoreInActionDb;Trusted_Connection=True;MultipleActiveResultSets=true";

        public EfCoreContext CreateDbContext(string[] args)
        {
            var optionsBuilder = new DbContextOptionsBuilder<EfCoreContext>();
            optionsBuilder.UseSqlServer(ConnectionString,
                b => b.MigrationsAssembly("DataLayer"));

            return new EfCoreContext(optionsBuilder.Options);
        }

Then you can add it to your startup.cs DI container like this.

 services.AddDbContextPool<AppDbContext >( 
                options => options.UseSqlServer(connection,
                b => b.MigrationsAssembly("DataLayer"))); 

Here is good tutorial on the subject. enter link description here



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow