2013-06-13 18 views
7

Gibt es eine Möglichkeit, wie Abfangen von Attribut in C# Einheit verwenden und den Objekt Registrierungscode in XML-Datei (wie app.config) beibehalten? Wenn ja, können Sie mir einen Code zur Verfügung stellen, wie sollte eine solche Registrierung aussehen? Ich habe viele Workarounds gemacht, aber keine funktionierende Lösung für dieses Problem gefunden.C# Einheit Abfangen von Attribut

Antwort

14

Ich nehme an, Sie meinen, mit einem benutzerdefinierten Attribut anzugeben, welche Methoden abzufangen sind. Sie können mithilfe der Richtlinieninjektion das Abfangen mithilfe der XML-Konfiguration erreichen.

Lassen Sie uns zunächst ein benutzerdefiniertes Attribut definieren:

[AttributeUsage(AttributeTargets.Method)] 
public class MyInterceptionAttribute : Attribute 
{ 
} 

Als nächstes werden wir einen ICallHandler erstellen können einige Abfangen Arbeit zu tun. Diese Implementierung wird einfach eine Console.WriteLine vor und nach der Methode:

public class MyLoggingCallHandler : ICallHandler 
{ 
    IMethodReturn ICallHandler.Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) 
    { 
     Console.WriteLine("Invoking " + input.MethodBase.Name); 
     IMethodReturn result = getNext()(input, getNext); 
     Console.WriteLine("Done Invoke"); 
     return result; 
    } 

    int ICallHandler.Order { get; set; } 
} 

Als nächstes nehmen wir an, dass wir eine Schnittstelle und eine Implementierung haben:

public interface IMyClass 
{ 
    void Do(); 
    void DoAgain(); 
} 

public class MyClass : IMyClass 
{ 
    [MyInterception] 
    public void Do() 
    { 
     Console.WriteLine("Do!"); 
    } 

    public void DoAgain() 
    { 
     Console.WriteLine("Do Again!"); 
    } 
} 

Beachten Sie, dass ich das benutzerdefinierte Attribut angewendet haben , MyInterception, nur für die Do-Methode, nicht aber für die DoAgain-Methode. Wir werden alle Aufrufe der Do-Methode abfangen.

Als Nächstes erstellen wir die Konfiguration, eine Politik zu definieren, die passende Regel konfigurieren und die Art zusammen mit einem Interceptor registrieren:

<?xml version="1.0"?> 
<configuration> 
    <configSections> 
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/> 
    </configSections> 
    <unity xmlns="http://schemas.microsoft.com/practices/2010/unity"> 
    <namespace name="UnityCallHandlerConfig" /> 
    <assembly name="UnityCallHandlerConfig" /> 
    <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration"/> 
    <container> 
     <extension type="Interception"/> 
     <interception> 
     <policy name="Policy"> 
      <matchingRule name="Match" type="Microsoft.Practices.Unity.InterceptionExtension.CustomAttributeMatchingRule, Microsoft.Practices.Unity.Interception"> 
      <constructor> 
       <param name="attributeType" value="UnityCallHandlerConfig.MyInterceptionAttribute, UnityCallHandlerConfig" typeConverter="AssemblyQualifiedTypeNameConverter" /> 
       <param name="inherited"> 
       <value value="false"/> 
       </param> 
      </constructor> 
      </matchingRule> 
      <callHandler name="MyLogging" type="MyLoggingCallHandler"> 
      <lifetime type="singleton"/> 
      </callHandler> 
     </policy> 
     </interception> 
     <register type="IMyClass" mapTo="MyClass"> 
     <interceptor type="InterfaceInterceptor"/> 
     <interceptionBehavior type="PolicyInjectionBehavior"/> 
     </register> 
    </container> 
    </unity> 

    <startup> 
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/> 
    </startup> 
</configuration> 

Wir haben auch einen Typkonverter benötigen die Stringdarstellung des benutzerdefinierten Attributs zu konvertieren die richtige Art:

public class AssemblyQualifiedTypeNameConverter : ConfigurationConverterBase 
{ 
    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) 
    { 
     if (value != null) 
     { 
      Type typeValue = value as Type; 
      if (typeValue == null) 
      { 
       throw new ArgumentException("Cannot convert type", typeof(Type).Name); 
      } 

      if (typeValue != null) return (typeValue).AssemblyQualifiedName; 
     } 
     return null; 
    } 

    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) 
    { 
     string stringValue = (string)value; 
     if (!string.IsNullOrEmpty(stringValue)) 
     { 
      Type result = Type.GetType(stringValue, false); 
      if (result == null) 
      { 
       throw new ArgumentException("Invalid type", "value"); 
      } 

      return result; 
     } 
     return null; 
    } 
} 

Sobald wir haben alles, was wir einen Container einrichten und laden Sie die Konfiguration erstellen:

var container = new UnityContainer().LoadConfiguration(); 

var myClass = container.Resolve<IMyClass>(); 
myClass.Do(); 
myClass.DoAgain(); 

Der Ausgang wird sein:

Invoking Do 
Do! 
Done Invoke 
Do Again! 

die zeigen, dass das erste Verfahren abgefangen wird, während der zweite nicht ist.

+0

Vielen Dank für Ihre Hilfe! –

+2

@Tuzo, wie sieht die Konfiguration in C# code aus? Brauchen Sie 'AssemblyQualifiedTypeNameConverter' noch, wenn es in Code gemacht wird? –