如何使用自动注册来确定单个库的范围
本文关键字:单个库 范围 何使用 注册 | 更新日期: 2023-09-27 18:17:12
是否可以设置/更改依赖生命周期范围选项(瞬态,单一,PerWebReqest等…)为一个,两个或x个特定的程序集,如果你自动注册/发现你的依赖关系?我有下面的示例代码:
public class RegistrationPackage : IPackage
{
public void RegisterServices(Container container)
{
var @namespace = "ConsoleLib";
var assemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(a => a.FullName.StartsWith(@namespace))
.Select(a => a).ToList();
var names = assemblies.Select(a => a.FullName);
Action<string> poormanslogger = Console.WriteLine;
//poormanslogger = Trace.WriteLine;
foreach (var name in names)
poormanslogger("Located the following assemblies: " + name);
// could combine these two and remove the if statement for the open generic registrations below
//container.RegisterManyForOpenGeneric(typeof(ICommandHandler<>), AppDomain.CurrentDomain.GetAssemblies());
foreach (var assembly in assemblies)
foreach (var type in assembly.GetExportedTypes().Where(a => !a.IsAbstract))
{
poormanslogger(string.Format("Exported Type: {0}", type.Name));
foreach (var @interface in type.GetInterfaces())
{
poormanslogger(string.Format("'tInterface type: {0}", @interface.Name));
// register open generic
if (@interface.IsGenericType && !type.IsAbstract && type.IsGenericType)
container.RegisterManyForOpenGeneric(type, assemblies);
// register closed generic
if (@interface.IsGenericType && !type.IsAbstract && !type.IsGenericType)
container.Register(@interface, type, Lifestyle.Transient);
// register non generic
if (!type.IsGenericType && !@interface.IsGenericType && !type.IsAbstract)
container.Register(@interface, type, Lifestyle.Transient);
}
// unregistered type fall back...
//container.RegisterOpenGeneric(typeof(ICommandHandler<>), typeof(UnRegisteredCommandHandler<>));
}
// The decorators are applied in the order in which they are registered
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(TransactionCommandHandlerDecorator<>));
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(DeadlockRetryCommandHandlerDecorator<>));
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(ValidationCommandHandlerDecorator<>));
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(SecurityCommandHandlerDecorator<>));
}
}
然后我有标准的东西:
var container = new Container();
container.RegisterPackages();
container.Verify();
var instance = container.GetInstance<ICommandHandler<UnregisteredCommand>>();
instance.Handle(new UnregisteredCommand());
我可以使用ExpressionBuilding事件吗?
Simple Injector提供的批处理注册功能限制了它自己对泛型类型的注册。文档解释了原因:
尽管许多其他的DI库包含一个高级的API来做基于约定的注册,我们发现用自定义来做这个LINQ查询更容易编写,更容易理解,并且通常可以被证明比使用预定义的限制性API更灵活。
但是,即使对于通用接口类型的批量注册,也没有内置支持来区分不同的生活方式。这是经过深思熟虑的,因为这将迫使你在类上放置库定义的属性来描述生活方式,这将使你的应用依赖于简单注入器,这是我们想要防止的。
Simple Injector 2.6引入了一个新的扩展点,叫做生活方式选择行为。当没有显式提供生活方式时,此扩展点允许您覆盖框架的默认选择行为。默认行为是使用Lifestyle.Transient
.
如果您需要这样的东西,您应该覆盖默认的生活方式选择行为,并创建一个可以应用于组件的属性。可以像这样:
public enum CreationPolicy { Transient, Scoped, Singleton };
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface,
Inherited = false, AllowMultiple = false)]
public sealed class CreationPolicyAttribute : Attribute {
public CreationPolicyAttribute(CreationPolicy policy) {
this.Policy = policy;
}
public CreationPolicy Policy { get; private set; }
}
此属性应在应用程序的基项目中定义,以便可以用它标记每个类:
[CreationPolicy(CreationPolicy.Scoped)]
public class CustomerRepository : IRepository<Customer>
{
public CustomerRepository(DbContext context) { }
public Customer GetById(Guid id) {
// etc.
}
}
使用这个已定义的属性,我们现在可以定义自定义的生活方式选择行为,如下所示:
public class AttributeLifestyleSelectionBehavior : ILifestyleSelectionBehavior {
private const CreationPolicy DefaultPolicy = CreationPolicy.Transient;
private readonly ScopedLifestyle scopedLifestyle;
public AttributeLifestyleSelectionBehavior (ScopedLifestyle scopedLifestyle) {
this.scopedLifestyle = scopedLifestyle;
}
public Lifestyle SelectLifestyle(Type serviceType, Type implementationType) {
var attribute = implementationType.GetCustomAttribute<CreationPolicyAttribute>()
?? serviceType.GetCustomAttribute<CreationPolicyAttribute>();
var policy = attribute == null ? DefaultPolicy : attribute.Policy;
switch (policy) {
case CreationPolicy.Singleton: return Lifestyle.Singleton;
case CreationPolicy.Scoped: return this.scopedLifestyle;
default: return Lifestyle.Transient;
}
}
}
这个自定义行为可以按照如下方式注册:
var container = new Container();
// This is the scoped lifestyle for our application type.
ScopedLifestyle scopedLifestyle = new WebRequestLifestyle();
container.Options.LifestyleSelectionBehavior =
new AttributeLifestyleSelectionBehavior(scopedLifestyle);
有了这个,我们简化了批注册,我们不需要做任何特别的事情来选择合适的生活方式:
container.RegisterOpenGeneric(
typeof(ICommandHandler<>),
AppDomain.CurrentDomain.GetAssemblies());
var registrations =
from assembly in assemblies
from type in assembly.GetExportedTypes()
where !type.IsAbstract && !type.IsGenericType
from @interface in type.GetInterfaces()
where !@interface.IsGenericType
select new { Service = @interface, Implementation = type };
foreach (var registration in registrations)
{
container.Register(registration.Service, registration.Implementation);
}
不覆盖生活方式选择行为,另一种选择是将所有内容注册为暂态(因为这将是一个明智的默认值),但覆盖那些应该以其他方式注册的少数注册:
container.RegisterOpenGeneric(
typeof(ICommandHandler<>),
AppDomain.CurrentDomain.GetAssemblies());
container.Options.EnableOverridingRegistrations = true;
container.Register<ICommandHandler<SpecialOperation>, SpecialOperationHandler>(
scopedLifestyle);
container.Options.EnableOverridingRegistrations = false;