使用.NETCore进行DAL和连接字符串的依赖注入

BLa*_*ars 9 .net dependency-injection asp.net-core

我是使用.NETCore的DI模式的新手,我无法将连接字符串连接到DAL.

我通过接受的答案和随后的评论遵循了这个帖子中给出的建议.

这是我的基类

public class BaseRepository : IRepository<IDataModel>
{
    private readonly IConfiguration config;

    public BaseRepository(IConfiguration config)
    {
        this.config = config;
    }

    public string GetSQLConnectionString()
    {
        return config["Data:DefaultConnetion:ConnectionString"];
    }
Run Code Online (Sandbox Code Playgroud)

这是继承基类的存储库类的片段

public class PrivacyLevelRepository : BaseRepository, IRepository<PrivacyLevelDM>
{
    public PrivacyLevelRepository(IConfiguration config) : base(config) { }
    public void Add(PrivacyLevelDM dataModel)
    {
         ...
    }
}
Run Code Online (Sandbox Code Playgroud)

这是在我的startup.cs中

public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services.
        services.AddMvc();
        services.AddScoped<IRepository<IDataModel>>(c => new BaseRepository(Configuration));
    }
Run Code Online (Sandbox Code Playgroud)

但是,在我的服务层中,存储库类的实例化仍然要求(IConfiguration配置)作为参数传递.

 PrivacyLevelRepository repo = new PrivacyLevelRepository();
Run Code Online (Sandbox Code Playgroud)

如何将IConfiguration直接加载到我的DAL,而不必从Controller> BLL> DAL传递它.这似乎非常低效,而且不正确.因为DAL应该确定对象的连接,而不是控制器或服务层.他们应该不知道数据源,不是吗?

我认为这很简单,我只是没有在DI/IoC范例中看到,但我无法弄明白.

编辑:我没有使用Entity Framework,而是使用自定义数据层.

Thanx任何帮助.

pok*_*oke 11

您可以使用配置框架遵循选项模式.这允许您定义包含配置设置(静态类型)的自定义类型,同时仅限于实际的相关配置.

你可以像这样使用它:

public void ConfigureServices(IServiceCollection services)
{
    // register the `Data:DefaultConnection` configuration section as
    // a configuration for the `DatabaseOptions` type
    services.Configure<DatabaseOptions>(Configuration.GetSection("Data:DefaultConnection"));

    // register your database repository
    // note that we don’t use a custom factory where we create the object ourselves
    services.AddScoped<IRepository<IDataModel>, BaseRepository>();
}
Run Code Online (Sandbox Code Playgroud)

这假定DatabaseOptions类似这样的类型:

public class DatabaseOptions
{
    public string ConnectionString { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后,你可以DatabaseOptions注入你的BaseRepository:

public class BaseRepository
{
    private readonly DatabaseOptions _options;

    public BaseRepository(IOptions<DatabaseOptions> databaseOptions)
    {
         _options = databaseOptions.Value;
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,如果你有这种子类型BaseRepository,你也需要注册它们并将选项传递给基类:

// register the repository as well in the `ConfigureServices` method
services.AddScoped<PrivacyLevelRepository>();
Run Code Online (Sandbox Code Playgroud)
public class PrivacyLevelRepository : BaseRepository, IRepository<PrivacyLevelDM>
{
    public PrivacyLevelRepository(IOptions<DatabaseOptions> databaseOptions)
        : base(databaseOptions)
    { }
}
Run Code Online (Sandbox Code Playgroud)

我正在实例化并使用像我一直拥有的回购.我不知道如何使用我没有实例化的类.我如何让这个对象知道它取决于PrivacyLevelRepository

PrivacyLevelRepository repo = new PrivacyLevelRepository();
returnValue = repo.GetAllByDomainID(DomainID).ToList();
return returnValue;
Run Code Online (Sandbox Code Playgroud)

您似乎还不了解依赖注入背后的想法.依赖注入及其基本原理Inversion of Control简单地说就是避免使用new创建对象.您没有主动依赖于实现(在您的示例中PrivacyLevelRepository),而是放弃了责任,只依靠外部系统为您提供所需的依赖项.

因此,不是创建新的PrivacyLevelRepository,而是注入由其他地方创建的实例.这就失去了对依赖项实现的耦合.一个非常实际的例子是如何PrivacyLevelRepository依赖IOptions<DatabaseOptions>.作为该存储库的使用者,您不需要关心如何使这样的对象能够创建存储库实例.您甚至不需要知道如何创建存储库实例.

因此,您的消费者PrivacyLevelRepository应该遵循与存储库本身相同的想法:存储库不知道如何获取这些数据库选项; 它只取决于构造实体传递这样的对象.而我的消费者,我假设一个控制器,应该这样做:

public class MyController
{
    private readonly PrivacyLevelRepository _privacyLevelRepository;

    public MyController(PrivacyLevelRepository privacyLevelRepository)
    {
         // instead of *creating* a repository, we just expect to get one
         _privacyLevelRepository = privacyLevelRepository;
    }

    public IActionResult SomeRoute()
    {
         var domainId = "whatever";
         var data = _privacyLevelRepository.GetAllByDomainID(domainId).ToList();
         return View(data);
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,某些事情必须在某些时候创建依赖关系.但是如果你完全接受依赖注入 - 哪个ASP.NET Core不仅非常容易,而且还需要你这么做才能完全工作 - 那么你就不需要关心那个部分了.您只需在ConfigureServices方法中注册类型,然后期望在您需要的地方满足依赖项.

有关更多信息,请务必查看文档的依赖注入章节.


Ste*_*ven 5

你不应该被注入IConfiguration 在所有到您的类.在IConfiguration允许访问所有的配置值,而一类只需要一个(或几个人).注入IConfiguration是与Service Locator反模式相同的配置(但用于解析配置值).它隐藏了消费者使用的实际配置值,使得类更难以使用和测试.

最重要的是,这个模型使得验证配置文件的正确性变得更加困难,因为只有在应用程序中第一次请求时,才能验证各个配置值,这可能是许多鼠标"点击"到应用程序中.

解决方案是在启动时加载并验证配置值,并仅注入一个类所需的配置值,仅此而已.这允许系统快速失败并从类的API中非常清楚它需要什么配置值.显然,您可以将配置值一起打包到一个Value Object中,而.NET Core使这更加简单,这非常好.

您应该防止的另一件事是使用基类.基类经常变得不断变化,并且正在增加代码块,其中包含辅助方法和交叉关注点.由于对基类的依赖性很强,它们的衍生物变得更难测试.

当您将连接字符串直接注入到您的中时PrivacyLevelRepository,不需要具有a的基类GetSQLConnectionString,因为存储库已经具有可用的连接字符串.可能还有其他原因导致您拥有此基类,例如因为您想要记录或实现安全功能,但我的建议是不要使用基类.取而代之的是使用装饰和拦截,因为它允许保持"衍生"忽视这些横切关注的问题,甚至允许更加模块化和灵活的系统.

UPDATE

这是配置它的方法

string conStr = config["Data:DefaultConnetion:ConnectionString"];

services.AddScoped<IRepository<IDataModel>>(c => new PrivacyLevelRepository(conStr));
Run Code Online (Sandbox Code Playgroud)

不管你做什么,不要让你的应用程序组件的依赖IOptions<T>,因为它有一些很糟糕的后果,如所描述这里.