将配置文件中的连接字符串注入EF Core DbContext

时间:2019-03-28 21:25:28

标签: c# entity-framework-core autofac clean-architecture property-injection

我需要将依赖项注入到我的项目的DbContext类中。我正在尝试按照“清洁体系结构”的方式进行操作。

我需要能够使用DbContext的空构造函数,并且仍然引用配置文件中的连接字符串。

我利用此通用存储库并将其注入服务。它是在应用程序层中定义的。

     public class GenericRepository<C, T> : IGenericRepository<C, T> where T : class where C : DbContext, new()
     {
        private DbContext _entities = new C();
     }

在我的EF Core Context类(在持久层中定义)中,我有这个:

   public partial class MyContext : DbContext
   {
       private IConnectionStringProvider _connectionStringProvider;  

        public virtual DbSet<MyEntity> { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {  
                optionsBuilder.UseSqlServer(_connectionStringProvider.GetConnectionString());
            }
        }
    }

我还具有在持久层中定义的IConnectionStringProvider的实现。我希望此实现在需要时可以轻松互换。目前,它从App.config中读取并使用ConfigurationManager nuget包。但这可能会在将来发生变化,因此需要易于交换。

IConnectionStringProvider在应用程序层中定义:

 public interface IConnectionStringProvider
 {
     string GetConnectionString();
 }

在我的Presentation层中,我有一个ASP.NET Core MVC项目。在StartUp.cs中,我使用了将dbContext注入到我的contollers的标准方法:

services.AddDbContext<ContractsWorkloadContext>();

如果我以以下方式重写OnModelCreatingMethod,我可以使它正常工作。

 protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
 {
     if (!optionsBuilder.IsConfigured)
     {
         _connectionStringProvider = new ConnectionStringProvider();
         optionsBuilder.UseSqlServer(ConnectionStringProvider.GetConnectionString());
     }
 }

此方法允许我将配置中的连接字符串值与空的上下文构造函数实例一起使用,这是它与我的通用存储库一起使用所必需的,并且仍然能够将构造函数与DbContextOptionsBuilder参数一起使用。

我唯一要解决的问题是如何在不依赖上下文依赖实现的情况下实现这一目标。

我已经尝试通过注册IConnectionStringProvider接口和实现以及还将Context类注册为属性“ Autowired”来进行Autofac属性注入。但是该属性始终为null。

是的,我知道上面是一个字段,而不是一个属性。我试图将上下文类修改为具有属性而不是字段。我甚至尝试创建一种方法来设置字段,并使用autofac进行方法注入。但是在每种情况下,属性/字段始终为null。

 builder.RegisterType<ConnectionStringProvider>().As<IConnectionStringProvider>()();
 builder.RegisterType<MyContext>().PropertiesAutowired();

 builder.RegisterType<ContractsWorkloadContext>().OnActivated(e =>
 {
     var provider = e.Context.Resolve<IConnectionStringProvider>();
     e.Instance.SetConnectionstringProvider(provider);
 });

总而言之,我需要:

  1. 将连接字符串注入上下文中
  2. 允许创建空的构造函数实例
  3. 保持“清洁建筑”设计

这里的任何帮助将不胜感激。

谢谢。

2 个答案:

答案 0 :(得分:0)

我最终要做的是创建对连接字符串的静态引用,然后在返回该值的上下文中创建一个只读属性。

通过这种方式,我能够在应用程序层中抽象IConnectionStringProvider并在基础结构层中实现它。如果需要,这将使实现的交换工作量最小。

此外,通过添加返回静态连接字符串的readonly属性,我可以使用利用new()的通用存储库,并且仍然从配置文件中获取连接字符串。

唯一的缺点是连接字符串在整个应用程序生命周期中都存储在内存中。

enter image description here

静态值是由提供程序实现在表示层的StartUp.cs文件中设置的。

DbContext

这绝对有效...但是,如果有更好的方法,那么我将不胜枚举...:)

答案 1 :(得分:0)

对于那些偶然发现此问题的人来说,这是一种更好的方法。

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

更具体的存储库实现只是扩展基本存储库实现的类。如:

EFCustomerRepository : EFRepository<MyContext, Customer>

现在,我可以通过依赖项注入在整个应用程序中的任何位置注入IMyEFCoreDatabase,并引用实现中存储的存储库。

这是比上面更好的方法,因为我的存储库不再调用new C()。相反,他们现在通过构造函数注入来注入上下文。