如何使用 Unity 将通用接口自动注册到该接口的非通用实现
How to auto-register a generic interface to non generic implementations of that interface with Unity
我的应用程序中有一个模式。
public interface ICommandService<TCommand>
{
public void Execute(TCommand command);
}
此接口由许多不同的服务实现 类。这是一个例子:
public class UpdateWidgetService : ICommandService<UpdateWidget>
{
public void Execute(UpdateWidget command)
{
...
}
}
public class UpdateWidget
{
...data
}
控制器和其他 类:
依赖这些服务 类
public class WidgetController
{
private readonly ICommandService<UpdateWidget> service;
public WidgetController(ICommandService<UpdateWidget> service)
{
this.service = service;
}
public ActionResult UpdateWidget(UpdateWidgetViewModel viewModel)
{
...
UpdateWidget command = viewModel.Command;
this.service.Execute(command);
}
}
如果我要手动注册所有类型映射,我认为这样的方法可行:
container.RegisterType(typeof(ICommandService<UpdateWidget>), typeof(UpdateWidgetService));
container.RegisterType(typeof(ICommandService<CreateWidget>), typeof(CreateWidgetService));
container.RegisterType(typeof(ICommandService<DeleteGizmo>), typeof(DeleteGizmoService));
container.RegisterType(typeof(ICommandService<LaunchNuclearStrike>), typeof(LaunchNuclearStrikeService));
但是如您所见,有一个约定,其中所有实现 ICommandService<TCommand>
的 类 都被命名为 [TCommand]Service
,我真的很想按照约定设置某种自动注册.到目前为止,我没有尝试过任何工作。我的问题似乎主要与泛型类型参数有关。这是我目前所拥有的:
var container = new UnityContainer();
var assembly = Assembly.GetExecutingAssembly();
var commandServices = assembly.GetTypes().Where(type => !type.IsAbstract && type.Name.EndsWith("Service") && type.GetInterfaces().Single().Name.StartsWith("ICommandService")).ToList();
foreach (var service in commandServices)
{
var serviceInterface = service.GetInterfaces().Single();
var genericTypeArgument = serviceInterface.GenericTypeArguments.Single();
container.RegisterType(typeof(ICommandService<genericTypeArgument>), service);
}
我收到以下错误:“'genericTypeArgument' 是一个变量,但像类型一样使用”。
看起来你的 commandServices 是类型,所以
foreach (var service in commandServices
{
foreach (var interfaceType in service.GetInterfaces())
{
container.RegisterType(interfaceType, service);
}
}
试试这个:
var container = new UnityContainer();
var assembly = Assembly.GetExecutingAssembly();
var commandServices = assembly.GetTypes().Where(type => !type.IsAbstract && type.Name.EndsWith("Service") && type.GetInterfaces().Single().Name.StartsWith("ICommandService")).ToList();
foreach (var service in commandServices)
{
var serviceInterface = service.GetInterfaces().Single();
var genericTypeArgument = serviceInterface.GenericTypeArguments.Single();
Type type = Type.GetType($"{genericTypeArgument.UnderlyingSystemType.ToString()}, {genericTypeArgument.Namespace}");
container.RegisterType(typeof(ICommandService<>), type, new TransientLifetimeManager());
}
这是我在各种项目中用来解决这个问题的解决方案,最常见的是领域驱动事件处理程序(如 IHandlerOf<DomainEvent>
)或插件系统。
首先,我指定要包含哪些程序集以进行类型发现。
var assemblies = RegistrationByConvention.FromSpecificAssemblies(
typeof(ClassInAssembly1).Assembly,
typeof(ClassInAssembly2).Assembly,
typeof(ClassInAssembly3).Assembly);
然后我收集所有非抽象类型、非值类型且可见的类型。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
public class RegistrationByConvention
{
public static IEnumerable<Type> FromSpecificAssemblies(params Assembly[] assemblies)
{
return GetTypes(assemblies);
}
private static IEnumerable<Type> GetTypes(IEnumerable<Assembly> assemblies)
{
return assemblies.SelectMany(assembly =>
{
try
{
return GetTypes(assembly.DefinedTypes);
}
catch (ReflectionTypeLoadException ex)
{
return GetTypes(ex.Types.TakeWhile(x => x != null).Select(x => x.GetTypeInfo()));
}
});
}
private static IEnumerable<Type> GetTypes(IEnumerable<TypeInfo> typeInfos)
{
return typeInfos
.Where(x => x.IsClass & !x.IsAbstract && !x.IsValueType && x.IsVisible)
.Select(ti => ti.AsType());
}
}
最后,对于像您这样的通用实现,我使用 IsAssignableFrom
和 MakeGenericType
。我建议为 UpdateWidget
和其他小部件分配一个基础 class 或接口,以便随后可以发现。
foreach (var matchingType in assemblies.Where((type) => { return typeof(BaseWidget).IsAssignableFrom(type); }))
{
container.RegisterTypes(
assemblies.Where((type) =>
{
return typeof(ICommandService<>).MakeGenericType(matchingType).IsAssignableFrom(type);
}),
WithMappings.FromAllInterfaces,
WithName.TypeName,
WithLifetime.Transient);
}
如果您只想解决您的错误并使用现有代码,您将需要像 typeof(ICommandService<>).MakeGenericType(matchingType)
中那样使用 Type.MakeGenericType
。
我的应用程序中有一个模式。
public interface ICommandService<TCommand>
{
public void Execute(TCommand command);
}
此接口由许多不同的服务实现 类。这是一个例子:
public class UpdateWidgetService : ICommandService<UpdateWidget>
{
public void Execute(UpdateWidget command)
{
...
}
}
public class UpdateWidget
{
...data
}
控制器和其他 类:
依赖这些服务 类public class WidgetController
{
private readonly ICommandService<UpdateWidget> service;
public WidgetController(ICommandService<UpdateWidget> service)
{
this.service = service;
}
public ActionResult UpdateWidget(UpdateWidgetViewModel viewModel)
{
...
UpdateWidget command = viewModel.Command;
this.service.Execute(command);
}
}
如果我要手动注册所有类型映射,我认为这样的方法可行:
container.RegisterType(typeof(ICommandService<UpdateWidget>), typeof(UpdateWidgetService));
container.RegisterType(typeof(ICommandService<CreateWidget>), typeof(CreateWidgetService));
container.RegisterType(typeof(ICommandService<DeleteGizmo>), typeof(DeleteGizmoService));
container.RegisterType(typeof(ICommandService<LaunchNuclearStrike>), typeof(LaunchNuclearStrikeService));
但是如您所见,有一个约定,其中所有实现 ICommandService<TCommand>
的 类 都被命名为 [TCommand]Service
,我真的很想按照约定设置某种自动注册.到目前为止,我没有尝试过任何工作。我的问题似乎主要与泛型类型参数有关。这是我目前所拥有的:
var container = new UnityContainer();
var assembly = Assembly.GetExecutingAssembly();
var commandServices = assembly.GetTypes().Where(type => !type.IsAbstract && type.Name.EndsWith("Service") && type.GetInterfaces().Single().Name.StartsWith("ICommandService")).ToList();
foreach (var service in commandServices)
{
var serviceInterface = service.GetInterfaces().Single();
var genericTypeArgument = serviceInterface.GenericTypeArguments.Single();
container.RegisterType(typeof(ICommandService<genericTypeArgument>), service);
}
我收到以下错误:“'genericTypeArgument' 是一个变量,但像类型一样使用”。
看起来你的 commandServices 是类型,所以
foreach (var service in commandServices
{
foreach (var interfaceType in service.GetInterfaces())
{
container.RegisterType(interfaceType, service);
}
}
试试这个:
var container = new UnityContainer();
var assembly = Assembly.GetExecutingAssembly();
var commandServices = assembly.GetTypes().Where(type => !type.IsAbstract && type.Name.EndsWith("Service") && type.GetInterfaces().Single().Name.StartsWith("ICommandService")).ToList();
foreach (var service in commandServices)
{
var serviceInterface = service.GetInterfaces().Single();
var genericTypeArgument = serviceInterface.GenericTypeArguments.Single();
Type type = Type.GetType($"{genericTypeArgument.UnderlyingSystemType.ToString()}, {genericTypeArgument.Namespace}");
container.RegisterType(typeof(ICommandService<>), type, new TransientLifetimeManager());
}
这是我在各种项目中用来解决这个问题的解决方案,最常见的是领域驱动事件处理程序(如 IHandlerOf<DomainEvent>
)或插件系统。
首先,我指定要包含哪些程序集以进行类型发现。
var assemblies = RegistrationByConvention.FromSpecificAssemblies(
typeof(ClassInAssembly1).Assembly,
typeof(ClassInAssembly2).Assembly,
typeof(ClassInAssembly3).Assembly);
然后我收集所有非抽象类型、非值类型且可见的类型。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
public class RegistrationByConvention
{
public static IEnumerable<Type> FromSpecificAssemblies(params Assembly[] assemblies)
{
return GetTypes(assemblies);
}
private static IEnumerable<Type> GetTypes(IEnumerable<Assembly> assemblies)
{
return assemblies.SelectMany(assembly =>
{
try
{
return GetTypes(assembly.DefinedTypes);
}
catch (ReflectionTypeLoadException ex)
{
return GetTypes(ex.Types.TakeWhile(x => x != null).Select(x => x.GetTypeInfo()));
}
});
}
private static IEnumerable<Type> GetTypes(IEnumerable<TypeInfo> typeInfos)
{
return typeInfos
.Where(x => x.IsClass & !x.IsAbstract && !x.IsValueType && x.IsVisible)
.Select(ti => ti.AsType());
}
}
最后,对于像您这样的通用实现,我使用 IsAssignableFrom
和 MakeGenericType
。我建议为 UpdateWidget
和其他小部件分配一个基础 class 或接口,以便随后可以发现。
foreach (var matchingType in assemblies.Where((type) => { return typeof(BaseWidget).IsAssignableFrom(type); }))
{
container.RegisterTypes(
assemblies.Where((type) =>
{
return typeof(ICommandService<>).MakeGenericType(matchingType).IsAssignableFrom(type);
}),
WithMappings.FromAllInterfaces,
WithName.TypeName,
WithLifetime.Transient);
}
如果您只想解决您的错误并使用现有代码,您将需要像 typeof(ICommandService<>).MakeGenericType(matchingType)
中那样使用 Type.MakeGenericType
。