我正在重写我公司的DAO库。由于我们的解决方案的特性,我们需要根据客户切换ADO库(Oracle / SQL Server)。 由于静态引用是我们问题的主要来源(f.e.Oracle包取自CI而不是SQL Server),我决定采用插件架构,并尝试动态加载所需的dll。
我是Simple Injector的新手(我使用的是Ninject,但在这种情况下,我们需要一些非常快的东西)。我使用了https://simpleinjector.readthedocs.org/en/latest/advanced.html#registering-plugins-dynamically文章并设法将正确的dll加载到域中。
我目前在容器验证(container.Verify())期间遇到一个奇怪的(在我看来)错误:
GenericDAO类型的构造函数包含Boolean类型的参数,名称为'isInUserContext',未注册。请确保在容器中注册了Boolean,或者更改了GenericDAO的构造函数。
我的构造函数看起来像这样:
public class GenericDAO : DBHelper
{
public GenericDAO(Boolean isInUserContext, string connectionString, string providerName, UniversalDataAccess universalDataAccess)
: base(isInUserContext, connectionString, providerName, universalDataAccess)
{
}
我的注册:
var pluginDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");
var pluginAssemblies =
from file in new DirectoryInfo(pluginDirectory).GetFiles()
where file.Extension.ToLower() == ".dll"
select Assembly.LoadFile(file.FullName);
var pluginTypes =
from assembly in pluginAssemblies
from type in assembly.GetExportedTypes()
where typeof (IDBHelper).IsAssignableFrom(type)
where !type.IsAbstract
where !type.IsGenericTypeDefinition
select type;
_kernel.RegisterAll<IDBHelper>(pluginTypes);
您知道如何成功初始化所有需要的类型吗? 我想公开GenericDAO类型的公共属性。
答案 0 :(得分:6)
异常消息有点误导,因为Simple Injector不允许您在容器中注册原始类型(例如Boolean
),所以实际上容器会告诉您一些不可能做的事情。
Simple Injector不允许您注册诸如int,bool,string之类的原语是因为这些类型是不明确的。 .NET中的DI容器根据类型信息注入服务,但是代码库中的所有组件在构造函数中需要string
的实际上不太可能需要完全相同的值。有些人期望连接字符串,有些人期望一些文件路径。但是如果你能够在容器中注册string
,你只能指定一个值(例如连接字符串),这样就无法注入另一个值(例如文件路径)。
然而,这个问题并不是Simple Injector特有的,这是一个普遍的问题。但是,其他DI容器可能允许您解析在构造函数中采用原始值的组件,并可能使用某个默认值填充此值。这几乎没用,因为如果你只需要那个原始类型的默认值,为什么还要通过构造函数公开这个类型呢?
很难说你应该做些什么来解决这个问题。一般来说,如果类型需要一些配置值,则需要明确告诉容器应该如何解析此类型。使用Simple Injector,这将如下所示:
container.Register<GenericDAO>(() => new GenericDAO(
isInUserContext: true,
connectionString: "con str",
providerName: "System.Sql.Client",
universalDataAccess: container.GetInstance<UniversalDataAccess>()));
但是,如果您有多个(或许多)组件需要相同的配置值,这通常意味着您缺少抽象。例如,isInUserContext
可能是隐藏在IUserContext
抽象背后的值,connectionString
和providerName
气味就像IDbConnectionFactory
抽象或其他东西类似。在后一种情况下,GenericDAO
类将把创建连接的责任转移到另一个类中,使其能够专注于它所承担的责任。
在这种情况下,GenericDAO
的构造函数将如下所示:
public GenericDAO(IUserContext userContext, IDbConnectionFactory connectionFactory,
UniversalDataAccess universalDataAccess)
现在因为构造函数不再有原始参数,所以现在可以通过容器自动连接类。在Simple Injector中,您现在可以按如下方式注册:
container.Register<GenericDAO>();
在您的情况下,优势变得尤为明显,因为您批量注册GenericDAO
作为插件。所有批次注册的类型必须是自动连线才能成功。
当然,您还必须注册IUserContext
和IDbConnectionFactory
,它们可能仍然依赖于某些原始值:
container.RegisterSingle<IUserContext>(new AspNetUserContext());
container.RegisterSingle<IDbConnectionFactory>(
new SqlServerDbConnectionFactory("con str"));
需要在引导程序和插件程序集引用的某个中央程序集中定义IUserContext
和IDbConnectionFactory
抽象。
另一个选择是让插件程序集自己注册插件。除此之外,这些插件程序集需要依赖于您的DI容器。 Simple Injector包含一个SimpleInjector.Packaging
NuGet项目,允许您在插件程序集中创建一个“包”。例如:
public class Plugin1Bootstrapper : IPackage
{
public void RegisterServices(Container container) {
container.Register<GenericDAO>(() => new GenericDAO(
isInUserContext: true,
connectionString: "con str",
providerName: "System.Sql.Client",
universalDataAccess: container.GetInstance<UniversalDataAccess>()));
// other registrations here.
}
}
在应用程序的启动路径中,您可以致电:
container.RegisterPackages(pluginAssemblies);
这将为所提供的程序集加载所有IPackage
实现,并在每个程序集上调用RegisterServices
。
请注意,我们再次在此处注册GenericDAO
的具体类型。您可以将其与您已使用的RegisterAll<IDBHelper>(...)
混合使用。 RegisterAll
注册将回调集合中每个元素的容器,因此通过使用Register<GenericDAO>
注册具体类型,可以明确指定Simple Injector必须如何解析该具体类型。否则,Simple Injector将尝试自动连接该具体类型并将其解析为瞬态。