我最近开始使用autofac,从structuremap和lamar迁移出来。使用以前的依赖注入工具,解决所有在给定类型的完整层次结构(所有接口和基类)上关闭的服务似乎相当简单。
例如结构图/拉马尔只需扫描
// ... registry stuff...
Scan(scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.ConnectImplementationsToTypesClosing(typeof(IAnimalFeeder<>));
});
解决问题只是GetAllInstances
呼吁context
:
var openFeederType = typeof(IAnimalFeeder<>);
var animalType = typeof(animal);
var closedType = openFeederType.MakeGenericType(animalType);
var feeders = context.GetAllInstances(closedType) // resolve all feeders for given animal hierarchy
但是, autofac似乎并非如此。除非我遗漏了一些文档和误解方法,否则我似乎需要跳过许多反射调用才能手动确定我的服务类型的类型层次结构是什么,关闭每个发现的类型的泛型,关闭该泛型一个IEnumerable
,最后Resolve
在每个关闭时打一个电话IEnumerable
。
下面是我的方法,我想确保它是有效的,而不是重新实现现有的功能
鉴于我们最喜欢的多态动物示例
public interface IAnimal { }
public class Dog : AbstractAnimal { }
public class Wolf : Dog { }
public class Cat : AbstractAnimal { }
我想发现 ( Resolve
) 所有可以喂食动物的喂食器,定义如下
public interface IAnimalFeeder<in TAnimal> { }
public abstract class AbstractAnimal : IAnimal { }
public class AnimalFeeder : IAnimalFeeder<IAnimal> { }
public class ObjectFeeder : IAnimalFeeder<object> { }
public class DogSnackFeeder : IAnimalFeeder<Dog> { }
public class DogFeeder : IAnimalFeeder<Dog> { }
public class WolfFeeder : IAnimalFeeder<Wolf> { }
public class CatFeeder : IAnimalFeeder<Cat> { }
例如:
- A
DogFeeder
可以同时喂Dog
和Wolf
,但不能Cat
- 可以
ObjectFeeder
喂Dog
,Wolf
,Cat
, 但不能IAnimal
AnimalFeeder
罐头喂食,Dog
,Wolf
,Cat
和IAnimal
- 等等
为了证明这一点,我编写了一个xuintResolve
理论,其中包含基于静态ResolutionHelper
类 ( )的自定义反射ResolveManyClosedGenericsOfTypeHierarchy
来完成我的解决方案
public class GenericResolveTests
{
[Theory]
[InlineData(new[] {typeof(ObjectFeeder), typeof(AnimalFeeder), typeof(DogFeeder), typeof(DogSnackFeeder)}, typeof(Dog))]
[InlineData(new[] {typeof(ObjectFeeder), typeof(AnimalFeeder), typeof(DogFeeder), typeof(DogSnackFeeder), typeof(WolfFeeder)}, typeof(Wolf))]
[InlineData(new[] {typeof(ObjectFeeder), typeof(AnimalFeeder), typeof(CatFeeder)}, typeof(Cat))]
[InlineData(new[] {typeof(ObjectFeeder)}, typeof(object))]
[InlineData(new[] {typeof(AnimalFeeder)}, typeof(IAnimal))]
public void Generic_Resolve_Test(Type[] expectedTypes,
Type closureType)
{
// Arrange
var assembly = Assembly.GetExecutingAssembly();
var builder = new ContainerBuilder();
// Registration
builder.RegisterAssemblyTypes(assembly) // Register sourcing from the given assembly
.PublicOnly() // Only register public classes
.AsClosedTypesOf(typeof(IAnimalFeeder<>)) // Register types that are assignable to a closed instance of the open generic type IAnimalFeeder<>
.AsSelf() // Register types as themselves (explicit concrete types are made available)
.AsImplementedInterfaces(); // Register the type as providing all of its public interfaces as services
var container = builder.Build();
// Act
// Resolution with non-standard autofac Resolve functionality
var result = container.ResolveManyClosedGenericsOfTypeHierarchy(closureType, typeof(IAnimalFeeder<>));
// Assert
Assert.NotNull(result);
var results = result.ToList();
Assert.Equal(expectedTypes.Length, results.Count);
var resultTypes = results.Select(r => r.GetType())
.ToList();
Assert.All(expectedTypes,
expectedType => Assert.Contains(expectedType, resultTypes));
}
}
public static class ResolutionHelper
{
/// <summary>
/// Resolve closed generics of <paramref name="openGenericType" /> based on the type hierarchy of
/// <typeparamref name="TServiceClosure" />
/// </summary>
/// <typeparam name="TServiceClosure">the service closure type</typeparam>
/// <param name="componentContext">the component context used for resolution</param>
/// <param name="openGenericType">the open generic type to close</param>
/// <returns>
/// a collection of closed <see cref="openGenericType" /> on the type hierarchy of
/// <typeparamref name="TServiceClosure" />
/// </returns>
public static IEnumerable<object> ResolveManyClosedGenericsOfTypeHierarchy<TServiceClosure>(this IComponentContext componentContext,
Type openGenericType)
{
return ResolveManyClosedGenericsOfTypeHierarchy(componentContext.Resolve, typeof(TServiceClosure), openGenericType);
}
/// <summary>
/// Resolve closed generics of <paramref name="openGenericType" /> based on the type hierarchy of
/// <typeparamref name="TServiceClosure" />
/// </summary>
/// <typeparam name="TServiceClosure">the service closure type</typeparam>
/// <param name="container">the container used for resolution</param>
/// <param name="openGenericType">the open generic type to close</param>
/// <returns>
/// a collection of closed <see cref="openGenericType" /> on the type hierarchy of
/// <typeparamref name="TServiceClosure" />
/// </returns>
public static IEnumerable<object> ResolveManyClosedGenericsOfTypeHierarchy<TServiceClosure>(this IContainer container,
Type openGenericType)
{
return ResolveManyClosedGenericsOfTypeHierarchy(container.Resolve, typeof(TServiceClosure), openGenericType);
}
/// <summary>
/// Resolve closed generics of <paramref name="openGenericType" /> based on the type hierarchy of
/// <paramref name="serviceClosureType" />
/// </summary>
/// <param name="componentContext">the component context used for resolution</param>
/// <param name="serviceClosureType">the service closure type</param>
/// <param name="openGenericType">the open generic type to close</param>
/// <returns>
/// a collection of closed <see cref="openGenericType" /> on the type hierarchy of
/// <paramref name="serviceClosureType" />
/// </returns>
public static IEnumerable<object> ResolveManyClosedGenericsOfTypeHierarchy(this IComponentContext componentContext,
Type serviceClosureType,
Type openGenericType)
{
return ResolveManyClosedGenericsOfTypeHierarchy(componentContext.Resolve, serviceClosureType, openGenericType);
}
/// <summary>
/// Resolve closed generics of <paramref name="openGenericType" /> based on the type hierarchy of
/// <paramref name="serviceClosureType" />
/// </summary>
/// <param name="container">the container used for resolution</param>
/// <param name="serviceClosureType">the service closure type</param>
/// <param name="openGenericType">the open generic type to close</param>
/// <returns>
/// a collection of closed <see cref="openGenericType" /> on the type hierarchy of
/// <paramref name="serviceClosureType" />
/// </returns>
public static IEnumerable<object> ResolveManyClosedGenericsOfTypeHierarchy(this IContainer container,
Type serviceClosureType,
Type openGenericType)
{
return ResolveManyClosedGenericsOfTypeHierarchy(container.Resolve, serviceClosureType, openGenericType);
}
/// <summary>
/// Resolve closed generics of <paramref name="openGenericType" /> based on the type hierarchy of
/// <paramref name="serviceClosureType" />
/// </summary>
/// <param name="resolver">
/// a resolution <see cref="Func{TInput, TResult}" /> that resolves <see cref="Type" /> input into
/// an <see cref="object" />
/// </param>
/// <param name="serviceClosureType">the service closure type</param>
/// <param name="openGenericType">the open generic type to close</param>
/// <returns>
/// a collection of closed <see cref="openGenericType" /> on the type hierarchy of
/// <paramref name="serviceClosureType" />
/// </returns>
public static IEnumerable<object> ResolveManyClosedGenericsOfTypeHierarchy(Func<Type, object> resolver,
Type serviceClosureType,
Type openGenericType)
{
if (resolver == null)
{
throw new ArgumentNullException(nameof(resolver));
}
if (serviceClosureType == null)
{
throw new ArgumentNullException(nameof(serviceClosureType));
}
if (openGenericType == null)
{
throw new ArgumentNullException(nameof(openGenericType));
}
if (!openGenericType.IsGenericTypeDefinition)
{
throw new ArgumentException("type must be a generic type definition", nameof(openGenericType));
}
var typesToResolve = GetAllClosedGenericOfTypeHierarchy(serviceClosureType, openGenericType);
return _()
.SelectMany(o => o);
IEnumerable<IEnumerable<object>> _()
{
foreach (var type in typesToResolve)
{
yield return (IEnumerable<object>) resolver(type);
}
}
}
/// <summary>
/// Create a closed generic type of <see cref="openGenericType" /> for each <see cref="type" /> and its implementing
/// <see langwod="interface" /> and base class
/// </summary>
/// <param name="type">the type to find linage of</param>
/// <param name="openGenericType">the open generic to make closed types of</param>
/// <returns>a collection of closed generic types</returns>
private static IEnumerable<Type> GetAllClosedGenericOfTypeHierarchy(Type type,
Type openGenericType)
{
return _();
IEnumerable<Type> _()
{
foreach (var selfAndSuper in GetSelfAndSupers(type))
{
var closedFeederType = openGenericType.MakeGenericType(selfAndSuper);
yield return typeof(IEnumerable<>).MakeGenericType(closedFeederType);
}
}
}
/// <summary>
/// Given a <see cref="Type" /> <paramref name="inputType" /> return a collection of <paramref name="inputType" />, all
/// of <paramref name="inputType" />'s interfaces, and all of its base classes
/// </summary>
/// <param name="inputType">the type to determine linage of</param>
/// <returns>
/// a collection of type including <paramref name="inputType" />, all of its interfaces, and all of its base
/// classes
/// </returns>
private static IEnumerable<Type> GetSelfAndSupers(Type inputType)
{
return _()
.Distinct();
IEnumerable<Type> _()
{
// return self
yield return inputType;
// return interfaces
foreach (var t in inputType.GetInterfaces())
{
yield return t;
}
// return base types
var baseType = inputType.BaseType;
while (baseType != null)
{
yield return baseType;
baseType = baseType.BaseType;
}
}
}
}
我是否跳了篮球,我不应该让这发生?
只是为了保持一致性,这是我用于 DTO 验证的方法,其中动物是各种类型的 DTO,而“饲养者”是 DTO 验证的明确单元。