2

嗨,我正在尝试实现我在本文中找到的用于简单注入器的扩展方法,因为它不支持开箱即用的特定构造函数的注册。

根据文章,我需要使用假代表注册服务,但由于我使用的是开放式泛型,所以我不能这样做。

到目前为止,这是我所做的代码:

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

1 回答 1

4

因为它不支持开箱即用的特定构造函数的注册

Simple Injector 不支持这个是有充分理由的。您应该更喜欢让您的组件有一个公共构造函数。拥有多个构造函数被认为是一种反模式

如果由于某种原因您不能遵循此建议,这里有一些替代方案。

一个简单的解决方案是在您的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<>));

如果您只处理一种类型,这是最实用的解决方案。如果您有许多具有多个构造函数的类型,则覆盖容器的构造函数解析行为会更好。您可以为开放的泛型类型编写自定义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>());

但现在麻烦开始了。传入的构造函数是 from List<int>,但我们应该能够得到 any 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>>();
于 2013-07-28T15:19:44.723 回答