4

私は自宅で栽培された "アスペクト"タイプのもの、実際は.net属性を使っていくつかの動作を適用しようとしています。私は起動時に "aspect"がどのように適用されているかを見るためにそれ自身を反映する基本クラス(BankingServiceBase)を持っています。その後、操作の前または後にカスタム動作を実行できます。私はIOCコンテナとしてAutofacを使用しています。私は、アスペクトの登録にPropertiesAutowiredメソッドを適用しようとしています。以下のサンプルコードでは、AutofacがILogインスタンスを自分のアスペクト/属性に挿入します。しかしそれはやっていません。私の推測では、GetCustomAttributesと呼ぶと、登録されたインスタンスをAutofacから取得するのではなく、新しいインスタンスが作成されているということです。思考?側面プロパティの依存関係を.net属性に挿入する方法は?

private readonly List<BankingServiceAspect> _aspects; 
private List<BankingServiceAspect> Aspects 
{ 
    get 
    { 
     if (_aspects == null) { 
      _aspects = GetType() 
       .GetCustomAttributes(typeof(BankingServiceAspect), true) 
       .Cast<BankingServiceAspect>() 
       .ToList(); 
     } 
     return _aspects; 
    } 
} 

の遅延ロードを実装するために

internal class Program 
{ 
    private static void Main() 
    { 
     var builder = new ContainerBuilder(); 

     builder 
      .RegisterType<ConsoleLog>() 
      .As<ILog>(); 

     builder 
      .RegisterType<BankingService>() 
      .As<IBankingService>(); 

     builder 
      .RegisterType<LogTransfer>() 
      .As<LogTransfer>() 
      .PropertiesAutowired(); 

     var container = builder.Build(); 

     var bankingService = container.Resolve<IBankingService>(); 

     bankingService.Transfer("ACT 1", "ACT 2", 180); 

     System.Console.ReadKey(); 
    } 

    public interface IBankingService 
    { 
     void Transfer(string from, string to, decimal amount); 
    } 

    public interface ILog 
    { 
     void LogMessage(string message); 
    } 

    public class ConsoleLog : ILog 
    { 
     public void LogMessage(string message) 
     { 
      System.Console.WriteLine(message); 
     } 
    } 

    [AttributeUsage(AttributeTargets.Class)] 
    public abstract class BankingServiceAspect : Attribute 
    { 
     public virtual void PreTransfer(string from, string to, decimal amount) 
     { 
     } 

     public virtual void PostTransfer(bool success) 
     { 
     } 
    } 

    public class LogTransfer : BankingServiceAspect 
    { 
     // Note: this is never getting set from Autofac! 
     public ILog Log { get; set; } 

     public override void PreTransfer(string from, string to, decimal amount) 
     { 
      Log.LogMessage(string.Format("About to transfer from {0}, to {1}, for amount {2}", from, to, amount)); 
     } 

     public override void PostTransfer(bool success) 
     { 
      Log.LogMessage(success ? "Transfer completed!" : "Transfer failed!"); 
     } 
    } 

    public abstract class BankingServiceBase : IBankingService 
    { 
     private readonly List<BankingServiceAspect> aspects; 

     protected BankingServiceBase() 
     { 
      // Note: My guess is that this "GetCustomAttributes" is happening before the IOC dependency map is built. 
      aspects = 
       GetType().GetCustomAttributes(typeof (BankingServiceAspect), true).Cast<BankingServiceAspect>(). 
        ToList(); 
     } 

     void IBankingService.Transfer(string from, string to, decimal amount) 
     { 
      aspects.ForEach(a => a.PreTransfer(from, to, amount)); 

      try 
      { 
       Transfer(from, to, amount); 
       aspects.ForEach(a => a.PostTransfer(true)); 
      } 
      catch (Exception) 
      { 
       aspects.ForEach(a => a.PostTransfer(false)); 
      } 
     } 

     public abstract void Transfer(string from, string to, decimal amount); 
    } 

    [LogTransfer] 
    public class BankingService : BankingServiceBase 
    { 
     public override void Transfer(string from, string to, decimal amount) 
     { 
      // Simulate some latency.. 
      Thread.Sleep(1000); 
     } 
    } 
} 
+0

