コンソールアプリケーションでは、Log4Netを使用しています。メインメソッドでは、ロガーオブジェクトを取得しています。ここで、ILogプロパティを持ち、コンストラクターインジェクションではなくプロパティインジェクションによって設定されることになっているBaseClassからすべてのクラスを継承させることにより、このログオブジェクトをすべてのクラスで使用できるようにします。
AutoFac IoCコンテナーを使用していますが、すべてのクラスのLogプロパティにログオブジェクトを挿入する方法は?
これを達成するための最良/最も簡単な方法は何ですか?
型を自動的に解決する方法はありますか?
以下は私のテストアプリケーションです。
namespace ConsoleApplication1
{
class Program
{
static ILog Log;
static IContainer Container;
static void Main(string[] args)
{
InitializeLogger();
InitializeAutoFac();
// the below works but could it be done automatically (without specifying the name of each class)?
Product.Log = Container.Resolve<ILog>();
// tried below but didn't inject ILog object into the Product
Container.Resolve<Product>();
RunTest();
Console.ReadLine();
}
private static void RunTest()
{
var product = new Product();
product.Do();
}
private static void InitializeAutoFac()
{
var builder = new ContainerBuilder();
builder.Register(c => Log).As<ILog>();
builder.RegisterType<Product>().PropertiesAutowired();
Container = builder.Build();
}
private static void InitializeLogger()
{
log4net.Config.XmlConfigurator.Configure();
Log = LogManager.GetLogger("LoggerName");
}
}
public class Product
{
public static ILog Log { get; set; }
public void Do()
{
// this throws exception because Log is not set
Log.Debug("some Debug");
}
}
}
私の意見では、ソリューションNinject created は、Autofacの propertyinjection よりもはるかに優れています。したがって、クラスを自動的に挿入するポストシャープスアスペクトであるカスタム属性を作成しました。
[AutofacResolve]
public IStorageManager StorageManager { get; set; }
私の側面:
[Serializable]
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class AutofacResolveAttribute : LocationInterceptionAspect
{
public override void OnGetValue(LocationInterceptionArgs args)
{
args.ProceedGetValue();
if (!args.Location.LocationType.IsInterface) return;
if ( args.Value != null )
{
args.Value = DependencyResolver.Current.GetService(args.Location.LocationType);
args.ProceedSetValue();
}
}
}
質問に対する答えはすでに与えられていることは知っていますが、これはAutofacでの自動プロパティインジェクションを解決する本当にすてきな方法だと思いました。たぶん、将来誰かに役立つでしょう。
Property Injection を使用します:
builder.Register(c => LogManager.GetLogger("LoggerName"))
.As<ILog>();
builder.RegisterType<CustomClass>()
.PropertiesAutowired();
プロパティの挿入は、フィールドではなくプロパティに対して機能します。クラスでは、Logはフィールドであり、プロパティではないため、Autofacによって解決されることはありません。
Property Injection (@cuongle answerに加えて)を使用します。
_builder.Register(c => LogManager.GetLogger("LoggerName")).As<ILog>();
builder.RegisterType<Product>()
.WithProperty("Log", LogManager.GetLogger("LoggerName"));
_
または、SetLog
メソッドをProduct
クラスに追加できます。
_public class Product
{
public static ILog Log { get; set; }
public SetLog(Log log)
{
this.Log = log;
}
}
_
この方法では、LogManager.GetLogger("LoggerName")
を2回呼び出す必要はありませんが、Log
を解決するためにビルダーのコンテキストを使用する必要があります。
_builder.Register(c => LogManager.GetLogger("LoggerName")).As<ILog>();
builder.Register(c =>
var product = new Product();
product.SetLog(c.Resolve<Log>());
return product;
);
_
OnActvated を使用します。
OnActivatedイベントは、コンポーネントが完全に構築されると発生します。ここでは、完全に構築されるコンポーネントに依存するアプリケーションレベルのタスクを実行できます。これらはまれです。
_builder.RegisterType<Product>()
.OnActivated((IActivatedEventArgs<Log> e) =>
{
var product = e.Context.Resolve<Parent>();
e.Instance.SetParent(product);
});
_
これらのオプションにより、より詳細な制御が可能になり、@ stevenコメントを心配する必要がなくなります。
ただし、PropertiesAutowiredの恐ろしい点は、暗黙的なプロパティインジェクションを実行することです。つまり、解決できない依存関係はスキップされます。これにより、構成エラーを見逃しやすくなり、実行時にアプリケーションが失敗する可能性があります
私はpostharpを使いたくなかったので、簡単な解決策を作りましたが、自動注入はしません。私はAutofacを初めて使用しますが、このソリューションをさらに発展させることができるはずです。
[Serializable]
[AttributeUsage(AttributeTargets.Property)]
public class AutofacResolveAttribute : Attribute
{
}
public class AutofactResolver
{
/// <summary>
/// Injecting objects into properties marked with "AutofacResolve"
/// </summary>
/// <param name="obj">Source object</param>
public static void InjectProperties(object obj)
{
var propertiesToInject = obj.GetType().GetProperties()
.Where(x => x.CustomAttributes.Any(y => y.AttributeType.Name == nameof(AutofacResolveAttribute))).ToList();
foreach (var property in propertiesToInject)
{
var objectToInject = Autofact.SharedContainer.Resolve(property.PropertyType);
property.SetValue(obj, objectToInject, null);
}
}
}
この呼び出しで使用します。
AutofactResolver.InjectProperties(sourceObject);