Dieser Artikel wurde maschinell übersetzt.

Innovation

Abfangfunktionen in Unity

Dino Esposito

image: Dino EspositoDes letzten Monats eingeführt ich kurz den abfangen Mechanismus im Unity 2.0 Dependency Injection-Container verwendet. Nachdem Sie zur Veranschaulichung der Prinzipien der zentralen Aspekt objektorientierte Programmierung (AOP), präsentiert ich ein konkretes Beispiel abgefangen, die wahrscheinlich sehr nahe an die Anforderungen von vielen Entwicklern heute ist.

Wollten Sie schon einmal das Verhalten des vorhandenen Codeabschnitt zu erweitern, ohne den Quellcode in irgendeiner Weise zu erreichen? Wollten Sie nur einige zusätzlichen Code, um den vorhandenen Code ausgeführt werden?

AOP wurde ein Verfahren bereitstellen, die aus anderen Bereichen Kerncode isoliert, die die Kern von Geschäftslogik crosscut erstellt. Einheit im 2.0 bietet ein Framework Microsoft .NET Framework-4-basierte erreichen Sie dies in erstaunlich schnell und einfach.

Um zu den Zweck dieses Artikels zur Nachverfolgung vollständig zu verstehen, müssen zunächst durch Aufsummieren, was ich letzten Monat erläutert. Wie Sie feststellen werden, im letzten Monat Code ich einige Annahmen gemacht und einige Komponenten standardmäßig verwendet. In diesem Monat werde ich Schritt zurück und ausführlicher erläutern, die Auswahlmöglichkeiten und Optionen, die in der Regel sowie die Art und Weise auftreten.

AOP in Unity

Nehmen Sie eine bereitgestellte Anwendung haben, wobei an einem bestimmten Punkt Sie geschäftsspezifischen Aktionen auszuführen. Einen Tag fragt der Kunden erweitern dieses Verhalten etwas mehr Arbeit durchführen. Sie nehmen den Quellcode, ändern und ein paar Stunden der Beratung von Zeit für die Codierung und testen die neuen Features in Rechnung zu stellen. Aber es wäre besser, wenn problemlos und ohne den vorhandenen Quellcode neue Verhaltensweisen hinzufügen können?

Genommen Sie an, ein etwas anderes Szenario. Zunächst, was geschieht, wenn Sie kein unabhängiger Berater, jedoch arbeiten vollständige Zeit für das Unternehmen? Die weitere Änderungsanforderungen, die mehrere Stunden erhalten Sie, die Sie außerhalb der aktuellen Projekt und, schlimmer noch, verbringen, riskieren Sie Bereiche Ihrer Codebasis erstellen neue (und nicht unbedingt erforderlich). Deshalb würden Sie Lösungen, mit die Sie neue Verhaltensweisen problemlos und keine Gefährdung des Quellcodes hinzufügen können, würde, heartily Willkommen.

Schließlich vorstellen, dass jemand einen Fehler oder eine schwerwiegende Leistungsprobleme meldet erreicht. Müssen Sie untersuchen und beheben Sie das Problem, und er absolut nicht störend sein soll. In diesem Fall auch wäre besser, fügen Sie neue Verhaltensweisen problemlos und keine Gefährdung des Quellcodes.

AOP hilft Ihnen in all diesen Szenarios.

Letzten Monat wurde ich wie Präbuild- und nachträgliche Verarbeitung Code, um eine vorhandene Methode hinzufügen, ohne es durch den wirksamen Einsatz das Abfangen API 2.0 Unity veranschaulicht. Dieser Demo anzusehen, versucht jedoch einige Annahmen.

Zunächst arbeitete er für einen Typ mit der Infrastruktur Unity Umkehrung des Steuerelements (IoC) registriert und über die Unity-Factory-Schicht instanziiert.

Zweitens wurde die Pointcut nur über eine Schnittstelle definiert. In der Fachsprache AOP stellt eine Pointcut eine Auflistung von Bereichen im Hauptteil der Zielklasse, in dem das Framework bei Bedarf zusätzliche Verhaltensweisen eingefügt werden.. Ein schnittstellenbasierten Pointcut bedeutet, dass nur die Member der Schnittstelle zur Laufzeit über Code-Injektion erweitert werden.