-1の世界であなたがそのクラスを記述したいと思う理由それ自体に反映されていますか?ボックスからの傍受によってAOPをサポートするいくつかのコンテナがあります。限り、[クイックグーグル検索で表示](http://code.google.com/p/autofac/wiki/DynamicProxy2)Autofacもサポートしています。 –

答えて

0

てみてください、あなたは正しいです。この

Aspects.ForEach(a => a.PreTransfer(from, to, amount)); 
... 
+0

私はOliverを試しましたが、同じ問題があります(ログの依存関係はnullです)。私の前提とRichのコメントに基づいて、Autofacは基本的にMSILコードにハードコードされているので、属性に属性を注入する方法がないためです。 –

4

のようにそれを使用します。ここでは、問題を表示するには、いくつかの使用可能なサンプルコードがありますそのGetCustomAttributesは、Autofac経由でカスタム属性を解決しません。あなたはそれについて考えると、GetCustomAttributesなどのFCLコードがどのようにAutofacについて知っていますか?カスタム属性は実際にアセンブリメタデータから取得されるため、Autofacの解決プロセスを経由することはないため、登録コードは決して使用されません。

できることは、属性インスタンスにサービスを注入することです。アスペクト属性のリストを生成するには、Oliver's answerのコードから始めます。ただし、リストを返す前に、各属性を処理して、依存するフィールドとプロパティにサービスを注入できます。私はAttributedDependencyInjectorというクラスを持っています。私はこれを拡張メソッドで使います。反射を使用してInjectDependencyAttributeで装飾されたフィールドとプロパティをスキャンし、それらのプロパティの値を設定します。さまざまなシナリオに対応するためのコードはかなりたくさんありますが、ここにはあります。

属性クラス:

/// <summary> 
///  Attribute that signals that a dependency should be injected. 
/// </summary> 
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)] 
public sealed class InjectDependencyAttribute : Attribute 
{ 
    /// <summary> 
    ///  Initializes a new instance of the <see cref = "InjectDependencyAttribute" /> class. 
    /// </summary> 
    public InjectDependencyAttribute() 
    { 
     this.PreserveExistingValue = false; 
    } 

    /// <summary> 
    /// Gets or sets a value indicating whether to preserve an existing non-null value. 
    /// </summary> 
    /// <value> 
    /// <c>true</c> if the injector should preserve an existing value; otherwise, <c>false</c>. 
    /// </value> 
    public bool PreserveExistingValue { get; set; } 
} 

インジェクタクラス:

public class AttributedDependencyInjector 
{ 
    /// <summary> 
    /// The component context. 
    /// </summary> 
    private readonly IComponentContext context; 

    /// <summary> 
    /// Initializes a new instance of the <see cref="AttributedDependencyInjector"/> class. 
    /// </summary> 
    /// <param name="context">The context.</param> 
    public AttributedDependencyInjector(IComponentContext context) 
    { 
     this.context = context; 
    } 

    /// <summary> 
    /// Injects dependencies into an instance. 
    /// </summary> 
    /// <param name="instance">The instance.</param> 
    public void InjectDependencies(object instance) 
    { 
     this.InjectAttributedFields(instance); 
     this.InjectAttributedProperties(instance); 
    } 

    /// <summary> 
    /// Gets the injectable fields. 
    /// </summary> 
    /// <param name="instanceType"> 
    /// Type of the instance. 
    /// </param> 
    /// <param name="injectableFields"> 
    /// The injectable fields. 
    /// </param> 
    private static void GetInjectableFields(
     Type instanceType, ICollection<Tuple<FieldInfo, InjectDependencyAttribute>> injectableFields) 
    { 
     const BindingFlags BindingsFlag = 
      BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly; 
     IEnumerable<FieldInfo> fields = instanceType.GetFields(BindingsFlag); 

     // fields 
     foreach (FieldInfo field in fields) 
     { 
      Type fieldType = field.FieldType; 

      if (fieldType.IsValueType) 
      { 
       continue; 
      } 

      // Check if it has an InjectDependencyAttribute 
      var attribute = field.GetAttribute<InjectDependencyAttribute>(false); 
      if (attribute == null) 
      { 
       continue; 
      } 

      var info = new Tuple<FieldInfo, InjectDependencyAttribute>(field, attribute); 
      injectableFields.Add(info); 
     } 
    } 

    /// <summary> 
    /// Gets the injectable properties. 
    /// </summary> 
    /// <param name="instanceType"> 
    /// Type of the instance. 
    /// </param> 
    /// <param name="injectableProperties"> 
    /// A list into which are appended any injectable properties. 
    /// </param> 
    private static void GetInjectableProperties(
     Type instanceType, ICollection<Tuple<PropertyInfo, InjectDependencyAttribute>> injectableProperties) 
    { 
     // properties 
     foreach (var property in instanceType.GetProperties(
      BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)) 
     { 
      Type propertyType = property.PropertyType; 

      // Can't inject value types 
      if (propertyType.IsValueType) 
      { 
       continue; 
      } 

      // Can't inject non-writeable properties 
      if (!property.CanWrite) 
      { 
       continue; 
      } 

      // Check if it has an InjectDependencyAttribute 
      var attribute = property.GetAttribute<InjectDependencyAttribute>(false); 
      if (attribute == null) 
      { 
       continue; 
      } 

      // If set to preserve existing value, we must be able to read it! 
      if (attribute.PreserveExistingValue && !property.CanRead) 
      { 
       throw new BoneheadedException("Can't preserve an existing value if it is unreadable"); 
      } 

      var info = new Tuple<PropertyInfo, InjectDependencyAttribute>(property, attribute); 
      injectableProperties.Add(info); 
     } 
    } 

