【问题标题】:How to use DbContext in separate class library .net core?如何在单独的类库.net core 中使用 DbContext?
【发布时间】:2020-05-02 07:50:20
【问题描述】:

我正在尝试从类库中的 .net core 3.1 MVC 项目访问我的 dbcontext。目前我将我的数据库注入到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();
    }
}

但是,我不确定如何在我的类库中访问这个 AppDbContext。我尝试像访问控制器一样访问它,但显然它不知道 DI 容器。

更多信息: 此库用于发出的常见请求。它必须始终与主 Web 应用程序分开,并且用户永远不必在此类库中编写代码。因此,我需要能够从类库中访问主 Web 项目中的 dbcontext。

【问题讨论】:

  • AppDbContext 类是在哪里创建的?
  • 在主应用程序中。
  • 你知道如何为不是 DbContext 的类做这件事吗? DbContext 是否有特定的东西阻止你的类库访问它,或者这是一个普遍的依赖问题?
  • 我通常做的是将数据上下文移动到它自己的库中并从那里重用。
  • @TheDizzle 请检查我的答案。希望对您有所帮助。

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


【解决方案1】:

正如您所说,您正在开发类库以使用库客户端传递的任何DbContext,那么您必须执行以下操作:

首先考虑你的类库有以下接口和类,你的DbContext 将被使用:

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);
    }
 }

现在在你的类库中编写一个服务集合扩展方法如下:

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));
    }
}

现在在您的客户端应用程序的Startup.ConfigureServices 中如下:

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

    .......
}

用法:

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();
    }
}

【讨论】:

  • 我通过了dbContext,但它不知道设置的任何模型。
  • 谢谢你。我能够添加到这个工作解决方案中:-------- interface IRepository + Task> ToListAsync(); + 任务 FirstOrDefault(); + void Empty(TEntity 实体); --------------- class Repository + public async Task> ToListAsync() => await _dbContext.Set().ToListAsync(); + 公共异步任务 FirstOrDefault() => await _dbContext.Set().FirstOrDefaultAsync(); + public void Empty(TEntity entity) => _dbContext.Set().Remove(entity);
【解决方案2】:

在收到 PMC 命令Add-Migration InitialCreate 的错误后,我就这样解决了:

Unable to create an object of type 'ApplicationDbContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728

IDesignTimeDbContextFactory 添加为@JoseGonzalez,但我不想在IDesignTimeDbContextFactory 的实现中硬编码我的连接字符串,而是从我的应用程序appsettings.json 中获取它。

最终解决方案如下所示:

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace MyNamespace
{
    public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
    {
        public ApplicationDbContext CreateDbContext(string[] args)
        {
            var configuration = new ConfigurationBuilder()
                    .SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appsettings.json")
                    .Build();

            var optionsBuilder = new DbContextOptionsBuilder();

            var connectionString = configuration
                        .GetConnectionString("DefaultConnection");

            optionsBuilder.UseSqlServer(connectionString);

            return new ApplicationDbContext(optionsBuilder.Options);
        }
    }
}

然后我可以像这样在我的其他项目中使用它:

var applicationDbContextFactory = new ApplicationDbContextFactory();

using (var dbContext = applicationDbContextFactory.CreateDbContext(args))
{
    
}

【讨论】:

    【解决方案3】:

    如果您在另一个项目/lib 中创建了 dbcontext,您首先需要迁移它,然后更新它。 EF 使用 IDesignTimeDbContextFactory 通知有关该库的实体框架。

     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);
            }
    

    然后你可以像这样将它添加到你的 startup.cs DI 容器中。

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

    这是关于这个主题的好教程。 enter link description here

    【讨论】:

      猜你喜欢
      • 2019-12-21
      • 2019-03-10
      • 2018-12-31
      • 1970-01-01
      • 1970-01-01
      • 2022-01-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多