Drittens ich größtenteils konzentriert sich auf die Konfigurationseinstellungen aktivieren abfangen und ignoriert die fließend API, die Sie in Code Unity konfigurieren kann.

Im Rest dieses Artikels werde ich die fließend API und alternative Möglichkeiten für die Definition von Unity-Interceptors untersuchen.

Interceptable Instanzen

Um ein neues Verhalten auf eine vorhandene oder neu erstellte Instanz einer Klasse hinzuzufügen, müssen Sie einige Kontrolle über die Factory ausführen. Mit anderen Worten, AOP ist nicht rein magische und Sie werden nicht in der Lage, Einbinden einer einfachen CLR-Klasse, die über den standardmäßigen new-Operator instanziiert:

var calculator = new Calculator();

Die Art und Weise, in der ein Framework AOP Kontrolle über die Instanz erhält, kann durchaus etwas abweichen. In Unity können Sie auf einige explizite Aufrufe zurückgreifen, die einen Proxy für das ursprüngliche Objekt zurückzugeben, oder behalten die ganze Sache, die hinter einem IoC-Framework ausgeführt. Aus diesem Grund bieten die meisten IoC-Frameworks AOP-Funktionen. Spring.NET und Unity sind zwei Beispiele. Wenn AOP IoC erfüllt, ist das Ergebnis eine nahtlose, einfache und effektive Codierung Erfahrung.

Starten Sie Let’s mit einem Beispiel, in dem keine IoC-Funktionen verwendet werden. Hier werden einige grundlegender Code, der eine vorhandene Instanz der Klasse des Rechners interceptable macht:

var calculator = new Calculator();
var calculatorProxy = Intercept.ThroughProxy<ICalculator>(calculator,
  new InterfaceInterceptor(), new[] { new LogBehavior() });
Console.WriteLine(calculatorProxy.Sum(2, 2));

Sie landen arbeiten mit einem interceptable Proxy, das das ursprüngliche Objekt. In diesem Fall bin ich davon aus, dass die Klasse des Rechners ICalculator-Schnittstelle implementiert. Interceptable werden muss eine Klasse entweder eine Schnittstelle implementieren oder erben von MarshalByRefObject abgeleitet. Wenn die Klasse von MarshalByRefObject abgeleitet wird, muss der Interceptor vom Typ TransparentProxyInterceptor sein:

var calculator = new Calculator();
var calculatorProxy = Intercept.ThroughProxy(calculator,
  new TransparentProxyInterceptor(), new[] { new LogBehavior() });
Console.WriteLine(calculatorProxy.Sum(2, 2));

Intercept-Klasse bietet auch eine NewInstance-Methode zum Erstellen eines interceptable Objekts eine direktere Weise aufgerufen werden kann. Hier ist seine Verwendung:

var calculatorProxy = Intercept.NewInstance<Calculator>(
  new VirtualMethodInterceptor(), new[] { new LogBehavior() });

Hinweis: Wenn Sie NewInstance die Komponente Interceptor hat etwas anders sein – weder InterfaceInterceptor noch TransparentProxyInterceptor, sondern vielmehr ein VirtualMethodInterceptor-Objekt. Vorhanden so wie viele Arten von Interceptors in Unity?

Instanz und Type-Interceptors

Ein Interceptor ist verantwortlich für den ursprünglichen Aufruf an das Zielobjekt zu erfassen und routing über eine Pipeline Verhaltensweisen, so dass jedes Verhalten seine vor oder ausgeführt werden kann, nach dem normalen Methodenaufruf Unity-Komponente. Abfangen von zwei Typen sind möglich: Instanz-abfangen und Typ abfangen.

Instanz-Interceptors erstellen einen Proxy so filtern Sie eingehende Anrufe an die abgefangenen Instanz gerichtet. Typ-Interceptors generiert eine neue Klasse abgeleitet vom Typ abfangen und Arbeit auf eine Instanz der abgeleiteten Typ. Selbstverständlich ist das Delta zwischen der ursprünglichen und der abgeleitete Typ alle die notwendige Logik, um eingehende Anrufe zu filtern.

