我是使用.NETCore的DI模式的新手,我无法将连接字符串连接到DAL。
我按照this thread中给出的建议,通过接受的答案和随后的评论。
这是我的基类
public class BaseRepository : IRepository<IDataModel>
{
private readonly IConfiguration config;
public BaseRepository(IConfiguration config)
{
this.config = config;
}
public string GetSQLConnectionString()
{
return config["Data:DefaultConnetion:ConnectionString"];
}
这是继承基类
的存储库类的片段public class PrivacyLevelRepository : BaseRepository, IRepository<PrivacyLevelDM>
{
public PrivacyLevelRepository(IConfiguration config) : base(config) { }
public void Add(PrivacyLevelDM dataModel)
{
...
}
}
这是我的startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
services.AddScoped<IRepository<IDataModel>>(c => new BaseRepository(Configuration));
}
但是,在我的服务层中,存储库类的实例化仍然要求将(IConfiguration配置)作为参数传递。
PrivacyLevelRepository repo = new PrivacyLevelRepository();
如何将IConfiguration直接加载到我的DAL,而不必从Controller&gt;传递它; BLL&gt; DAL。这似乎非常低效,而且不正确。因为DAL应该确定对象的连接,而不是控制器或服务层。他们应该不知道数据源,不是吗?
我认为这很简单,我只是没有在DI / IoC范例中看到,但我无法弄明白。
编辑:我没有使用Entity Framework,而是使用自定义数据层。
Thanx寻求帮助。
答案 0 :(得分:8)
您可以使用配置框架关注options pattern。这允许您定义保存配置设置(静态类型)的自定义类型,同时仅限于您的实际相关配置。
你可以像这样使用它:
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>();
}
这假定类型为DatabaseOptions
:
public class DatabaseOptions
{
public string ConnectionString { get; set; }
}
然后,您可以将DatabaseOptions
注入BaseRepository
:
public class BaseRepository
{
private readonly DatabaseOptions _options;
public BaseRepository(IOptions<DatabaseOptions> databaseOptions)
{
_options = databaseOptions.Value;
}
}
当然,如果您有BaseRepository
的子类型,您还需要注册这些子类并将选项传递给基类:
// register the repository as well in the `ConfigureServices` method
services.AddScoped<PrivacyLevelRepository>();
public class PrivacyLevelRepository : BaseRepository, IRepository<PrivacyLevelDM>
{
public PrivacyLevelRepository(IOptions<DatabaseOptions> databaseOptions)
: base(databaseOptions)
{ }
}
我正在实例化并使用像我一直拥有的回购。我不知道如何使用我没有实例化的类。我如何让这个对象知道它取决于
PrivacyLevelRepository
?PrivacyLevelRepository repo = new PrivacyLevelRepository(); returnValue = repo.GetAllByDomainID(DomainID).ToList(); return returnValue;
您似乎还不了解依赖注入背后的想法。依赖于其基本原则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);
}
}
当然,某些东西必须在某些时候创建依赖项。但是如果你完全接受依赖注入 - 哪个ASP.NET Core不仅非常容易,而且主动需要你这样做才能完全工作 - 那么你不需要关心那个部分。您只需在ConfigureServices
方法中注册类型,然后期望在您需要的地方满足相关性。
有关详细信息,请务必查看文档的dependency injection chapter。
答案 1 :(得分:5)
您不应该将IConfiguration
注入所有到您的课程中。 IConfiguration
允许访问所有配置值,而类只需要一个(或其中一些)。注入IConfiguration
是等同于Service Locator anti-pattern的配置(但是用于解析配置值)。它隐藏了消费者使用的实际配置值,使得类更难以使用和测试。
最重要的是,此模型使得验证配置文件的正确性变得更加困难,因为只有在应用程序中第一次请求时,才会验证各个配置值,这可能是许多鼠标“点击”进入申请表。
解决方案是在启动时加载并验证配置值 并仅注入一个类所需的配置值,仅此而已。这允许系统故障快速,并从类的API中清楚地了解它需要什么配置值。显然,您可以将配置值组合到一个Value Object中,而.NET Core使这更加简单,这非常好。
您应该防止的另一件事是使用基类。基类经常变得不断变化,并且越来越多的代码块带有辅助方法和交叉关注点。由于对基类的严重依赖,它们的衍生物变得更难测试。
当您将连接字符串直接注入PrivacyLevelRepository
时,不需要具有GetSQLConnectionString
的基类,因为存储库已经具有可用的连接字符串。可能还有其他原因导致您拥有此基类,例如因为您想要记录或实现安全功能,但我的建议是不要使用基类。取而代之的是使用装饰和拦截,因为它可以保持“派生”不会忽视这些横切关注的问题,甚至允许更加模块化和灵活的系统。
<强>更新强>
这是配置它的方法
string conStr = config["Data:DefaultConnetion:ConnectionString"];
services.AddScoped<IRepository<IDataModel>>(c => new PrivacyLevelRepository(conStr));
无论你做什么,都不要让你的应用程序组件依赖于IOptions<T>
,因为这会产生一些不良后果,如here所述。
答案 2 :(得分:1)
正如Steven所提到的,不要让你的应用程序组件依赖IOptions<T>
。
从IConfigurationRoot访问连接字符串的更合适方法如下:
string connectionString = configuration.GetConnectionString("DefaultConnection"); //substitute "DefaultConnection" for your named connection.
答案 3 :(得分:0)
定义一个具有您所需属性的类:
public class ConfHelper
{
public string ConnString { get; set; }
}
比在Startup.Configure Services中,您可以执行以下操作以从Configuration中读取设置:
services.AddSingleton(new ConfHelper() {ConnString = Configuration["ConnectionStrings:Default"]});
您可以将ConnectionString保留在appSettings.json中
"ConnectionStrings": {
"Default": "Persist Security Info=False;User ID=USER;Password=PASS;Initial Catalog=DBNAME;Data Source=localhost;"
}
您可以期望该平台在代码中的任何位置将对象注入构造函数中。
public class HomeController : ControllerBase {
private readonly ConfHelper _conf;
public HomeController(ConfHelper conf) { _conf = conf; }
}
除了调用services.AddSingleton,您还可以根据自己的约束将服务注册为Transient和Scoped。