[英]How to solve Autofac circular dependency?
最近開始使用 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";
}
}
解決此問題的任何指示都會有所幫助。
您在這里有 2 個選項:
下面是一個使用屬性注入的例子:
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 參考
另一種解決方法是在依賴服務的構造函數中使用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;
}
}
Autofac 提供了另一種克服循環依賴的選項,如果 A 需要 B 並且 B 需要 A,而 B 不在其構造函數中使用 A ,B 可以以稱為動態實例化的方式使用 A,請參見
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 但這是另一回事)
您需要使它們在實現中互斥。 例子:
此處的循環引用表明您可能沒有以易於維護的方式執行此操作(即您將具有更高的耦合度)。
如果您想保持代碼原樣並使其正常工作,您可以將 log 方法和 getsettings 方法設為靜態。 在我看來,這是一個 hack,您應該嘗試頂部列出的選項 1 或 2。 這樣做的原因是因為在我看來,使某些東西成為靜態不應該改變代碼的行為,而是應該用於內存優化(即,請參閱本領域的一些類似閱讀的單例反模式)。
對於您的代碼,我建議您從 appsettingsprovider 中刪除日志記錄,而是使用它的記錄器啟動來添加圍繞該類使用的日志語句。 或者你可以探索:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.