C# 如何在运行时更新Autofac容器中的注册实例

C# 如何在运行时更新Autofac容器中的注册实例,c#,asp.net-mvc,dependency-injection,inversion-of-control,autofac,C#,Asp.net Mvc,Dependency Injection,Inversion Of Control,Autofac,我想在运行时用ASP.NET MVC应用程序中的新实例替换Autofac中现有的已注册实例。注册是在我处理不同子类型的实例集合时输入的,尽管这似乎与我的问题无关 启动应用程序时的初始注册 foreach (var instance in instances) { builder.RegisterInstance(instance).Keyed<IInstance>(InstanceType.A); } IContainer container = builder.Build

我想在运行时用ASP.NET MVC应用程序中的新实例替换Autofac中现有的已注册实例。注册是在我处理不同子类型的实例集合时输入的,尽管这似乎与我的问题无关

启动应用程序时的初始注册

foreach (var instance in instances)
{
    builder.RegisterInstance(instance).Keyed<IInstance>(InstanceType.A); 
}
IContainer container = builder.Build();    
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
foreach(实例中的var实例)
{
builder.RegisterInstance(instance).Keyed(InstanceType.A);
}
IContainer容器=builder.Build();
SetResolver(新的AutofacDependencyResolver(容器));
此外,在控制器方法中,我执行以下操作:处置旧实例,获取新实例,创建新生成器,重新注册现有组件,同时注册新实例,然后更新Autofac的ComponentContext

//...dispose old instances, obtain new instances

var builder = new ContainerBuilder();
foreach (var c in _componentContext.ComponentRegistry.Registrations)
{
    builder.RegisterComponent(c);
}
foreach (var instance in newInstances)
{
    builder.RegisterInstance(instance).Keyed<IInstance>(InstanceType.A);
}
builder.Update(_componentContext.ComponentRegistry);
/…处置旧实例,获取新实例
var builder=new ContainerBuilder();
foreach(var c在_componentContext.ComponentRegistry.Registrations中)
{
建造商注册组件(c);
}
foreach(newInstances中的var实例)
{
builder.RegisterInstance(instance).Keyed(InstanceType.A);
}
builder.Update(_componentContext.ComponentRegistry);

下次我输入controller方法时,在controller构造函数中,旧实例解析为
IIndex
,而不是新实例。我做错了什么

您的代码不起作用,因为
componentContext
是当前作用域的上下文,而不是全局作用域。您可以查看此.NetFiddle以显示一些说明问题的代码:

如果确实要替换实例,则使用提供程序会更简单:

class Program
{
    static void Main(string[] args)
    {
        ContainerBuilder builder = new ContainerBuilder();
        builder.RegisterInstance(new FooProvider(new Foo("a")))
               .As<FooProvider>();
        builder.Register(c => c.Resolve<FooProvider>().Value)
               .ExternallyOwned()
               .Keyed<Foo>(1);

        IContainer container = builder.Build();
        using (ILifetimeScope scope = container.BeginLifetimeScope())
        {
            Do(scope);
        }

        using (ILifetimeScope scope = container.BeginLifetimeScope())
        {
            IComponentContext context = scope.Resolve<IComponentContext>();
            container.Resolve<FooProvider>().Value = new Foo("b");
            Do(scope);
        }

        using (ILifetimeScope scope = container.BeginLifetimeScope())
        {
            Do(scope);
        }
    }

    static void Do(ILifetimeScope scope)
    {
        IIndex<Int32, Foo> index = scope.Resolve<IIndex<Int32, Foo>>();
        Foo foo = index[1];
        Console.WriteLine(foo.Value);
    }
}

public class FooProvider 
{
    public FooProvider(Foo value)
    {
        this._value = value;
    }

    private volatile Foo _value;
    public Foo Value
    {
        get
        {
            return this._value;
        }
    }

    public void ChangeValue(Foo value)
    {
        if(value == null)
        {
            throw new ArgumentNullException("value");
        }
        if(value == this._value)
        {
            return; 
        }

        Foo oldValue = this._value; 
        this._value = value;
        oldValue.Dispose();
    }

    public void Dispose() 
    {
        this._value.Dispose(); 
    }
}
public class Foo : IDisposable
{
    public Foo(String value)
    {
        this._value = value;
    }

    private readonly String _value;
    public String Value
    {
        get
        {
            return this._value;
        }
    }

    public void Dispose()
    {
        // do things
    }
}
类程序
{
静态void Main(字符串[]参数)
{
ContainerBuilder=新的ContainerBuilder();
builder.RegisterInstance(新FooProvider(新Foo(“a”)))
.As();
builder.Register(c=>c.Resolve().Value)
.ExternallyOwned()
.键入(1);
IContainer容器=builder.Build();
使用(ILifetimeScope scope=container.BeginLifetimeScope())
{
Do(范围);
}
使用(ILifetimeScope scope=container.BeginLifetimeScope())
{
IComponentContext=scope.Resolve();
container.Resolve().Value=newfoo(“b”);
Do(范围);
}
使用(ILifetimeScope scope=container.BeginLifetimeScope())
{
Do(范围);
}
}
静态void Do(ILifetimeScope范围)
{
IIndex index=scope.Resolve();
Foo-Foo=指数[1];
控制台写入线(foo.Value);
}
}
公共类FoodProvider
{
公共FooProvider(Foo值)
{
此值为._值=值;
}
私有易失Foo_值;
公共食品价值
{
得到
{
返回此值;
}
}
公共无效值(Foo值)
{
如果(值==null)
{
抛出新的ArgumentNullException(“值”);
}
如果(值==此值)
{
返回;
}
Foo oldValue=此值;
此值为._值=值;
Dispose();
}
公共空间处置()
{
此._value.Dispose();
}
}
公共类Foo:IDisposable
{
公共Foo(字符串值)
{
此值为._值=值;
}
私有只读字符串_值;
公共字符串值
{
得到
{
返回此值;
}
}
公共空间处置()
{
//做事
}
}

我觉得这是个坏主意。为什么不注册一个可以保存和更改引用的对象呢?若您在应用程序的外部使用容器,那个么这是一种代码气味。我完全同意@SamHolder:这样做是一种可怕的做法。它很复杂,容易出错,性能很高,而且是维护的噩梦。如果你解释一下你想要完成什么(为什么要替换注册),我们可能会给出一些反馈,并展示一种更好的方法。@SamHolder和Steven:是的,我知道代码闻起来很难闻,但我很好奇为什么组件上下文更新不起作用。实例是MassTransit服务总线,我需要保留对象引用,以便能够正确关闭它们并按需获取诊断数据。在执行刷新操作时,总线的配置可能会更改,因此我必须关闭旧总线并启动新的总线。无论如何,最好的方法似乎是使用一种对象提供程序来保存对可用服务总线的引用。我不敢在容器本身中注册容器的实例(否则我如何让它注入mvc控制器构造函数?),这太邪恶了。我认为这个想法是使用ComponentContext对象作为使用ReisterInstance()注册的实例的默认生存期范围是Singleton。对象提供程序确实有意义,毫无疑问,在应用程序启动后,我将重写代码以避免任何与容器相关的操作。@jjjj我很高兴一些容器滥用,如果禁止:)@SamHolder没有容器受到伤害,至少在活动环境中:)