我正在.NET 4.5中构建一个系统,该系统将具有不同的实现(即在不同客户的前提下实现)。每个客户都有自己的基础架构和数据库结构,因此我在很大程度上依赖于洋葱架构来构建系统,本身依赖于接口和DI。通过这种方式,我可以使用客户特定的"存储库"和"服务"实现。
我的目标是在不重新编译的情况下,能够在客户服务器上安装系统(系统入口点基本上是一个Windows服务,包含定期触发的业务逻辑,并且还托管WCF服务)。为了实现这一目标,我想到的是某种“依赖性”"或"插件" folder作为包含Windows服务可执行文件的文件夹的子文件夹,该文件夹将包含客户特定的DLL,该DLL具有实现应用程序所依赖的所有必要接口的具体类。
我试图通过Simple Injector实现这一目标。我已经看过SimpleInjector.Packaging程序集,还有关于"动态注册插件的段落" here,但我仍然有点卡住,不知道从哪里开始,比如我应该在哪个汇编中定义。
我需要一些关于如何实现这一目标的具体样本。
SimpleInjector Packaging程序集是用于此目的,还是我看到了这个错误?如果是这样,怎么样?
任何人都请赐教。
由于
ps:100%清晰:接口和具体实现显然分成不同的程序集。这个问题是关于如何使用Simple Injector动态连接所有内容。
答案 0 :(得分:5)
与简单注入器等IoC容器结合使用的好处在于,可以很容易地为所有插件添加通用逻辑。我最近写了一个批量图像转换器实用程序,允许插入新的图像转换器。
这是界面
public interface IImageConverter : IDisposable
{
string Name { get; }
string DefaultSourceFileExtension { get; }
string DefaultTargetFileExtension { get; }
string[] SourceFileExtensions { get; }
string[] TargetFileExtensions { get; }
void Convert(ImageDetail image);
}
注册是这样完成的(注意错误检查不是.NET程序集的插件依赖项)
private void RegisterImageConverters(Container container)
{
var pluginAssemblies = this.LoadAssemblies(this.settings.PluginDirectory);
var pluginTypes =
from dll in pluginAssemblies
from type in dll.GetExportedTypes()
where typeof(IImageConverter).IsAssignableFrom(type)
where !type.IsAbstract
where !type.IsGenericTypeDefinition
select type;
container.RegisterAll<IImageConverter>(pluginTypes);
}
private IEnumerable<Assembly> LoadAssemblies(string folder)
{
IEnumerable<string> dlls =
from file in new DirectoryInfo(folder).GetFiles()
where file.Extension == ".dll"
select file.FullName;
IList<Assembly> assemblies = new List<Assembly>();
foreach (string dll in dlls) {
try {
assemblies.Add(Assembly.LoadFile(dll));
}
catch { }
}
return assemblies;
}
所有插件操作所需的通用逻辑由装饰器处理
container.RegisterDecorator(
typeof(IImageConverter),
typeof(ImageConverterChecksumDecorator));
container.RegisterDecorator(
typeof(IImageConverter),
typeof(ImageConverterDeleteAndRecycleDecorator));
container.RegisterDecorator(
typeof(IImageConverter),
typeof(ImageConverterUpdateDatabaseDecorator));
container.RegisterDecorator(
typeof(IImageConverter),
typeof(ImageConverterLoggingDecorator));
这个最后的类更具体,但我已经添加了它作为如何在不直接依赖容器的情况下找到所需插件的示例
public sealed class PluginManager : IPluginManager
{
private readonly IEnumerable<IImageConverter> converters;
public PluginManager(IEnumerable<IImageConverter> converters) {
this.converters = converters;
}
public IList<IImageConverter> List() {
return this.converters.ToList();
}
public IList<IImageConverter> FindBySource(string extension) {
IEnumerable<IImageConverter> converters = this.converters
.Where(x =>
x.SourceFileExtensions.Contains(extension));
return converters.ToList();
}
public IList<IImageConverter> FindByTarget(string extension) {
IEnumerable<IImageConverter> converters = this.converters
.Where(x =>
x.TargetFileExtensions.Contains(extension));
return converters.ToList();
}
public IList<IImageConverter> Find(
string sourceFileExtension,
string targetFileExtension)
{
IEnumerable<IImageConverter> converter = this.converters
.Where(x =>
x.SourceFileExtensions.Contains(sourceFileExtension) &&
x.TargetFileExtensions.Contains(targetFileExtension));
return converter.ToList();
}
public IImageConverter Find(string name) {
IEnumerable<IImageConverter> converter = this.converters
.Where(x => x.Name == name);
return converter.SingleOrDefault();
}
}
您在容器中注册IPluginManager
,而其他人使用Simple Injector:
container.Register<IPluginManager, PluginManager>();
我希望这会有所帮助。
答案 1 :(得分:2)
你可能正在寻找的是:
public class TypeLoader<T> : List<T>
{
public const BindingFlags ConstructorSearch =
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance |
BindingFlags.Instance;
private void Load(params Assembly[] assemblies)
{
foreach (
Type t in
assemblies.SelectMany(
asm =>
asm.GetTypes()
.Where(t => t.IsSubclassOf(typeof (T)) || t.GetInterfaces().Any(i => i == typeof (T)))))
{
Add((T) Activator.CreateInstance(t, true));
}
}
}
您需要做的就是从插件目录中调用Assembly.ReflectionOnlyLoad
程序集并将它们传递给此方法。
例如,如果您在程序集中的所有插件类中声明IPlugin
,则可以像new TypeLoader<IPlugin>().Load(assemblies);
一样使用它,并且最终会得到一个整齐的列表实现IPlugin
的对象。
答案 2 :(得分:1)
您需要在启动时探测插件目录,并使用.NET反射API从动态加载的程序集中获取存储库类型。根据您的确切需要,有很多方法可以做到这一点。 Simple Injector中没有这个API,只是因为有很多方法可以做到这一点,编写一些自定义LINQ语句通常更具可读性和灵活性。
以下是一个示例:
// Find all repository abstractions
var repositoryAbstractions = (
from type in typeof(ICustomerRepository).Assembly.GetExportedTypes()
where type.IsInterface
where type.Name.EndsWith("Repository")
select type)
.ToArray();
string pluginDirectory =
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");
// Load all plugin assemblies
var pluginAssemblies =
from file in new DirectoryInfo(pluginDirectory).GetFiles()
where file.Extension.ToLower() == ".dll"
select Assembly.LoadFile(file.FullName);
// Find all repository abstractions
var repositoryImplementationTypes =
from assembly in pluginAssemblies
from type in assembly.GetExportedTypes()
where repositoryAbstractions.Any(r => r.IsAssignableFrom(type))
where !type.IsAbstract
where !type.IsGenericTypeDefinition
select type;
// Register all found repositories.
foreach (var type in repositoryImplementationTypes)
{
var abstraction = repositoryAbstractions.Single(r => r.IsAssignableFrom(type));
container.Register(abstraction, type);
}
上面的代码是Simple Injector文档中Registering plugins dynamically wiki page中代码示例的变体。