For instance I have following object graph:

new Root(
   new Branch(),
   new BranchWithLeafs(
      new Leaf()
   )
);

Let's take Leaf I can determine what the parent of Leaf using context dependent injection but how I can get Root?

有帮助吗?

解决方案

The RegisterWithContext extension method uses the ExpressionBuilding event on the background and intercepts the Expression object of the Leaf's parent and changes the invocation of the Leaf's delegate so that the DependencyContext is passed in.

It's quite easy to extend the code to allow the parent's parent (Root in your case) to be intercepted as well, but unfortunately this breaks rather quickly when you register your services with a lifestyle that is longer than Transient. This is caused by the optimizations that Simple Injector does under the cover. For instance, when you register Branch as singleton, its value gets created before the Root Expression gets created, which means that at that point there is no Expression tree that can be changed once the Root gets built. So in that case the Root simply depends on a Expression.Constant that holds a reference to the Branch instance and you lose the ability to change the creation of the Leaf. I think this is one of the few scenarios where these optimizations that Simple Injector does work against you.

Here is an altered version of the RegisterWithContext extension method that allows working with 'turtles all the way down', but keep in mind that the chain gets truncated once you register anything in the tree with a different lifestyle than Transient:

[DebuggerDisplay("DependencyContext (ServiceType: {ServiceType}, " + 
    "ImplementationType: {ImplementationType})")]
public class DependencyContext {
    internal static readonly DependencyContext Root = 
        new DependencyContext();

    internal DependencyContext(Type serviceType, 
        Type implementationType, DependencyContext parent)
    {
        this.ServiceType = serviceType;
        this.ImplementationType = implementationType;
        this.Parent = parent;
    }

    private DependencyContext() { }

    // There's now a Parent property!
    public DependencyContext Parent { get; private set; }
    public Type ServiceType { get; private set; }
    public Type ImplementationType { get; private set; }
}

public static class ContextDependentExtensions {
    public static void RegisterWithContext<TService>(
        this Container container,
        Func<DependencyContext, TService> contextBasedFactory)
        where TService : class {
        if (contextBasedFactory == null) {
            throw new ArgumentNullException("contextBasedFactory");
        }

        Func<TService> rootFactory = 
            () => contextBasedFactory(DependencyContext.Root);

        container.Register<TService>(rootFactory, Lifestyle.Transient);

        // Allow the Func<DependencyContext, TService> to be 
        // injected into parent types.
        container.ExpressionBuilding += (sender, e) => {
            if (e.RegisteredServiceType != typeof(TService)) {
                var rewriter = new DependencyContextRewriter {
                    ServiceType = e.RegisteredServiceType,
                    ContextBasedFactory = contextBasedFactory,
                    RootFactory = rootFactory,
                    Expression = e.Expression
                };

                e.Expression = rewriter.Visit(e.Expression);
            }
        };
    }

    private sealed class DependencyContextRewriter : ExpressionVisitor {
        internal Type ServiceType { get; set; }
        internal object ContextBasedFactory { get; set; }
        internal object RootFactory { get; set; }
        internal Expression Expression { get; set; }

        internal Type ImplementationType
        {
            get {
                var expression = this.Expression as NewExpression;

                if (expression != null) {
                    return expression.Constructor.DeclaringType;
                }

                return this.ServiceType;
            }
        }

        protected override Expression VisitInvocation(InvocationExpression node) {
            var context = GetContextFromNode(node);

            if (context == null) {
                return base.VisitInvocation(node);
            }

            return Expression.Invoke(
                Expression.Constant(this.ContextBasedFactory),
                Expression.Constant(
                    new DependencyContext(
                        this.ServiceType,
                        this.ImplementationType,
                        context)));
        }

        private DependencyContext GetContextFromNode(InvocationExpression node) {
            var constant = node.Expression as ConstantExpression;

            if (constant != null) {
                if (object.ReferenceEquals(constant.Value, this.RootFactory)) {
                    return DependencyContext.Root;
                }

                if (object.ReferenceEquals(constant.Value, this.ContextBasedFactory)) {
                    var arg = (ConstantExpression)node.Arguments[0];
                    return (DependencyContext)(arg.Value);
                }
            }

            return null;
        }
    }
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top