【问题标题】:How Does Parameterized Instantiation Work with SingleInstance Life Scope参数化实例化如何与 SingleInstance Life Scope 一起工作
【发布时间】:2021-11-12 03:16:05
【问题描述】:

根据Parameterized Instantiation (Func<X, Y, B>) 文档:

但是,如果将一个对象注册为 SingleInstance() 并多次调用 Func 来解析该对象,则无论传入不同的参数,每次都会得到相同的对象实例. 只是传递不同的参数不会破坏对生命周期范围的尊重。

具有不同参数构造函数的单例类如何工作?

【问题讨论】:

    标签: c# autofac


    【解决方案1】:

    Autofac 工厂注册不会缓存每个参数的实例。

    让我们尝试以下类型:

    public class Foo
    {
        public Foo(Int32 a)
        {
            this._a = a;
        }
    
        private readonly Int32 _a;
    
        public override String ToString()
        {
            return String.Format("a={0}", this._a);
        }
    }
    

    如果你这样注册:

    ContainerBuilder builder = new ContainerBuilder();
    builder.RegisterType<Foo>().AsSelf().SingleInstance();
    

    Autofac 将始终注册单个实例,无论您如何解决它(即使您使用 Func 工厂):

    Func<Int32, Foo> foo1Factory = scope.Resolve<Func<Int32, Foo>>();
    Foo foo1 = foo1Factory(1);
    Func<Int32, Foo> foo2Factory = scope.Resolve<Func<Int32, Foo>>();
    Foo foo2 = foo2Factory(2);
    

    foo1foo2 将是同一个实例。

    如果您希望每个参数只有一个实例,这意味着您需要多个Foo 实例,因此您不能使用SingleInstance() 方法注册Foo。您将必须实现一个自定义工厂,该工厂将进行缓存以使每个参数都有一个实例:

    public class FooFactory
    {
        public FooFactory(IComponentContext componentContext)
        {
            this._componentContext = componentContext;
            this._instances = new Dictionary<Int32, Foo>();
            this._lock = new Object();
        }
    
        private readonly IComponentContext _componentContext;
        private readonly Dictionary<Int32, Foo> _instances;
        private readonly Object _lock;
    
        public Foo GetInstance(Int32 a)
        {
            Foo parameterizedFoo;
            if (!this._instances.TryGetValue(a, out parameterizedFoo))
            {
                lock (this._lock)
                {
                    if (!this._instances.TryGetValue(a, out parameterizedFoo))
                    {
                        Parameter aParameter = new TypedParameter(typeof(Int32), a);
                        parameterizedFoo = this._componentContext
                                               .Resolve<Foo>(aParameter);
                        this._instances[a] = parameterizedFoo;
                    }
                }
            }
            return parameterizedFoo;
        }
    }
    

    你可以这样注册:

    ContainerBuilder builder = new ContainerBuilder();
    builder.RegisterType<Foo>().AsSelf();
    builder.RegisterType<FooFactory>().AsSelf().SingleInstance();
    

    使用下面的代码来使用它:

    FooFactory fooFactory = scope.Resolve<FooFactory>();
    Foo foo = fooFactory.GetInstance(1);
    

    但是如果你想使用Func&lt;Int32, Foo&gt;而不是FooFactory.GetInstance,你可以将方法注册为Func&lt;Int32, Foo&gt;

    ContainerBuilder builder = new ContainerBuilder();
    builder.RegisterType<Foo>().AsSelf();
    builder.RegisterType<FooFactory>().AsSelf().SingleInstance();
    builder.Register(c => new Func<Int32, Foo>(c.Resolve<FooFactory>().GetInstance))
           .As<Func<Int32, Foo>>()
           .SingleInstance();
    

    您现在可以使用此代码:

    Func<Int32, Foo> foo1Factory = scope.Resolve<Func<Int32, Foo>>();
    Foo foo1 = foo1Factory(1);
    Func<Int32, Foo> foo2Factory = scope.Resolve<Func<Int32, Foo>>();
    Foo foo2 = foo2Factory(2);
    Func<Int32, Foo> foo1PrimeFactory = scope.Resolve<Func<Int32, Foo>>();
    Foo foo1Prime = foo1PrimeFactory(1);
    

    foo1foo2 将不同,而 foo1Prime 将与 foo1 相同。

    【讨论】:

      【解决方案2】:

      最后,我的同事也有类似的方法

      public interface IKey
      {
      }
      
      public interface IKeyed<out T>
      {
          T this[IKey key] { get; }
      }
      
      public class Keyed<T> : IKeyed<T>
      {
          private ConcurrentDictionary<IKey, T> _dict = new ConcurrentDictionary<IKey,T>();
      
          T IKeyed<T>.this[IKey key]
          {
              get 
              {
                  return _dict.GetOrAdd(key, k => IoC.Resolve<T>(new TypedParameter(key.GetType(), key)));
              }
          }
      }
      

      在应用启动中,注册

         _builder.RegisterGeneric(typeof(Keyed<>))
              .As(typeof(IKeyed<>))
              .SingleInstance();
      

      【讨论】:

        猜你喜欢
        • 2019-06-05
        • 1970-01-01
        • 2022-01-16
        • 2021-06-05
        • 2011-03-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多