Unity 3.5:通过其开放的通用接口检索具体实现

Unity 3.5: Retrieve concrete implementation by its open generic interface

此博客 post 描述了存储库模式的一个很好的替代方案。

https://cuttingedge.it/blogs/steven/pivot/entry.php?id=92

作者建议使用命令和查询而不是存储库。特定博客 post 描述了 .NET/C#.

中查询部分的实现

查询和查询处理程序有两个接口:

public interface IQuery<TResult>
{
}

public interface IQueryHandler<TQuery, TResult> where TQuery : IQuery<TResult>
{
    TResult Handle(TQuery query);
}

他还为每一个都提供了一个例子:

public class FindUsersBySearchTextQuery : IQuery<User[]>
{
    public string SearchText { get; set; }

    public bool IncludeInactiveUsers { get; set; }
}

public class FindUsersBySearchTextQueryHandler
    : IQueryHandler<FindUsersBySearchTextQuery, User[]>
{
    private readonly NorthwindUnitOfWork db;

    public FindUsersBySearchTextQueryHandler(NorthwindUnitOfWork db)
    {
        this.db = db;
    }

    public User[] Handle(FindUsersBySearchTextQuery query)
    {
        return (
            from user in this.db.Users
            where user.Name.Contains(query.SearchText)
            select user)
            .ToArray();
    }
}

查询处理程序可以作为构造函数参数提供给 MVC 控制器。

public class UserController : Controller
{
    IQueryHandler<FindUsersBySearchTextQuery, User[]> handler;

    public UserController(IQueryHandler<FindUsersBySearchTextQuery, User[]> handler)
    {
        this.handler = handler;
    }

    public View SearchUsers(string searchString)
    {
        var query = new FindUsersBySearchTextQuery
        {
            SearchText = searchString,
            IncludeInactiveUsers = false
        };

        User[] users = this.handler.Handle(query);

        return this.View(users);
    }
}

作者使用依赖注入容器Simple Injector一次注册所有的IQueryHandler:

container.RegisterManyForOpenGeneric(
    typeof(IQueryHandler<,>),
    typeof(IQueryHandler<,>).Assembly);

我的问题是:如何在 Unity 中执行最后一条语句?

我正在使用 Unity 3.5。

我可以像这样手动注册每个 QueryHandler:

container.RegisterType<IQueryHandler<FindUsersBySearchTextQuery, User[]>, 
                       FindUsersBySearchTextQueryHandler>();

这很好用,但我不想每次出现新的 QueryHandler 时都添加新的映射。我想使用一个约定来设置所有映射,其中包括未来的 QueryHandler。 Unity 3.5 提供了一个基于约定的注册工作流程,但我无法让它适用于我的案例。我试过了,但不幸的是它没有生成有问题的映射。

container.RegisterTypes(
    AllClasses.FromLoadedAssemblies(),
    WithMappings.FromMatchingInterface,
    WithName.Default);

如果您要有许多 IQueryHandler<,> 的特定实现而不是一个通用版本,那么您不能使用开放通用来注册。但是您可以使用反射来查找所有实现并分别注册它们。 (这就是 RegisterTypes 在幕后为您所做的)。

您已接近尝试调用问题中的 RegisterTypes,但您使用了 WithMappings.FromMatchingInterface。这将只注册 classes 及其接口,这些接口符合 MyClass : IMyClass 的命名约定(在 class 名称前加上 'I')。如果您改为使用 WithMappings.FromAllInterfaces 注册,您将获得所需的注册。

container.RegisterTypes(
    AllClasses.FromLoadedAssemblies(),
    WithMappings.FromAllInterfaces,
    WithName.Default);

如果您只需要注册那些 classes,您可以将 classes 过滤为仅那些实现了您想要的接口的...

public static class EnumerableTypeExtensions
{
    public static IEnumerable<Type> WhichImplementsInterface<T>
        (this IEnumerable<Type> types)
    {
        return types.WhichImplementsInterface(typeof (T));
    }

    public static IEnumerable<Type> WhichImplementsInterface
        (this IEnumerable<Type> types, Type interfaceType)
    {
        return types.WhichImplementsInterface(interfaceType.Name);
    }

    public static IEnumerable<Type> WhichImplementsInterface
        (this IEnumerable<Type> types, string interfaceTypeName)
    {
        return types.Where(t => t.GetInterface(interfaceTypeName) != null);
    }
}

然后你可以像这样使用这些过滤器...

container.RegisterTypes(
    AllClasses.FromLoadedAssemblies().WhichImplementsInterface(typeof(IQueryHandler<,>)),
    WithMappings.FromAllInterfaces,
    WithName.Default);