c# - 如何使用 Unity 将通用接口(interface)自动注册到该接口(interface)的非通用实现

标签 c# generics reflection unity-container ioc-container

我的应用中有一个模式。

public interface ICommandService<TCommand>
{
    public void Execute(TCommand command);
}

此接口(interface)由许多不同的服务类实现。这是一个例子:

public class UpdateWidgetService : ICommandService<UpdateWidget>
{
    public void Execute(UpdateWidget command)
    {
        ...
    }

}

public class UpdateWidget
{
    ...data
}

这些服务类在 Controller 和其他类中被依赖:

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' 是一个变量,但用作类型”。

最佳答案

这是我在各种项目中用来解决此问题的解决方案,最常见的是域驱动的事件处理程序(如 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());
    }
}

最后,对于像你这样的通用实现,我使用 IsAssignableFromMakeGenericType .我建议将基类或接口(interface)分配给 UpdateWidget和其他小部件,以便随后被发现。

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);
}

如果您只想解决错误并使用现有代码,您将需要使用 Type.MakeGenericTypetypeof(ICommandService<>).MakeGenericType(matchingType) .

关于c# - 如何使用 Unity 将通用接口(interface)自动注册到该接口(interface)的非通用实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65904431/

相关文章:

c# - if 语句中的数据绑定(bind)

c# - ASPNETZERO - 数据表问题

c# - C# 中的通用方法不调用最严格的重载

scala - 如何通过 Scala 反射获得可用的隐式?

c# - 如何使用反射来检索属性?

c# - 手动复位事件,自动复位事件

c# - System.Windows.Forms.Timer 和 MessageBox

java - 泛型与遗留代码 : 相比有何优势

c++ - 在 C++ 中,我们可以为每个整数创建一个类吗?

java - 一个类中有多个具有相同参数类型的方法