C# Autofac单例激活解析
我误解的症结在于,我想直接解析()一个嵌套方法中的一个类型,该方法是由于OnActivating事件调用的,用于同一个单例类型,而autofac正试图创建该单例的第二个实例 长得多的版本: 首先是一个完整的示例,然后我将总结:C# Autofac单例激活解析,c#,design-patterns,inversion-of-control,autofac,C#,Design Patterns,Inversion Of Control,Autofac,我误解的症结在于,我想直接解析()一个嵌套方法中的一个类型,该方法是由于OnActivating事件调用的,用于同一个单例类型,而autofac正试图创建该单例的第二个实例 长得多的版本: 首先是一个完整的示例,然后我将总结: public static class AutofacTest { public static void Test() { var builder = new ContainerBuilder(); // Register
public static class AutofacTest
{
public static void Test()
{
var builder = new ContainerBuilder();
// Register the environment as a singleton, and call Initialize when created
builder.RegisterType<Environment>().AsSelf().As<IEnvironment>().SingleInstance().OnActivating(e => e.Instance.Initialize());
// Register the simulator, also a singleton and dependent on
builder.RegisterType<Simulator>().AsSelf().As<ISimulator>().SingleInstance();
// Register a simple class, that needs an initialized environment
builder.RegisterType<IndependentClass>();
// Build/scope
var context = builder.Build();
var scope = context.BeginLifetimeScope();
// Register the service locator
ServiceLocator.GlobalScope = scope;
//var childScope = scope.BeginLifetimeScope(cb =>
//{
// cb.RegisterType<IndependentClass>();
//});
// Now resolve the independent class, which will trigger the environment/simulator instantiation
var inst = scope.Resolve<IndependentClass>();
}
}
public static class ServiceLocator
{
public static ILifetimeScope GlobalScope { get; set; }
}
public interface IEnvironment
{
bool IsInitialized { get; }
}
public class Environment : IEnvironment
{
private static Environment Instance;
private SampleComponent _component;
private bool _isInitialized;
public bool IsInitialized
{
get { return _isInitialized; }
}
public void Initialize()
{
if (Instance != null) throw new InvalidOperationException();
Instance = this;
// Canonical complex code which forces me into what I think is a tricky situation...
_component = new SampleComponent(SampleServiceType.SimulatedThing);
_component.Initialize();
_isInitialized = true;
}
}
public interface ISimulator { }
public class Simulator : ISimulator
{
private static Simulator Instance;
private readonly IEnvironment _environment;
public Simulator(IEnvironment environment)
{
if (Instance != null) throw new InvalidOperationException();
Instance = this;
_environment = environment;
}
}
public enum SampleServiceType
{
None = 0,
RealThing,
SimulatedThing,
}
public class SampleComponent
{
private readonly SampleServiceType _serviceType;
public SampleComponent(SampleServiceType serviceType)
{
_serviceType = serviceType;
}
public void Initialize()
{
// Sample component that has different types of repositories
switch (_serviceType)
{
case SampleServiceType.SimulatedThing:
var sim = ServiceLocator.GlobalScope.Resolve<ISimulator>();
// Create a repositiry object or something requriing the simulator
break;
}
}
}
public class IndependentClass
{
public IndependentClass(IEnvironment env)
{
if (!env.IsInitialized) throw new InvalidOperationException();
}
}
不过,这是可行的:1<仍然会为每个“新”实例调用code>on激活。2.只是感觉太粗糙了——最终我现在正在管理实例和构造,这就是容器的用途。(当您实际想要使用容器解析参数时,这也有点烦人,但同样可以很容易地解决。)
所以说了这么多(我非常感谢你能做到这一点),似乎我在这里有一个基本的误解。(我猜它与SampleComponent
中的服务定位器模式和/或偶然工厂有关,但我不再猜测。)
我想真正的问题是:我遗漏了什么?试图运行示例中的确切代码,我无法解析
独立类,因为我(正确地)得到了一个异常。异常堆栈看起来像一个循环依赖项,它嵌套和嵌套同一个异常,就像堆栈溢出:
Autofac.Core.DependencyResolutionException was unhandled
_HResult=-2146233088
_message=An exception was thrown while executing a resolve operation. See the InnerException for details.
HResult=-2146233088
IsTransient=false
Message=An exception was thrown while executing a resolve operation. See the InnerException for details. ---> Operation is not valid due to the current state of the object. (See inner exception for details.)
Source=Autofac
StackTrace:
at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance)
at Autofac.ResolutionExtensions.ResolveService(IComponentContext context, Service service, IEnumerable`1 parameters)
at Autofac.ResolutionExtensions.Resolve(IComponentContext context, Type serviceType, IEnumerable`1 parameters)
at Autofac.ResolutionExtensions.Resolve[TService](IComponentContext context, IEnumerable`1 parameters)
at Autofac.ResolutionExtensions.Resolve[TService](IComponentContext context)
at SingletonRepro.SampleComponent.Initialize() in e:\dev\spike\SingletonRepro\SingletonRepro\Program.cs:line 120
at SingletonRepro.Environment.Initialize() in e:\dev\spike\SingletonRepro\SingletonRepro\Program.cs:line 75
at SingletonRepro.Program.<Main>b__0(IActivatingEventArgs`1 e) in e:\dev\spike\SingletonRepro\SingletonRepro\Program.cs:line 17
at Autofac.Builder.RegistrationBuilder`3.<>c__DisplayClass6.<OnActivating>b__5(Object s, ActivatingEventArgs`1 e)
at Autofac.Core.Registration.ComponentRegistration.RaiseActivating(IComponentContext context, IEnumerable`1 parameters, Object& instance)
at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters)
at Autofac.Core.Resolving.InstanceLookup.<Execute>b__0()
at Autofac.Core.Lifetime.LifetimeScope.GetOrCreateAndShare(Guid id, Func`1 creator)
at Autofac.Core.Resolving.InstanceLookup.Execute()
at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Resolving.InstanceLookup.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Activators.Reflection.AutowiringParameter.<>c__DisplayClass2.<CanSupplyValue>b__0()
at Autofac.Core.Activators.Reflection.ConstructorParameterBinding.Instantiate()
at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters)
at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters)
at Autofac.Core.Resolving.InstanceLookup.Execute()
at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Resolving.ResolveOperation.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance)
at Autofac.ResolutionExtensions.ResolveService(IComponentContext context, Service service, IEnumerable`1 parameters)
at Autofac.ResolutionExtensions.Resolve(IComponentContext context, Type serviceType, IEnumerable`1 parameters)
at Autofac.ResolutionExtensions.Resolve[TService](IComponentContext context, IEnumerable`1 parameters)
at Autofac.ResolutionExtensions.Resolve[TService](IComponentContext context)
at SingletonRepro.Program.Main(String[] args) in e:\dev\spike\SingletonRepro\SingletonRepro\Program.cs:line 38
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException: System.InvalidOperationException
_HResult=-2146233079
_message=Operation is not valid due to the current state of the object.
HResult=-2146233079
IsTransient=false
Message=Operation is not valid due to the current state of the object.
Source=SingletonRepro
StackTrace:
at SingletonRepro.Environment.Initialize() in e:\dev\spike\SingletonRepro\SingletonRepro\Program.cs:line 68
at SingletonRepro.Program.<Main>b__0(IActivatingEventArgs`1 e) in e:\dev\spike\SingletonRepro\SingletonRepro\Program.cs:line 17
at Autofac.Builder.RegistrationBuilder`3.<>c__DisplayClass6.<OnActivating>b__5(Object s, ActivatingEventArgs`1 e)
at Autofac.Core.Registration.ComponentRegistration.RaiseActivating(IComponentContext context, IEnumerable`1 parameters, Object& instance)
at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters)
at Autofac.Core.Resolving.InstanceLookup.<Execute>b__0()
at Autofac.Core.Lifetime.LifetimeScope.GetOrCreateAndShare(Guid id, Func`1 creator)
at Autofac.Core.Resolving.InstanceLookup.Execute()
at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Resolving.InstanceLookup.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Activators.Reflection.AutowiringParameter.<>c__DisplayClass2.<CanSupplyValue>b__0()
at Autofac.Core.Activators.Reflection.ConstructorParameterBinding.Instantiate()
at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters)
at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters)
at Autofac.Core.Resolving.InstanceLookup.<Execute>b__0()
at Autofac.Core.Lifetime.LifetimeScope.GetOrCreateAndShare(Guid id, Func`1 creator)
at Autofac.Core.Resolving.InstanceLookup.Execute()
at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Resolving.ResolveOperation.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters)
InnerException: ...
如果您创建的存储库需要i模拟器
,请尝试将该存储库的构造函数更改为使用Lazy
,并且仅在最后一刻调用Lazy.Value
。这将延迟环境的解析操作
足够长的时间,使整个链第一次正确完成,并使您摆脱循环解析问题
一个更好的选择是重构以一直使用DI。现在,您可以通过代码混合依赖注入、服务定位和手动实例构造<代码>环境
手动创建一个采样组件
SampleComponent
使用服务位置获取一个ISimulator
ISimulator
使用DI获取IEnvironment
。像这样的混合和匹配会让你陷入各种各样的麻烦,就像你现在看到的那样
事实上,一直使用DI意味着您实际上不需要在任何地方实现singleton模式,只需使用构造函数并根据需要注册东西SingleInstance
这是您的代码的更新版本(在控制台应用程序表单中),其中显示了一些可能的操作方法。显然,您真正的代码可能更复杂,因此我无法确切地向您展示每个边缘情况的所有可能解决方案,但这是一种打破链条的方法。你可以利用这里和其他地方的想法来找出应对挑战的方法
using System;
using Autofac;
using Autofac.Features.Indexed;
namespace SingletonRepro
{
class Program
{
static void Main()
{
var builder = new ContainerBuilder();
// You can still keep the Initialize call if you want.
builder.RegisterType<Environment>().As<IEnvironment>().SingleInstance().OnActivated(args => args.Instance.Initialize());
// Everything's in DI now, not just some things.
builder.RegisterType<Simulator>().As<ISimulator>().SingleInstance();
builder.RegisterType<IndependentClass>();
// Using keyed services to choose the repository rather than newing things up.
builder.RegisterType<RealRepository>().Keyed<IRepository>(SampleServiceType.RealThing);
builder.RegisterType<SimulatedRepository>().Keyed<IRepository>(SampleServiceType.SimulatedThing);
builder.RegisterType<SampleComponent>().WithParameter("serviceType", SampleServiceType.SimulatedThing);
var context = builder.Build();
using (var scope = context.BeginLifetimeScope())
{
// Using Lazy<T> in the IndependentClass to defer the need for
// IEnvironment right away - breaks the dependency circle.
var inst = scope.Resolve<IndependentClass>();
inst.DoWork();
Console.WriteLine("Instance: {0}", inst);
}
}
}
public interface IEnvironment
{
bool IsInitialized { get; }
}
public class Environment : IEnvironment
{
public SampleComponent _component;
public Environment(SampleComponent component)
{
this._component = component;
}
public void Initialize()
{
this._component.DoSomethingWithRepo();
this.IsInitialized = true;
}
public bool IsInitialized { get; private set; }
}
public interface ISimulator
{
}
public class Simulator : ISimulator
{
public Simulator(IEnvironment environment)
{
this.Environment = environment;
}
public IEnvironment Environment { get; private set; }
}
public enum SampleServiceType
{
None = 0,
RealThing,
SimulatedThing,
}
public class SampleComponent
{
private IIndex<SampleServiceType, IRepository> _repositories;
private SampleServiceType _serviceType;
// Use indexed/keyed services to pick the right one from a dictionary
// rather than newing up the repository (or whatever) manually.
public SampleComponent(IIndex<SampleServiceType, IRepository> repositories, SampleServiceType serviceType)
{
this._repositories = repositories;
this._serviceType = serviceType;
}
public void DoSomethingWithRepo()
{
// You could always take the service type parameter in this function
// rather than as a constructor param.
var repo = this._repositories[this._serviceType];
repo.DoWork();
}
}
public interface IRepository
{
void DoWork();
}
public class SimulatedRepository : IRepository
{
private ISimulator _simulator;
public SimulatedRepository(ISimulator simulator)
{
this._simulator = simulator;
}
public void DoWork()
{
}
}
public class RealRepository : IRepository
{
public void DoWork()
{
}
}
public class IndependentClass
{
private Lazy<IEnvironment> _env;
// Delaying the need for the IEnvironment in the constructor
// can help break the circular dependency chain, as well as not
// immediately checking that it's initialized. (Can you just
// TRUST that it's initialized and call it good?)
public IndependentClass(Lazy<IEnvironment> env)
{
this._env = env;
}
public void DoWork()
{
if (!this._env.Value.IsInitialized)
throw new InvalidOperationException();
}
}
}
使用系统;
使用Autofac;
使用Autofac.Features.index;
名称空间单音复制
{
班级计划
{
静态void Main()
{
var builder=new ContainerBuilder();
//如果需要,您仍然可以保留初始化调用。
已激活(args=>args.Instance.Initialize());
//现在一切都在DI中,不仅仅是一些东西。
builder.RegisterType().As().SingleInstance();
RegisterType();
//使用键控服务选择存储库,而不是更新内容。
builder.RegisterType().Keyed(SampleServiceType.RealThing);
已键入builder.RegisterType().Keyed(SampleServiceType.SimulatedThing);
builder.RegisterType().WithParameter(“serviceType”,SampleServiceType.SimulatedThing);
var context=builder.Build();
使用(var scope=context.BeginLifetimeScope())
{
//在独立类中使用Lazy延迟
//即时环境-打破依赖循环。
var inst=scope.Resolve();
安装道工();
WriteLine(“实例:{0}”,inst);
}
}
}
公共接口环境
{
布尔已初始化{get;}
}
公共类环境:IEnEnvironment
{
公共样本组件_组件;
公共环境(采样组件)
{
这个._组件=组件;
}
公共无效初始化()
{
此._component.DoSomethingWithRepo();
this.IsInitialized=真;
}
公共布尔值已初始化{get;private set;}
}
公共接口模拟器
{
}
公共类模拟器:ISimulator
{
公共模拟器(IEN环境)
{
这个。环境=环境;
}
公共IEnEnvironment环境{get;private set;}
}
公共枚举SampleServiceType
{
无=0,
房地产,
模拟,,
}
公共类采样组件
{
私有IIndex_存储库;
私有SampleServiceType_serviceType;
//使用索引/键控服务从字典中选择正确的服务
//而不是手动更新存储库(或其他)。
公共SampleComponent(IIndex存储库,SampleServiceType服务类型)
{
这是。_repositories=repositories;
此.\u serviceType=serviceType;
}
公共无效DoSomethingWithRepo()
{
//您可以始终在此函数中使用服务类型参数
//而不是作为构造函数参数。
var repo=this.\u存储库[this.\u服务类型];
回购协议;
}
}
公共接口假定
{
无效销钉();
}
公共类模拟存储:IRepository
{
专用ISimulator_模拟器;
公共模拟存储库(ISimulator模拟器)
{
这个._模拟器=模拟器;
}
公共工作
{
}
}
公共类RealRepository:IRepository
{
公共工作
{
}
}
公共类独立类
{
var sim = ServiceLocator.GlobalScope.Resolve<Lazy<ISimulator>>();
using System;
using Autofac;
using Autofac.Features.Indexed;
namespace SingletonRepro
{
class Program
{
static void Main()
{
var builder = new ContainerBuilder();
// You can still keep the Initialize call if you want.
builder.RegisterType<Environment>().As<IEnvironment>().SingleInstance().OnActivated(args => args.Instance.Initialize());
// Everything's in DI now, not just some things.
builder.RegisterType<Simulator>().As<ISimulator>().SingleInstance();
builder.RegisterType<IndependentClass>();
// Using keyed services to choose the repository rather than newing things up.
builder.RegisterType<RealRepository>().Keyed<IRepository>(SampleServiceType.RealThing);
builder.RegisterType<SimulatedRepository>().Keyed<IRepository>(SampleServiceType.SimulatedThing);
builder.RegisterType<SampleComponent>().WithParameter("serviceType", SampleServiceType.SimulatedThing);
var context = builder.Build();
using (var scope = context.BeginLifetimeScope())
{
// Using Lazy<T> in the IndependentClass to defer the need for
// IEnvironment right away - breaks the dependency circle.
var inst = scope.Resolve<IndependentClass>();
inst.DoWork();
Console.WriteLine("Instance: {0}", inst);
}
}
}
public interface IEnvironment
{
bool IsInitialized { get; }
}
public class Environment : IEnvironment
{
public SampleComponent _component;
public Environment(SampleComponent component)
{
this._component = component;
}
public void Initialize()
{
this._component.DoSomethingWithRepo();
this.IsInitialized = true;
}
public bool IsInitialized { get; private set; }
}
public interface ISimulator
{
}
public class Simulator : ISimulator
{
public Simulator(IEnvironment environment)
{
this.Environment = environment;
}
public IEnvironment Environment { get; private set; }
}
public enum SampleServiceType
{
None = 0,
RealThing,
SimulatedThing,
}
public class SampleComponent
{
private IIndex<SampleServiceType, IRepository> _repositories;
private SampleServiceType _serviceType;
// Use indexed/keyed services to pick the right one from a dictionary
// rather than newing up the repository (or whatever) manually.
public SampleComponent(IIndex<SampleServiceType, IRepository> repositories, SampleServiceType serviceType)
{
this._repositories = repositories;
this._serviceType = serviceType;
}
public void DoSomethingWithRepo()
{
// You could always take the service type parameter in this function
// rather than as a constructor param.
var repo = this._repositories[this._serviceType];
repo.DoWork();
}
}
public interface IRepository
{
void DoWork();
}
public class SimulatedRepository : IRepository
{
private ISimulator _simulator;
public SimulatedRepository(ISimulator simulator)
{
this._simulator = simulator;
}
public void DoWork()
{
}
}
public class RealRepository : IRepository
{
public void DoWork()
{
}
}
public class IndependentClass
{
private Lazy<IEnvironment> _env;
// Delaying the need for the IEnvironment in the constructor
// can help break the circular dependency chain, as well as not
// immediately checking that it's initialized. (Can you just
// TRUST that it's initialized and call it good?)
public IndependentClass(Lazy<IEnvironment> env)
{
this._env = env;
}
public void DoWork()
{
if (!this._env.Value.IsInitialized)
throw new InvalidOperationException();
}
}
}