    /// <summary> 
    /// Determines whether the <paramref name="propertyType"/> can be resolved in the specified context. 
    /// </summary> 
    /// <param name="propertyType"> 
    /// Type of the property. 
    /// </param> 
    /// <returns> 
    /// <c>true</c> if <see cref="context"/> can resolve the specified property type; otherwise, <c>false</c>. 
    /// </returns> 
    private bool CanResolve(Type propertyType) 
    { 
     return this.context.IsRegistered(propertyType) || propertyType.IsAssignableFrom(typeof(ILog)); 
    } 

    /// <summary> 
    /// Injects dependencies into the instance's fields. 
    /// </summary> 
    /// <param name="instance"> 
    /// The instance. 
    /// </param> 
    private void InjectAttributedFields(object instance) 
    { 
     Type instanceType = instance.GetType(); 

     // We can't get information about the private members of base classes through reflecting a subclass, 
     // so we must walk up the inheritance hierarchy and reflect at each level 
     var injectableFields = new List<Tuple<FieldInfo, InjectDependencyAttribute>>(); 
     var type = instanceType; 
     while (type != null) 
     { 
      GetInjectableFields(type, injectableFields); 
      type = type.BaseType; 
     } 

     // fields 
     foreach (var fieldDetails in injectableFields) 
     { 
      var field = fieldDetails.Item1; 
      var attribute = fieldDetails.Item2; 

      if (!this.CanResolve(field.FieldType)) 
      { 
       continue; 
      } 

      // Check to preserve existing value 
      if (attribute.PreserveExistingValue && (field.GetValue(instance) != null)) 
      { 
       continue; 
      } 

      object fieldValue = this.Resolve(field.FieldType, instanceType); 
      field.SetValue(instance, fieldValue); 
     } 
    } 

    /// <summary> 
    /// Injects dependencies into the instance's properties. 
    /// </summary> 
    /// <param name="instance"> 
    /// The instance. 
    /// </param> 
    private void InjectAttributedProperties(object instance) 
    { 
     Type instanceType = instance.GetType(); 

     // We can't get information about the private members of base classes through reflecting a subclass, 
     // so we must walk up the inheritance bierarchy and reflect at each level 
     var injectableProperties = new List<Tuple<PropertyInfo, InjectDependencyAttribute>>(); 
     var type = instanceType; 
     while (type != typeof(object)) 
     { 
      Debug.Assert(type != null, "type != null"); 
      GetInjectableProperties(type, injectableProperties); 
      type = type.BaseType; 
     } 

     // Process the list and inject properties as appropriate 
     foreach (var details in injectableProperties) 
     { 
      var property = details.Item1; 
      var attribute = details.Item2; 

      // Check to preserve existing value 
      if (attribute.PreserveExistingValue && (property.GetValue(instance, null) != null)) 
      { 
       continue; 
      } 

      var propertyValue = this.Resolve(property.PropertyType, instanceType); 
      property.SetValue(instance, propertyValue, null); 
     } 
    } 

    /// <summary> 
    /// Resolves the specified <paramref name="propertyType"/> within the context. 
    /// </summary> 
    /// <param name="propertyType"> 
    /// Type of the property that is being injected. 
    /// </param> 
    /// <param name="instanceType"> 
    /// Type of the object that is being injected. 
    /// </param> 
    /// <returns> 
    /// The object instance to inject into the property value. 
    /// </returns> 
    private object Resolve(Type propertyType, Type instanceType) 
    { 
     if (propertyType.IsAssignableFrom(typeof(ILog))) 
     { 
      return LogManager.GetLogger(instanceType); 
     } 

     return this.context.Resolve(propertyType); 
    } 
} 

拡張メソッド:

public static class RegistrationExtensions 
{ 
    /// <summary> 
    /// Injects dependencies into the instance's properties and fields. 
    /// </summary> 
    /// <param name="context"> 
    /// The component context. 
    /// </param> 
    /// <param name="instance"> 
    /// The instance into which to inject dependencies. 
    /// </param> 
    public static void InjectDependencies(this IComponentContext context, object instance) 
    { 
     Enforce.ArgumentNotNull(context, "context"); 
     Enforce.ArgumentNotNull(instance, "instance"); 

     var injector = new AttributedDependencyInjector(context); 
     injector.InjectDependencies(instance); 
    } 
} 
+0

ありがとうございました。私もこのようなことをやろうと考えていました。私が持っている唯一の問題は、IOCコンテナをベースクラスに導入する必要があることです。これで、 "ILog"のようなものを解決するためにIOCコンテナを調べる必要があります。私はベースクラスがIOCコンテナを認識できないような解決策を見つけることを望んでいましたが、それは不可能かもしれません。 –

+0

'AttributedDependencyInjector'を基底クラスに注入することができます。おそらくもっと良い方法は、依存関係解決アスペクト属性の適切なコレクションを返す' AspectFactory'クラスです。 –

関連する問題