C# 简单注入器:获取图形的根

C# 简单注入器:获取图形的根,c#,dependency-injection,tree,simple-injector,C#,Dependency Injection,Tree,Simple Injector,例如,我有以下对象图: new Root( new Branch(), new BranchWithLeafs( new Leaf() ) ); 让我们以Leaf为例,我可以确定Leaf的父对象使用的是什么,但如何获取Root?RegisterWithContext扩展方法在后台使用ExpressionBuilding事件,并截取Leaf父对象的表达式并进行更改调用叶的委托,以便传入依赖上下文 扩展代码以允许父级的父级(Root,在您的情况下)也被拦截是非常容易的

例如,我有以下对象图:

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

让我们以
Leaf
为例,我可以确定
Leaf
的父对象使用的是什么,但如何获取
Root

RegisterWithContext
扩展方法在后台使用
ExpressionBuilding
事件,并截取
Leaf
父对象的
表达式
并进行更改调用
的委托,以便传入
依赖上下文

扩展代码以允许父级的父级(
Root
,在您的情况下)也被拦截是非常容易的,但不幸的是,当您使用比
Transient
更长的生活方式注册服务时,这会很快中断。这是由简单喷油器在盖子下进行的优化引起的。例如,当您将
分支
注册为单例时,其值在创建
表达式之前创建,这意味着在该点上,一旦构建了
,就没有可以更改的表达式树。因此,在这种情况下,
仅仅依赖于一个
表达式.常量
,该常量包含对
分支
实例的引用,您将无法更改
的创建。我认为这是为数不多的几种情况之一,在这些情况下,简单的注入器确实会对您产生不利影响

下面是一个修改版的
RegisterWithContext
扩展方法,它允许一路使用“海龟”,但请记住,一旦你在树中注册了与
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;
        }
    }
}
[DebuggerDisplay(“DependencyContext(ServiceType:{ServiceType},”+
“ImplementationType:{ImplementationType})”)]
公共类依赖上下文{
内部静态只读DependencyContext根=
新的DependencyContext();
内部依赖上下文(类型serviceType,
类型implementationType,DependencyContext父级)
{
this.ServiceType=ServiceType;
this.ImplementationType=ImplementationType;
这个。父=父;
}
私有依赖上下文(){}
//现在有了一个父属性!
public DependencyContext父项{get;private set;}
公共类型ServiceType{get;private set;}
公共类型实现类型{get;private set;}
}
公共静态类contextDependentTextensions{
公共静态无效注册表WithContext(
这个集装箱,
Func contextBasedFactory)
where-TService:class{
if(contextBasedFactory==null){
抛出新ArgumentNullException(“contextBasedFactory”);
}
Func根工厂=
()=>contextBasedFactory(DependencyContext.Root);
容器。注册(rootFactory,lifesture.Transient);
//允许使用Func
//注入父类型。
container.ExpressionBuilding+=(发送方,e)=>{
if(例如RegisteredServiceType!=typeof(TService)){
var rewriter=新的DependencyContextRewriter{
ServiceType=e.RegisteredServiceType,
ContextBasedFactory=ContextBasedFactory,
RootFactory=RootFactory,
表达式
};
e、 表达式=重写器访问(e.Expression);
}
};
}
私有密封类DependencyContextRewriter:ExpressionVisitor{
内部类型ServiceType{get;set;}
内部对象ContextBasedFactory{get;set;}
内部对象根工厂{get;set;}
内部表达式{get;set;}
内部类型实现类型
{
得到{
var expression=this.expression作为NewExpression;
if(表达式!=null){
返回表达式.Constructor.DeclaringType;
}
返回此.ServiceType;
}
}
受保护的重写表达式VisitInvocation(调用表达式节点){
var context=GetContextFromNode(节点);
if(上下文==null){
返回基地。访问职业(节点);
}
返回表达式.Invoke(
Expression.Constant(this.ContextBasedFactory),
表达式.常数(
新依赖上下文(
这个.ServiceType,
这个.ImplementationType,
),;
}
private DependencyContext GetContextFromNode(调用表达式节点){
var常量=节点。表达式为常量表达式;
if(常数!=null){
if(object.ReferenceEquals(constant.Value,this.RootFactory)){
返回DependencyContext.Root;
}
if(object.ReferenceEquals(constant.Value,this.ContextBasedFactory)){
var arg=(ConstantExpression)node.Arguments[0];
返回(DependencyContext)(参数值);
}
}
返回null;
}
}
}

通过对
分支
分支WithLeafs
使用上下文相关注入?如果它与
Leaf
一起工作,那么它应该与更高级别一起工作。@Robert这是一个非常简单的示例。在我的场景中,节点的数量及其生命周期各不相同。是的,但机制不应该如此。基本上,这将是一个递归操作,对吗?如果是的话,你应该能够一直走到树的根部。这实际上是相当复杂的。你能更新你的问题来解释你想要达到的目标吗。也许还有另一种(或更好的)方法。@RobertHarvey:这是一个相当复杂的问题