Autofac:将所有 MediatR 处理程序注册为通用类型的内部 类
Autofac: Register all MediatR handlers as inner classes of generic types
上下文
在尝试简化我们的 MediatR 代码的同时,我们为 GetAll、GetSingle 等功能创建了通用 Request/Response/Handler 组合。您可以在下面找到 GetAll 实现
public class GetAll<T> where T : class
{
public class Request : IRequest<Response>
{
}
public class Response
{
public IQueryable<T> All { get; set; }
}
public class Handler : IRequestHandler<Request, Response>
{
private readonly IRepository<T> repository;
public Handler(IRepository<T> repository)
{
this.repository = repository;
}
public Response Handle(Request message)
{
return new Response
{
All = repository.GetAll()
};
}
}
}
问题
我们似乎无法使用 Autofac 一次注册所有 RequestHandler。
我们可以使用以下方法在我们的 Autofac 模块中注册一个特定类型的处理程序:
builder.RegisterGeneric(typeof(GetAll<>.Handler)).AsImplementedInterfaces();
但我们希望对 IRequestHandler<,>
的 所有 实现(而不仅仅是 GetAll 实现)执行此操作。我们已经按照文档中的建议使用 AsClosedTypesOf
函数进行了尝试:
builder.RegisterAssemblyTypes(typeof(GetAll<>.Request).Assembly)
.AsClosedTypesOf(typeof(IRequestHandler<,>));
或
builder.RegisterAssemblyTypes(typeof(GetAll<>.Request).Assembly)
.AsClosedTypesOf(typeof(IRequestHandler<,>))
.AsImplementedInterfaces();
但这会产生以下异常:
Autofac.Core.Registration.ComponentNotRegisteredException: 'The requested service 'MediatR.IRequestHandler<GetAll<T>.Request,GetAll<T>.Response>'
has not been registered. To avoid this exception, either register a component to provide the service, check for service registration using IsRegistered(), or use the ResolveOptional() method to resolve an optional dependency.'
我们在这里做错了什么(或忘记了什么)?
您将无法立即注册它们,因为正如您提到的,您必须将它们中的每一个都注册为泛型。但是,有一种方法可以让您避免手动保留此类处理程序的列表并进行一些反思:
var genericRequestHandlers = typeof(GetAll<>).Assembly
.ExportedTypes
.Where(x => IsGenericRequestHandler(x))
.ToArray();
foreach (var genericRequestHandler in genericRequestHandlers)
{
builder
.RegisterGeneric(genericRequestHandler)
.AsImplementedInterfaces();
}
private static bool IsGenericRequestHandler(Type t)
{
return
t.IsGenericTypeDefinition &&
t.GetInterfaces().Any(i =>
{
return
i.IsGenericType &&
i.GetGenericTypeDefinition() == typeof(IRequestHandler<,>);
});
}
以下是对 IsGenericRequestHandler
中所做检查的解释:
- 类型是泛型类型定义吗?换句话说,我们可以从这个类型定义中构造通用类型吗?
GetAll<T>.Handler
是因为您可以从中构建 GetAll<int>.Handler
、GetAll<string>.Handler
等...
- 该类型是否实现了泛型类型的接口?
GetAll<T>.Handler
实现 IRequestHandler<GetAll<T>.Request, GetAll<T>.Response>
,这是一个通用类型
- 最后,这个接口的泛型类型定义是
IRequestHandler<,>
?在我们的例子中,IRequestHandler<GetAll<T>.Request, GetAll<T>.Response>
的通用类型定义是 IRequestHandler<,>
,因此该类型符合必要的条件。
我希望这是有道理的。
上下文
在尝试简化我们的 MediatR 代码的同时,我们为 GetAll、GetSingle 等功能创建了通用 Request/Response/Handler 组合。您可以在下面找到 GetAll 实现
public class GetAll<T> where T : class
{
public class Request : IRequest<Response>
{
}
public class Response
{
public IQueryable<T> All { get; set; }
}
public class Handler : IRequestHandler<Request, Response>
{
private readonly IRepository<T> repository;
public Handler(IRepository<T> repository)
{
this.repository = repository;
}
public Response Handle(Request message)
{
return new Response
{
All = repository.GetAll()
};
}
}
}
问题
我们似乎无法使用 Autofac 一次注册所有 RequestHandler。
我们可以使用以下方法在我们的 Autofac 模块中注册一个特定类型的处理程序:
builder.RegisterGeneric(typeof(GetAll<>.Handler)).AsImplementedInterfaces();
但我们希望对 IRequestHandler<,>
的 所有 实现(而不仅仅是 GetAll 实现)执行此操作。我们已经按照文档中的建议使用 AsClosedTypesOf
函数进行了尝试:
builder.RegisterAssemblyTypes(typeof(GetAll<>.Request).Assembly)
.AsClosedTypesOf(typeof(IRequestHandler<,>));
或
builder.RegisterAssemblyTypes(typeof(GetAll<>.Request).Assembly)
.AsClosedTypesOf(typeof(IRequestHandler<,>))
.AsImplementedInterfaces();
但这会产生以下异常:
Autofac.Core.Registration.ComponentNotRegisteredException: 'The requested service
'MediatR.IRequestHandler<GetAll<T>.Request,GetAll<T>.Response>'
has not been registered. To avoid this exception, either register a component to provide the service, check for service registration using IsRegistered(), or use the ResolveOptional() method to resolve an optional dependency.'
我们在这里做错了什么(或忘记了什么)?
您将无法立即注册它们,因为正如您提到的,您必须将它们中的每一个都注册为泛型。但是,有一种方法可以让您避免手动保留此类处理程序的列表并进行一些反思:
var genericRequestHandlers = typeof(GetAll<>).Assembly
.ExportedTypes
.Where(x => IsGenericRequestHandler(x))
.ToArray();
foreach (var genericRequestHandler in genericRequestHandlers)
{
builder
.RegisterGeneric(genericRequestHandler)
.AsImplementedInterfaces();
}
private static bool IsGenericRequestHandler(Type t)
{
return
t.IsGenericTypeDefinition &&
t.GetInterfaces().Any(i =>
{
return
i.IsGenericType &&
i.GetGenericTypeDefinition() == typeof(IRequestHandler<,>);
});
}
以下是对 IsGenericRequestHandler
中所做检查的解释:
- 类型是泛型类型定义吗?换句话说,我们可以从这个类型定义中构造通用类型吗?
GetAll<T>.Handler
是因为您可以从中构建GetAll<int>.Handler
、GetAll<string>.Handler
等... - 该类型是否实现了泛型类型的接口?
GetAll<T>.Handler
实现IRequestHandler<GetAll<T>.Request, GetAll<T>.Response>
,这是一个通用类型 - 最后,这个接口的泛型类型定义是
IRequestHandler<,>
?在我们的例子中,IRequestHandler<GetAll<T>.Request, GetAll<T>.Response>
的通用类型定义是IRequestHandler<,>
,因此该类型符合必要的条件。
我希望这是有道理的。