Im Falle einer Instanz abfangen den Anwendungscode erstellt zunächst das Zielobjekt, das mit einer klassischen Factory (oder des new-Operators) und wird dann zur Interaktion mit über den Proxy von Unity bereitgestellten erzwungen.

Im Fall des Typs abfangen die Anwendung das Zielobjekt über die API oder Unity erstellt und dann arbeitet mit dieser Instanz. (Sie können keine erstellen Sie das Objekt direkt mit dem new-Operator und Art abfangen zu erhalten.) Das Zielobjekt ist von den ursprünglichen Typ jedoch nicht. Der tatsächliche Typ wird zur Laufzeit durch Unity abgeleitet und fügt abfangen Logik (siehe Abbildung 1 ).

image: Instance Interceptor and Type Interceptor

Abbildung 1 Instance-Interceptors und Type-Interceptors

InterfaceInterceptor und TransparentProxyInterceptor sind zwei Unity-Interceptors, die die Instanz Interceptor Kategorie angehören. VirtualMethodInterceptor gehört zur Kategorie Interceptor.

InterfaceInterceptor kann nur eine Schnittstelle für das Zielobjekt öffentliche Instanzmethoden abfangen. Der Interceptor kann für neue und vorhandene Instanzen angewendet werden.

TransparentProxyInterceptor kann öffentliche Instanzmethoden für mehr als eine Schnittstelle und Marshal-by-Reference-Objekte abgefangen werden. Hierbei handelt es sich um den langsamsten Ansatz zum Abfangen, aber den breitesten Satz von Methoden abgefangen werden können. Der Interceptor kann für neue und vorhandene Instanzen angewendet werden.

Virtuelle Methoden, öffentliche und geschützte kann VirtualMethodInterceptor abgefangen werden. Der Interceptor kann nur auf neue Instanzen angewendet werden.

Es sollte beachtet werden, dass Instanz Abfangen von public-Instanz-Methoden jedoch keine Konstruktoren angewendet werden kann. Das ist ziemlich offensichtlich, für das Szenario, in dem Abfangen einer vorhandenen Instanz gilt. Es ist weniger offensichtlich, wenn etwas Abfangen einer neu erstellten Instanz angewendet wird. Die Implementierung der Instanz abgefangen wird, so dass der Konstruktor der Anwendungscode ruft wieder ab, ein Objekt mit bereits durch die Zeit ausgeführt wurde. Maßnahmen interceptable folgt daher unbedingt das Erstellen der Instanz.

Abfangen von Typ verwendet dynamische Codegenerierung, um ein Objekt zurückzugeben, das von den ursprünglichen Typ erbt. Auf diese Weise werden alle öffentlichen und geschützten virtuellen Methoden zur Unterstützung von abfangen überschrieben. Betrachten Sie den folgenden Code:

var calculatorProxy = Intercept.NewInstance<Calculator>(
  new VirtualMethodInterceptor(), new[] { new LogBehavior() });

Rechner-Klasse sieht folgendermaßen aus:

public class Calculator {
  public virtual Int32 Sum(Int32 x, Int32 y) {
    return x + y;
  }
}

Abbildung 2 zeigt den tatsächlichen Namen des Typs, die durch eine dynamische Prüfung der CalculatorProxy Variablen.

image: Actual Type After Type Interception

Abbildung 2 Aktueller Typ nach Art abfangen

Es ist außerdem zu beachten, dass einige wesentliche Unterschiede zwischen Instanz- und Typ abgefangen werden, z. B. Aufrufe abgefangen, durch das Objekt selbst. Wenn der Aufruf einer Methode eine andere Methode für dasselbe Objekt ist bei der Verwendung des Typs abfangen, dann das self-call kann abgefangen werden, da die Abfangen von Logik in dasselbe Objekt ist. Geschieht jedoch mit Instanz abfangen, das Abfangen nur, wenn der Aufruf über den Proxy geht. Self-Calls, natürlich nicht über den Proxy wechseln, und daher keine abfangen geschieht.

Mithilfe den IoC-Container

Im letzten Monat Beispiel habe ich IoC-Container der Bibliothek Unity Objekterstellung kümmern. IoC-Container ist eine zusätzliche Schicht um Objekterstellung, die Flexibilität der Anwendung hinzugefügt. Dies ist sogar truer, wenn Sie mit zusätzlichen Funktionen für AOP IoC-Frameworks in Betracht ziehen. Darüber hinaus – und wie ich die Dinge sehen – das Maß an Flexibilität Code hinter Vorstellungskraft wächst, auch Kombinieren von IoC-Container mit offline-Konfiguration. Allerdings let’s beginnen mit ein Beispiel, das die Unity-Container mit codebasierte fließend Konfiguration verwendet:

// Configure the IoC container
var container = UnityStarter.Initialize();

// Start the application
var calculator = container.Resolve<ICalculator>();
var result = calculator.Sum(2, 2);

Jeder Code erforderlich, den Container neu gestartet kann in eine unterschiedliche Klasse isoliert und einmal beim Start der Anwendung aufgerufen werden. Der bootstrap-Code wird dem Container angewiesen, wie Typen, um die Anwendung zu beheben und den Umgang mit abfangen. Ein Aufruf an die Resolve-Methode, die Sie von den Details des abfangen abschirmt. Abbildung 3 zeigt eine mögliche Implementierung des bootstrap-Codes.

Abbildung 3 Bootstrapping Unity

public class UnityStarter {
  public static UnityContainer Initialize() {
    var container = new UnityContainer();

    // Enable interception in the current container 
    container.AddNewExtension<Interception>();

    // Register ICalculator with the container and map it to 
    // an actual type.
In addition, specify interception details.
container.RegisterType<ICalculator, Calculator>(
      new Interceptor<VirtualMethodInterceptor>(),
      new InterceptionBehavior<LogBehavior>());

    return container;
  }
}

Die schöne Sache ist, dass dieser Code kann in einer separaten Assembly verschoben und geladen oder dynamisch geändert. Noch wichtiger ist, verfügen Sie über eine zentrale Stelle zum Konfigurieren von Unity. Dies wird nicht passieren, solange Sie Intercept-Klasse verwenden, die verhält sich wie eine smart Factory und jedes Mal, wenn Sie vorbereitet werden muss. Also AOP in Ihren Anwendungen erhalten Sie von mehr als einem Fall erhalten Sie über IoC-Container. Die gleiche Projektmappe kann durch die Konfigurationsdetails aus der app.config-Datei (oder web.config) zu verschieben, ist eine Webanwendung noch flexibler Weise implementiert werden. In diesem Fall umfasst die folgenden beiden Zeilen der bootstrap-Code:

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

Abbildung 4 zeigt das Skript in der Konfigurationsdatei ist erforderlich. Hier registriert ich zwei Verhaltensweisen für den Typ ICalculator. Dies bedeutet, dass Aufrufe an die öffentlichen Member der Schnittstelle vor und nach LogBehavior und BinaryBehavior post-processed.

Abbildung 4 Hinzufügen abfangen Details über die Konfiguration von

<unity xmlns="https://schemas.microsoft.com/practices/2010/unity">
  <assembly name="SimplestWithConfigIoC"/>
  <namespace name="SimplestWithConfigIoC.Calc"/>
  <namespace name="SimplestWithConfigIoC.Behaviors"/>

  <sectionExtension 
    type="Microsoft.Practices.Unity.
InterceptionExtension.Configuration.
InterceptionConfigurationExtension,     
      Microsoft.Practices.Unity.Interception.Configuration" />

  <container>
    <extension type="Interception" />

    <register type="ICalculator" mapTo="Calculator">
      <interceptor type="InterfaceInterceptor"/>
      <interceptionBehavior type="LogBehavior"/>
      <interceptionBehavior type="BinaryBehavior"/>
    </register>

    <register type="LogBehavior">
    </register>

    <register type="BinaryBehavior">
    </register>

  </container>
</unity>

Beachten Sie, dass da LogBehavior und BinaryBehavior konkrete Typen sind, Sie tatsächlich benötigen Sie überhaupt zu registrieren. Standardwerte für die Einheit im funktioniert automatisch für Sie.

Verhalten

