最近开始使用autofac,我遇到了一个循环依赖问题,我在使用Unity时遇到了这个问题。这是我的代码:
void Main()
{
var builder = new ContainerBuilder();
// 1) Every class needs logger
// 2) Logger needs AppSettingsProvider which needs AppEnvironmentProvider
// 3) Listener needs AppSettingsProvider which needs AppEnvironmentProvider
builder.RegisterType<Logger>().As<ILogger>().SingleInstance();
builder.RegisterType<AppSettingsProvider>().As<IAppSettingsProvider>().SingleInstance();
builder.RegisterType<AppEnvironmentProvider>().As<IAppEnvironmentProvider>().SingleInstance();
builder.RegisterType<Listener>().As<IListener>().SingleInstance();
var container = builder.Build();
var listener = container.Resolve<IListener>();
listener.Do();
}
public interface IListener
{
string Do();
}
public class Listener : IListener
{
IAppSettingsProvider appSettingsProvider;
public Listener(IAppSettingsProvider appSettingsProvider)
{
// this class needs IAppSettingsProvider to get some settings
// but not actually used on this example.
this.appSettingsProvider = appSettingsProvider;
}
public string Do()
{
return "doing something";
}
}
public interface ILogger
{
void Log(string message);
}
public class Logger : ILogger
{
IAppSettingsProvider appSettingsProvider;
public Logger(IAppSettingsProvider appSettingsProvider)
{
this.appSettingsProvider = appSettingsProvider;
}
public void Log(string message)
{
// simplified
if (this.appSettingsProvider.GetSettings())
{
Console.WriteLine(message);
}
}
}
public interface IAppSettingsProvider
{
// will return a class, here simplified to bool
bool GetSettings();
}
public class AppSettingsProvider : IAppSettingsProvider
{
ILogger logger;
public AppSettingsProvider(ILogger logger)
{
this.logger = logger;
}
public bool GetSettings()
{
this.logger.Log("Getting app settings");
return true;
}
}
public interface IAppEnvironmentProvider
{
string GetEnvironment();
}
public class AppEnvironmentProvider : IAppEnvironmentProvider
{
ILogger logger;
public AppEnvironmentProvider(ILogger logger)
{
this.logger = logger;
}
public string GetEnvironment()
{
this.logger.Log("returning current environment");
return "dev";
}
}
任何有关解决此问题的建议都会有所帮助。
答案 0 :(得分:2)
这里有2个选项:
以下是使用Property Injection的示例:
static void Main()
{
var builder = new ContainerBuilder();
// 1) Every class needs logger
// 2) Logger needs AppSettingsProvider which needs AppEnvironmentProvider
// 3) Listener needs AppSettingsProvider which needs AppEnvironmentProvider
builder.RegisterType<Logger>().As<ILogger>().SingleInstance();
builder.RegisterType<AppSettingsProvider>()
.As<IAppSettingsProvider>()
.SingleInstance()
.PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies);
builder.RegisterType<AppEnvironmentProvider>().As<IAppEnvironmentProvider>().SingleInstance();
builder.RegisterType<Listener>().As<IListener>().SingleInstance();
var container = builder.Build();
var listener = container.Resolve<IListener>();
Console.WriteLine(listener.Do());
Console.Read();
}
public interface IListener
{
string Do();
}
public class Listener : IListener
{
IAppSettingsProvider appSettingsProvider;
public Listener(IAppSettingsProvider appSettingsProvider)
{
// this class needs IAppSettingsProvider to get some settings
// but not actually used on this example.
this.appSettingsProvider = appSettingsProvider;
}
public string Do()
{
return "doing something using circular Dependency";
}
}
public interface ILogger
{
void Log(string message);
}
public class Logger : ILogger
{
IAppSettingsProvider appSettingsProvider;
public Logger(IAppSettingsProvider appSettingsProvider)
{
this.appSettingsProvider = appSettingsProvider;
}
public void Log(string message)
{
// simplified
if (this.appSettingsProvider.GetSettings())
{
Console.WriteLine(message);
}
}
}
public interface IAppSettingsProvider
{
// will return a class, here simplified to bool
bool GetSettings();
}
public class AppSettingsProvider : IAppSettingsProvider
{
ILogger logger;
public AppSettingsProvider()
{
}
public ILogger Logger { get; set; }
public bool GetSettings()
{
Logger.Log("Getting app settings");
return true;
}
}
public interface IAppEnvironmentProvider
{
string GetEnvironment();
}
public class AppEnvironmentProvider : IAppEnvironmentProvider
{
ILogger logger;
public AppEnvironmentProvider(ILogger logger)
{
this.logger = logger;
}
public string GetEnvironment()
{
this.logger.Log("returning current environment");
return "dev";
}
}
以下是Autofac建议: Autofac reference
答案 1 :(得分:2)
Autofac提供了另一种克服循环依赖的选项,如果A需要B并且B需要A,并且B在其构造函数中不使用A ,则B可以通过称为< strong>动态实例化,参见
https://docs.autofac.org/en/latest/resolve/relationships.html#dynamic-instantiation-func-b
通过这种方式,B将在其构造函数中使用A的工厂方法Func<A>
(并以某种惰性的方式在构造函数之后调用它)以获取A的实例。
例如在上述问题中,更改Logger以克服循环依赖问题:
public class Logger : ILogger
{
Func<IAppSettingsProvider> appSettingsProviderFactory;
IAppSettingsProvider _appSettingsProvider;
IAppSettingsProvider appSettingsProvider { get
{
if (_appSettingsProvider == null) _appSettingsProvider = appSettingsProviderFactory();
return _appSettingsProvider;
}
}
public Logger(Func<IAppSettingsProvider> appSettingsProviderFactory)
{
this.appSettingsProviderFactory = appSettingsProviderFactory;
}
public void Log(string message)
{
// simplified
if (this.appSettingsProvider.GetSettings())
{
Console.WriteLine(message);
}
}
}
(注意:当我在Do()中添加appSettingsProvider.GetSettings();时,有问题的代码进入
由于StackOverflowException,进程正在终止。
因为GetSettings调用Log,而Log调用GetSettings,但这是另一回事了
答案 2 :(得分:2)
另一种解决方法是在依赖服务的构造函数中使用Lazy<ILogger>
。
public class AppSettingsProvider : IAppSettingsProvider
{
Lazy<ILogger> logger;
public AppSettingsProvider(Lazy<ILogger> logger)
{
this.logger = logger;
}
public bool GetSettings()
{
this.logger.Value.Log("Getting app settings");
return true;
}
}
答案 3 :(得分:1)
您需要在实施中使它们互斥。例如:
此处的循环引用表明您可能不会以易于维护的方式执行此操作(即,您将具有更高的耦合)。
如果您希望保持代码不变并且无法正常工作,那么您可以将日志方法和getsettings方法设置为静态。在我看来这是一个黑客,你应该尝试在顶部列出的选项1或2。这样做的原因是因为在我看来,制作一些静态的东西不应该改变代码的行为,而应该用于内存优化(即在这个区域中看一些类似读取的单例反模式)。
对于您的代码,我建议您从appsettingsprovider中删除日志记录,而是使用logger的初始化来添加围绕该类使用的日志语句。或者你可以探索: