统一装饰拓失败,多个实现多个

2023-09-09 21:12:39 作者:一纸愁肠

我一直在努力奋斗着这个问题了几天,我仍然不知道如何解决这个问题。

I've been struggling with this problem for a couple days, and I still am not sure how to solve it.

我创建了一个容器扩展的统一容器,使我能够在容器中轻松地注册装饰类。这是一个实现我现在有,这几乎是相同的所述一个中的这个文章:

I've created a container extension for the Unity Container to enable me to easily register decorator classes in the container. This is the implementation I currently have, which is almost identical to the one in this article:

public class DecoratorExtension : UnityContainerExtension
{
    private int m_order;
    private Dictionary<Type, IList<DecoratorRegistration>> m_typeStacks;

    protected override void Initialize()
    {
        m_typeStacks = new Dictionary<Type, IList<DecoratorRegistration>>();
        Context.Registering += AddRegistration;
        Context.Strategies.Add(new DecoratorBuildStrategy(m_typeStacks), UnityBuildStage.PreCreation);
    }

    private void AddRegistration(object _sender, RegisterEventArgs _e)
    {
        if (_e.TypeFrom == null || !_e.TypeFrom.IsInterface)
            return;

        GetStack(_e.TypeFrom)
            .Add(new DecoratorRegistration {Order = m_order++, Type = _e.TypeTo});
    }

    private IList<DecoratorRegistration> GetStack(Type _type)
    {
        if (!m_typeStacks.ContainsKey(_type))
            m_typeStacks.Add(_type, new List<DecoratorRegistration>());

        return m_typeStacks[_type];
    }
}

这样做是使用列表对每种类型,存储所有类型登记为同一目标类型,这样我可以重新组装时解决被调用时,使用此版本策略:

What this does is use a list for each type, to store all type registrations for the same target type, so that I can reassemble it when Resolve is called, using this build strategy:

internal class DecoratorBuildStrategy : BuilderStrategy
{
    private readonly Dictionary<Type, IList<DecoratorRegistration>> m_typeStacks;

    internal DecoratorBuildStrategy(Dictionary<Type, IList<DecoratorRegistration>> _typeStacks)
    {
        m_typeStacks = _typeStacks;
    }

    public override void PreBuildUp(IBuilderContext _context)
    {
        var key = _context.OriginalBuildKey;
        if (_context.GetOverriddenResolver(key.Type) != null)
            return;

        // Only interfaces can use decorators.
        if (!key.Type.IsInterface)
            return;

        // Gets the list of types required to build the 'decorated' instance.
        // The list is reversed so that the least dependent types are built first.
        var decoratorTypes = GetDecoratorTypes(key.Type).Reverse().ToList();
        if (!decoratorTypes.Any())
            return;

        object value = null;
        foreach (var type in decoratorTypes)
        {
            Type typeToBuild = type;
            if (typeToBuild.IsGenericTypeDefinition)
            {
                Type[] genericArgumentTypes = key.Type.GetGenericArguments();
                typeToBuild = typeToBuild.MakeGenericType(genericArgumentTypes);
            }

            value = _context.NewBuildUp(new NamedTypeBuildKey(typeToBuild, key.Name));

            // An Override is created so that in the next BuildUp the already 
            // built object gets used instead of doing the BuildUp again and 
            // entering an infinite loop
            _context.AddResolverOverrides(new DependencyOverride(key.Type, value));
        }

        _context.Existing = value;
        _context.BuildComplete = true;
    }

    private IEnumerable<Type> GetDecoratorTypes(Type _type)
    {
        var typeList = m_typeStacks.GetValueOrDefault(_type) ?? new List<DecoratorRegistration>(0);
        if (!_type.IsGenericType)
            return typeList.Select(_reg => _reg.Type);

        // If the type is a generic type, we need to get all open generic registrations
        // alongside the closed ones
        var openGenericList = m_typeStacks
                .GetValueOrDefault(_type.GetGenericTypeDefinition()) ?? 
                new List<DecoratorRegistration>(0);

        // The final result is an ordered concatenation of the closed and open registrations 
        // that should be used for the type
        return typeList
            .Concat(openGenericList)
            .OrderBy(_registration => _registration.Order)
            .Select(_reg => _reg.Type);
    }
}