In Unity sind Verhaltensweisen für Objekte, die die Aspekte Crosscutting tatsächlich zu implementieren. Eine Klasse, die die Schnittstelle IInterceptionBehavior ein Verhalten implementiert kann Neuschreiben den Ausführung Zyklus der abgefangenen-Methode und ändern Methodenparameter oder Rückgabewerte. Verhaltensweisen können sogar beenden die Methode aufgerufen wird oder diese mehrfach aufrufen.

Ein Verhalten besteht aus drei Methoden. Abbildung 5 zeigt ein Beispiel-Verhalten, das die Sum-Methode abgefangen und schreibt den Rückgabewert als binäre Zeichenfolge. Die WillExecute-Methode ist einfach eine Möglichkeit, um den Proxy zu optimieren. Wenn false zurückgegeben wird, wird nicht das Verhalten führen.

Abbildung 5 A Sample Verhalten

public class BinaryBehavior : IInterceptionBehavior {
  public IEnumerable<Type> GetRequiredInterfaces() {
    return Type.EmptyTypes;
  }

  public bool WillExecute {
    get { return true; }
  }

  public IMethodReturn Invoke(
    IMethodInvocation input, 
    GetNextInterceptionBehaviorDelegate getNext) {

    // Perform the operation
    var methodReturn = getNext().Invoke(input, getNext);

    // Grab the output
    var result = methodReturn.ReturnValue;

    // Transform
    var binaryString = ((Int32)result).ToBinaryString();

    // For example, write it out
    Console.WriteLine("Rendering {0} as binary = {1}", 
      result, binaryString);

    return methodReturn;
  }
}

Dies ist tatsächlich etwas subtileres. Rufen Sie wird immer aufgerufen werden, so dass Ihr Verhalten tatsächlich ausgeführt wird, auch wenn Sie false zurückgegeben. Allerdings wird bei der Proxy oder einen abgeleiteten Typ erstellt die Verhaltensweisen, die für den Typ registriert haben WillExecute-Gruppe auf False festgelegt, der Proxy selbst wird nicht erstellt werden, und Sie werden Arbeiten mit dem raw-Objekt erneut. Der tatsächlich zum Optimieren von Proxy erstellen.

Die GetRequiredInterfaces-Methode ermöglicht es, das Verhalten des Zielobjekts neue Schnittstellen hinzu;Schnittstellen, die von dieser Methode zurückgegebene werden der Proxy hinzugefügt werden. Daher ist der Kern des ein Verhalten der Invoke-Methode. Die Parametereingabe erhält Zugriff auf die Methode, die aufgerufen wird, für das Zielobjekt. Der Parameter GetNext ist der Delegat für Sie in das nächste Verhalten in der Pipeline und schließlich auf dem Zielcomputer Ausführen der Methode.

Die Invoke-Methode bestimmt die eigentliche Logik verwendet, um einen Aufruf an eine öffentliche Methode für das Zielobjekt auszuführen. Beachten Sie, dass alle Methoden für das Zielobjekt abgefangen wird ausgeführt, nach der Logik, ausgedrückt in aufrufen.

Was geschieht, wenn möchten Sie spezielle Zuordnungsregeln verwenden? Mit einfachen abfangen wie in diesem Artikel beschrieben ausführen ausführen über eine Vielzahl von IF-Anweisungen, um herauszufinden, welche Methode tatsächlich aufgerufen wird, wie folgt:

if(input.MethodBase.Name == "Sum") {
  ...
}

Nächsten Monat werde hier besprechen Sie effektiver Weise abfangen gelten, und definieren Sie die entsprechende Regeln für Methoden abgefangene fortgesetzt werden.

Dino Esposito is the author of “Programming Microsoft ASP.NET MVC” (Microsoft Press, 2010) and coauthored “Microsoft .NET: Architecting Applications for the Enterprise" (Microsoft Press, 2008) beteiligt. Esposito lebt in Italien und ist ein weltweit gefragter Referent bei Branchenveranstaltungen. Sie finden seinen Blog unter weblogs.asp.net/despos.

Dank an den folgenden technischen Experten für die Überprüfung der in diesem Artikel: Chris Tavares