您好我正在尝试实现我在this article中为简单注入器找到的扩展方法,因为它不支持开箱即用的特定构造函数的注册。
根据文章,我需要使用假代理注册服务,但由于我使用的是开放式通用,我不能这样做。
到目前为止,这是我完成的代码:
public sealed class ConstructorSelector : IConstructorSelector
{
public static readonly IConstructorSelector MostParameters =
new ConstructorSelector(type => type.GetConstructors()
.OrderByDescending(c => c.GetParameters().Length).First());
public static readonly IConstructorSelector LeastParameters =
new ConstructorSelector(type => type.GetConstructors()
.OrderBy(c => c.GetParameters().Length).First());
private readonly Func<Type, ConstructorInfo> _selector;
public ConstructorSelector(Func<Type, ConstructorInfo> selector)
{
_selector = selector;
}
public ConstructorInfo GetConstructor(Type type)
{
return _selector(type);
}
}
public interface IConstructorSelector
{
ConstructorInfo GetConstructor(Type type);
}
public static class SimpleInjectorExtesions
{
public static void RegisterOpenGeneric(this Container container,
Type serviceType,
Type implementationtype, IConstructorSelector selector)
{
//TODO: register the service with a fake delegate
container.ExpressionBuilt += (sender, e) =>
{
if (e.RegisteredServiceType != serviceType) return;
var ctor = selector.GetConstructor(implementationtype);
var parameters =
from p in ctor.GetParameters()
select container.GetRegistration(p.ParameterType, true)
.BuildExpression();
e.Expression = Expression.New(ctor, parameters);
};
}
}
答案 0 :(得分:4)
因为它不支持注册特定的构造函数 的方框
Simple Injector不支持这个有充分理由。您应该更喜欢让组件具有单个公共构造函数。 multiple constructors is considered to be an anti-pattern。
如果由于某种原因你不能遵循这个建议,这里有一些选择。
一个简单的解决方案是在Composition Root内创建一个继承自开放泛型类型的新类型,并定义一个单独的构造函数。您可以注册该类型:
// new sub type
private sealed class SingleCtorOpenType<T> : OpenType<T>
{
public SingleCtorOpenType(IDep1 dep1, IDep2 dep2)
: base(dep1, dep2) { }
}
// registration
container.RegisterOpenGeneric(
typeof(IOpenType<>),
typeof(SingleCtorOpenType<>));
如果您只处理一种类型,这是最实用的解决方案。如果您有许多具有多个构造函数的类型,则覆盖容器的constructor resolution behavior会更好。您可以为开放泛型类型编写自定义IConstructorResolutionBehavior
实现:
public class SpecialConstructorResolutionBehavior
: IConstructorResolutionBehavior
{
private IConstructorResolutionBehavior original;
public SpecialConstructorResolutionBehavior(
IConstructorResolutionBehavior original)
{
this.original = original;
}
public ConstructorInfo GetConstructor(Type serviceType,
Type implementationType)
{
if (serviceType.IsGenericType &&
serviceType.GetGenericTypeDefinition() == typeof(IOpenType<>))
{
// do alternative constructor selection here for open types.
// For instance:
return (
from ctor in implementationType.GetConstructors()
orderby ctor.GetParameters().Length descending
select ctor)
.First();
}
// fall back to default behavior
return original.GetConstructor(serviceType, implementationType);
}
}
您可以按如下方式注册:
container.Options.ConstructorResolutionBehavior =
new SpecialConstructorResolutionBehavior(
container.Options.ConstructorResolutionBehavior);
<强>更新强>
如果要为扩展方法提供Expression
以强制选择特定构造函数,可以创建一个超出Expression<Func<object>>
的扩展方法,为您提取NewExpression
像这样:
public static void RegisterOpenGeneric(this Container container,
Type openGenericServiceType,
Type openGenericImplementation,
Expression<Func<object>> constructorSelector)
{
var constructor =
((NewExpression)constructorSelector.Body).Constructor;
}
您可以按如下方式调用此扩展方法:
container.RegisterOpenGeneric(typeof(IList<>), typeof(List<>),
constructorSelector: () => new List<int>());
但现在麻烦开始了。传入的构造函数来自List<int>
,但我们应该能够获得任何List<T>
,因此您必须将List<int>.ctor()
转换为您要解析的类型的构造函数。据我所知,.NET缺少方便的辅助方法,如ConstructorInfo.GetGenericMethodDefinition()
和ConstructorInfo.MakeGenericMethod()
重载,因此您需要进行一些查询和摆弄才能找到正确的构造函数。
如果将此模型与IConstructorResolutionBehavior
扩展性机制集成,可能是最简单的,因为RegisterOpenGeneric
方法调用该接口。
所以你的解决方案看起来像这样:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using SimpleInjector;
using SimpleInjector.Advanced;
using SimpleInjector.Extensions;
public static class OpenGenericConstructorExtensions
{
public static void EnableOpenGenericConstructorSelection(
this ContainerOptions options)
{
var selectors = new List<Func<Type, ConstructorInfo>>();
options.ConstructorResolutionBehavior =
new ConstructorResolutionBehavior(
options.ConstructorResolutionBehavior, selectors);
options.Container.SetItem("selectors", selectors);
}
public static void RegisterOpenGeneric(this Container container,
Type openGenericServiceType,
Type openGenericImplementation,
Expression<Func<object>> constructorSelector)
{
var selectors = (List<Func<Type, ConstructorInfo>>)
container.GetItem("selectors");
if (selectors == null) throw new InvalidOperationException(
"call Options.EnableOpenGenericConstructorSelection first.");
var constructor =
((NewExpression)constructorSelector.Body).Constructor;
selectors.Add(type =>
{
if (type == openGenericImplementation || (type.IsGenericType &&
type.GetGenericTypeDefinition() == openGenericImplementation))
{
return GetConstructorForType(type, constructor);
}
return null;
});
container.RegisterOpenGeneric(openGenericServiceType,
openGenericImplementation);
}
private static ConstructorInfo GetConstructorForType(
Type closedImplementationType,
ConstructorInfo closedConstructor)
{
var parameters = closedConstructor.GetParameters();
var constructors =
from ctor in closedImplementationType.GetConstructors()
where ctor.GetParameters().Length == parameters.Length
let parameterPairs = ctor.GetParameters().Zip(parameters, (p1, p2) =>
new { left = p1.ParameterType, right = p2.ParameterType })
where parameterPairs.All(pair => pair.left == pair.right ||
(pair.left.IsGenericType && pair.right.IsGenericType &&
pair.left.GetGenericTypeDefinition() == pair.right.GetGenericTypeDefinition()))
select ctor;
return constructors.Single();
}
private sealed class ConstructorResolutionBehavior
: IConstructorResolutionBehavior
{
private readonly IConstructorResolutionBehavior original;
private readonly List<Func<Type, ConstructorInfo>> constructorSelectors;
public ConstructorResolutionBehavior(
IConstructorResolutionBehavior original,
List<Func<Type, ConstructorInfo>> constructorSelectors)
{
this.original = original;
this.constructorSelectors = constructorSelectors;
}
public ConstructorInfo GetConstructor(Type serviceType,
Type implementationType)
{
var constructors =
from selector in this.constructorSelectors
let constructor = selector(implementationType)
where constructor != null
select constructor;
return constructors.FirstOrDefault() ??
this.original.GetConstructor(serviceType,
implementationType);
}
}
}
由于需要自定义IConstructorResolutionBehavior
实施,因此您必须始终调用上面示例中定义的EnableOpenGenericConstructorSelection
。这是代码的使用方式:
var container = new Container();
var container = new Container();
container.Options.EnableOpenGenericConstructorSelection();
container.RegisterOpenGeneric(typeof(IList<>), typeof(List<>),
constructorSelector: () => new List<int>());
var list = container.GetInstance<IList<string>>();
var listOfObject = container.GetInstance<IList<object>>();