这是其中DecoratorRegistration模型被使用。这仅仅是一个对类型/ int值,重新presents登记的顺序。我创建了这是能够混合开放和正确关闭通用注册:

This is where the DecoratorRegistration model is used. It is just a pair of type/int that represents the order of the registration. I created this to be able to mix open and closed generic registrations correctly:

internal struct DecoratorRegistration
{
    public int Order { get; set; }
    public Type Type { get; set; }
}

这可以创造奇迹的大部分。这个问题开始时,我不得不说实现了两个接口,其中一个被装饰一类,而另一个没有。

This works wonders for the most part. The problem started when I had a class that implemented two interfaces, one which was decorated, and one that wasn't.

这是目前的测试情况下,我努力使工作:

This is the current test case I'm trying to make work:

private interface IAny<T> {}
private interface IAnotherInterface {}
private class Base<T> : IAnotherInterface, IAny<T> {}   
private class Decorator1<T> : IAny<T>
{
    internal readonly IAny<T> Decorated;

    public Decorator1(IAny<T> _decorated)
    {
        Decorated = _decorated;
    }
}

[TestMethod]
public void DecoratorExtensionDoesNotInterfereWithNormalRegistrations()
{
    // Arrange
    var container = new UnityContainer()
        .AddNewExtension<DecoratorExtension>()
        .RegisterType<Base<string>>(new ContainerControlledLifetimeManager())
        .RegisterType<IAny<string>, Decorator1<string>>()
        .RegisterType<IAny<string>, Base<string>>()
        .RegisterType<IAnotherInterface, Base<string>>();

    // Act
    var decorated = container.Resolve<IAny<string>>();
    var normal = container.Resolve<IAnotherInterface>();
    var anotherDecorated = container.Resolve<IAny<string>>();
    var anotherNormal = container.Resolve<IAnotherInterface>();

    // Assert
    Assert.IsInstanceOfType(normal, typeof (IAnotherInterface));
    Assert.IsInstanceOfType(decorated, typeof (Decorator1<string>));
    Assert.AreSame(normal, anotherNormal);
    Assert.AreSame(decorated, anotherDecorated);
}

这个测试应该让我的意图明确。我想单身​​类,但在第一次调用来解决,对于任何一个 IAnotherInterface IANY&LT;字符串&GT; 的结果以后每调用返回同样的事情。因此,我得到一个异常:

This test should make my intent clear. I wanted singleton classes, but the first call to Resolve, for either IAnotherInterface or IAny<string> results in every subsequent call to return the same thing. Thus, I get an exception:

System.InvalidCastException: Unable to cast object of type 'Decorator1`1[System.String]' to type 'IAnotherInterface'.

在这条线:

    var normal = container.Resolve<IAnotherInterface>();

我不知道这里做什么。我不得不暂时关闭在我们的项目单身,使这可能发挥作用的。我想的是,基地&LT;字符串&GT; 实例是sintleton,但是当我请求 IANY&LT;字符串&GT; 它会创建一个相同的基本被装饰一新的实例。

I'm not sure what to do here. I had to temporarily disable singletons in our project so that this could work as intended. What I wanted is that the Base<string> instance was a sintleton, but when I requested a IAny<string> it would create a NEW instance with the SAME base being decorated.

这是仍在使用.NET 4.0中,所以我坚持使用Unity 2.1位置(不应该在这种情况下,不管虽然)。

This is still using .Net 4.0, so I'm stuck with Unity 2.1 here (shouldn't matter in this case though).

推荐答案

这已经有一段时间,因为我已经解决了这一点,所以我想,这将是很好的复制我从兰迪·利维接到了EntLib团队在这里的答案

It's been a while since I've solved this, so I figured that it would be good to replicate the answer I got from Randy Levy from the EntLib team here.

这基本上可以归结为我所用注册的装饰实例构建的关键。用我的code,实例实际上与基类类型注册,而我需要与实际装饰类注册。

It basically boils down to the build key I was using to register the decorator instance. With my code, the instance was actually registered with the base class type, while I needed to register it with the actual decorator type.

这个帖子有建议的解决方法的问题,这工作很漂亮的我们的终点。

This post has the suggested workaround for the issue, which worked very nicely on our end.