Este artículo proviene de un motor de traducción automática.
Tecnología de vanguardia
Inserción de directivas en Unity
Dino Esposito
. Unidad no es una excepción. El propósito principal de AOP es permitir que los desarrolladores que más eficazmente tratan transversales, preocupaciones. En esencia, AOP responde a la pregunta siguiente: cuando se diseña un modelo de objetos de una aplicación, ¿cómo afrontar con aspectos del código como seguridad, almacenamiento en caché o la sesión? Estos aspectos son la clave para la implementación, pero Don ' t pertenecen estrictamente a los objetos en el modelo que esté creando. ¿Debe puedan alterar el diseño para incorporar los aspectos de ajenos al negocio? ¿O mejor que la decoración de sus clases orientadas a negocios con aspectos adicionales? Si elige esto último, AOP básicamente proporciona una sintaxis para definir y adjuntar estos aspectos.
Un aspecto es la implementación de un problema de crosscutting. En la definición de un aspecto, debe especificar algunas cosas. En primer lugar, deberá proporcionar el código de la preocupación que implemente. En la jerga AOP, esto se conoce como el aviso. Un aviso se aplica a un punto específico de código, si el cuerpo de un método, el captador o establecedor de una propiedad o quizás un controlador de excepciones. Esto se conoce como punto de unión. Por último, en la jerga AOP, encontrará pointcuts. Un pointcut representa una colección de puntos de unión. Normalmente, pointcuts se definen según los criterios que utilice caracteres comodín y los nombres de métodos. En última instancia, AOP actúa en el tiempo de ejecución para inyectar el código de los consejos antes, después y alrededor del punto de unión. Un aviso, a continuación, se asocia con un pointcut.
En los artículos anteriores, han examinado la interceptación de API de Unity. La API le permite definir aconsejarles para adjuntar a clases. En la jerga de unidad, el Consejo es un objeto de comportamiento. Generalmente se asocia el comportamiento a un tipo que se resuelve mediante el mecanismo de IoC de unidad, aunque el mecanismo de interceptación estrictamente no requiere la funcionalidad de IoC. De hecho, puede configurar la interceptación también se apliquen a las instancias creadas a través del código normal.
Un comportamiento consiste en una clase que implementa una interfaz fija, la interfaz IInterceptionBehavior. La interfaz incluye un método denominado Invoke. Al reemplazar este método, define realmente los pasos que se va a ejecutar antes o después de la llamada al método regular, o ambos. Puede adjuntar un comportamiento a un tipo mediante código fluent así como una secuencia de comandos de configuración. De esta forma, tiene que hacer es definir un punto de unión. Pero, ¿pointcuts?
Como vimos el mes pasado, todos los métodos interceptados en el objeto de destino se ejecutarán con arreglo a la lógica que se expresa en el método Invoke del objeto de comportamiento. La API de interceptación básica no proporciona la capacidad de distinguir entre los métodos y no es compatible con las reglas de coincidencia específicas. Para obtener esto, podrán recurrir a la API de inyección de directivas.
PIAB así como la inyección de directivas
Si ha utilizado versiones de Microsoft Enterprise Library (EntLib) anterior a la versión más reciente, 5.0, es posible que haya oído hablar acerca de bloque de aplicación de inyección directivas (PIAB) y lo más probable es que también ha realizado esta ventaja en algunas de las aplicaciones. EntLib 5.0 también dispone de un módulo PIAB. ¿Cuál es la diferencia entre la inserción de la directiva de unidad y EntLib PIAB?
En EntLib 5.0, PIAB existe principalmente por razones de compatibilidad. Puede cambiar el contenido del ensamblado PIAB en la nueva versión. En particular, toda la maquinaria para la intercepción ahora es parte de la unidad y llamada de proporcionados por el sistema todos los controladores en versiones anteriores de EntLib se movieron a otros ensamblados, como se muestra en de la figura 1.
Figura 1 de refactorización de controladores de la llamada en la biblioteca de Microsoft Enterprise 5.0
Llamar al controlador | Nuevo ensamblado de Enterprise Library 5.0 |
Controlador de autorización | Microsoft.Practices.EnterpriseLibrary.Security.dll |
Controlador de control de almacenamiento en caché | Quita de PIAB |
Controlador de control de excepciones | Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.dll |
Controlador de registro | Microsoft.Practices.EnterpriseLibrary.Logging.dll |
Controlador de contador de rendimiento | Microsoft.Practices.EnterpriseLibrary.PolicyInjection.dll |
Controlador de validación | Microsoft.Practices.EnterpriseLibrary.Validation.dll |
Como puede ver en de la figura 1, cada controlador de la llamada se movió al ensamblado del bloque de aplicación asociada. Así que el controlador de llamada de control de excepciones que se mueve al bloque de aplicaciones de control de excepciones y el controlador de validación se mueve al bloque de aplicación de la validación y así sucesivamente. La única excepción a esta regla se ha producido el controlador de contador de rendimiento, que se movió al ensamblado PolicyInjection. Aunque puede cambiar los ensamblados, seguía siendo igual que el espacio de nombres de las clases. También merece la pena tener en cuenta que, debido a problemas de seguridad, el controlador de llamada almacenamiento en caché PIAB incluido previamente se ha quitado EntLib 5.0 y están disponible sólo a través del sitio Web de CodePlex de mantenimiento del hogar de EntLib en bit.ly/gIcP6H. El efecto neto de estos cambios es que PIAB ahora consta de antiguos componentes disponible sólo para compatibilidad con versiones anteriores que aún requieren algunos cambios en el código para compilar con la versión 5.0. A menos que tenga graves dependencias heredadas, el enfoque recomendado es la que hace el upgrade capas de la inyección de directivas para aprovechar las ventajas de la API de inyección de directivas nuevo (y muy similares) preparado en el bloque de aplicaciones de unidad. Lo podemos averiguar más acerca de la inyección de directivas en la unidad.
Inserción de la directiva de un vistazo
La inserción de directiva es una capa de código que extiende la interceptación de la unidad básica API para agregar reglas de asignación y llamar a los controladores en una base de cada método. Se implementa como un comportamiento especial de interceptación, inyección de directiva consta de dos fases principales: inicialización y el tiempo de ejecución.
Durante la fase de inicialización, el marco de trabajo determina cuál de las directivas disponibles, se puede aplicar al método de destino sea interceptado. En este contexto, una directiva se describe como un conjunto de operaciones que puede insertarse en un orden determinado, entre el objeto a la interceptación y el llamador real. Sólo puede interceptar los métodos en objetos (instancias existentes o recién creadas instancias) que se hayan configurado explícitamente para la inserción de la directiva.
Tener imaginado de la lista de las directivas aplicables, el marco de la inyección de directivas prepara la canalización de operaciones (una operación se conoce como un controlador de la llamada). Los resultados de la canalización de la combinación de todos los controladores definidos para cada una de las directivas coincidentes. Los controladores de la canalización se ordenan en función del orden de la directiva y la prioridad asignada a cada identificador de la directiva principal. Cuando se invoca un método de políticas, se procesa la canalización generada anteriormente. Si el método, a su vez, coloca las llamadas a otros métodos de la directiva habilitada en el mismo objeto, se combinan las tuberías de controlador de esos métodos en la tubería principal.
Llamar a los controladores
Un controlador de llamada es más específico que un "comportamiento" y parece realmente un aviso, según se definió originalmente en AOP. Considerando que un comportamiento se aplica a un tipo y deja la carga de emprender acciones diferentes para distintos métodos, se especifica un controlador de llamada en una base de cada método.
Los controladores de llamada son redactados en una tubería y se invoca en un orden predeterminado. Cada controlador es capaz de obtener acceso a detalles de la llamada, incluido el nombre de método, los parámetros, valores devueltos y tipo de valor devuelto esperado. Un controlador de llamada también puede modificar los parámetros y valores devueltos, se detiene la propagación de la llamada a la canalización y provoca una excepción.
Resulta interesante observar que Unity no incluye todos los controladores de la llamada. Sólo puede crear los suyos propios, o hacer referencia a los bloques de aplicación de EntLib 5.0 y utilizar cualquiera de los controladores de llamada enumerados en de la figura 1.
Un controlador de llamada es una clase que implementa la interfaz ICallHandler, similar al siguiente:
public interface ICallHandler
{
IMethodReturn Invoke(
IMethodInvocation input,
GetNextHandlerDelegate getNext);
int Order { get; set; }
}
La propiedad Order indica la prioridad de este controlador relacionados con todos los demás. El método Invoke devuelve una instancia de una clase que contiene cualquier valor devuelto del método.
La implementación de un controlador de llamada es bastante sencilla en el sentido de que tiene sólo se espera realizar sus propias acciones concretas y, a continuación, deje que la canalización de ir. Para producir el control al siguiente controlador de la canalización, el controlador llama el parámetro getNext recibe desde el tiempo de ejecución de la unidad. El parámetro getNext es un delegado que se define como:
public delegate InvokeHandlerDelegate GetNextHandlerDelegate();
A su vez, el InvokeHandlerDelegate se define como:
public delegate IMethodReturn InvokeHandlerDelegate(
IMethodInvocation input,
GetNextHandlerDelegate getNext);
La documentación de la unidad proporciona un claro diagrama que ilustra la interceptación. En de la figura 2, verá un diagrama ligeramente modificado que presenta la arquitectura de inyección de directivas.
Figura 2 de la canalización de controlador de llamada en la inyección de la directiva de unidad
Dentro de los límites de un comportamiento de inyección de directivas proporcionados por el sistema, verá la cadena de controladores para procesar un método dado ha invocado en un objeto proxy o la clase derivada. Para completar la visión general de inyección de directivas en la unidad, necesitamos echar un vistazo a las reglas de coincidencia.
Las reglas de coincidencia
A través de una regla coincidente, se especifica dónde aplicar la lógica de interceptación. Si utiliza comportamientos, el código se aplica a todo el objeto;con una o más reglas coincidentes, puede definir un filtro. Una regla coincidente, indica un criterio para seleccionar objetos y miembros a los que Unity adjuntará una canalización de controlador. Con la terminología AOP, una regla coincidente es el criterio que se utiliza para definir la pointcuts. Figura 3 enumera las reglas de coincidencia Unity admitidas originalmente.
Figura 3 de lista de reglas de coincidencia admitidas en Unity 2.0
Regla de coincidencia | Descripción |
AssemblyMatchingRule | Selecciona como destino objetos basados en tipos del ensamblado especificado. |
CustomAttributeMatchingRule | Permite seleccionar objetos según un atributo personalizado en el nivel de miembro de destino. |
MemberNameMatchingRule | Permite seleccionar objetos según el nombre del miembro de destino. |
MethodSignatureMatchingRule | Selecciona como destino objetos basados en la firma de the. |
NamespaceMatchingRule | Selecciona como destino objetos basados en el espacio de nombres the. |
ParameterTypeMatchingRule | Permite seleccionar objetos según el nombre de un parámetro de tipo para un miembro de destino. |
PropertyMatchingRule | Permite seleccionar objetos según el miembro names, incluyendo los caracteres comodín como destino. |
ReturnTypeMatchingRule | Permite seleccionar objetos según el tipo de valor devuelto de destino. |
TagMatchingRule | Permite seleccionar objetos según el valor asignado a un atributo Tag ad hoc de destino. |
TypeMatchingRule | Permite seleccionar objetos según el nombre del tipo de destino. |
Una regla coincidente es una clase que implementa la interfaz IMatchingRule. Al contar con estos conocimientos, veamos cómo trabajar con la inyección de directivas. Hay esencialmente tres formas en el que puede definir directivas: mediante atributos, mediante código fluent y a través de la configuración.
Agregar directivas a través de atributos
Figura 4 se muestra a un controlador de llamada de ejemplo que se produce una excepción si el resultado de una operación es negativo. Vamos a usar este controlador de la misma en varios escenarios.
Figura 4 de La clase NonNegativeCallHandler
public class NonNegativeCallHandler : ICallHandler
{
public IMethodReturn Invoke(IMethodInvocation input,
GetNextHandlerDelegate getNext)
{
// Perform the operation
var methodReturn = getNext().Invoke(input, getNext);
// Method failed, go ahead
if (methodReturn.Exception != null)
return methodReturn;
// If the result is negative, then throw an exception
var result = (Int32) methodReturn.ReturnValue;
if (result <0)
{
var exception = new ArgumentException("...");
var response = input.CreateExceptionMethodReturn(exception);
// Return exception instead of original return value
return response;
}
return methodReturn;
}
public int Order { get; set; }
}
Utilizar el controlador de la forma más sencilla es mediante la conexión a cualquier método que piensa que puede ser útil. Para ello, necesitará un atributo, como:
public class NonNegativeCallHandlerAttribute : HandlerAttribute
{
public override ICallHandler CreateHandler(
IUnityContainer container)
{
return new NonNegativeCallHandler();
}
}
Aquí es una clase de la Calculadora de ejemplo que se decora con directivas basadas en el atributo:
public class Calculator : ICalculator
{
public Int32 Sum(Int32 x, Int32 y)
{
return x + y;
}
[NonNegativeCallHandler]
public Int32 Sub(Int32 x, Int32 y)
{
return x - y;
}
}
El resultado es que las llamadas al método suma continúe la forma habitual, independientemente del valor devuelto, mientras que las llamadas al método Sub producirá una excepción si un número negativo se devuelve.
Utilizando código Fluent
Si se Don ' t, como atributos, se puede expresar la misma lógica a través de una API intuitiva. En este caso, debe proporcionar muchos más detalles en cuanto se refieran a las reglas de coincidencia. Vamos a ver cómo se pueden expresar la idea de que se desea insertar el código sólo en métodos que devuelven un Int32 y se denominan Sub. Utiliza la API fluent para configurar el contenedor de unidad (consulte de la figura 5).
Figura 5 de Fluent código para definir un conjunto de reglas de coincidencia
public static UnityContainer Initialize()
{ // Creating the container
var container = new UnityContainer();
container.AddNewExtension<Interception>();
// Adding type mappings
container.RegisterType<ICalculator, Calculator>(
new InterceptionBehavior<PolicyInjectionBehavior>(),
new Interceptor<TransparentProxyInterceptor>());
// Policy injection
container.Configure<Interception>()
.AddPolicy("non-negative")
.AddMatchingRule<TypeMatchingRule>(
new InjectionConstructor(
new InjectionParameter(typeof(ICalculator))))
.AddMatchingRule<MemberNameMatchingRule>(
new InjectionConstructor(
new InjectionParameter(new[] {"Sub", "Test"})))
.AddMatchingRule<ReturnTypeMatchingRule>(
new InjectionConstructor(
new InjectionParameter(typeof(Int32))))
.AddCallHandler<NonNegativeCallHandler>(
new ContainerControlledLifetimeManager(),
new InjectionConstructor());
return container;
}
Tenga en cuenta que si utiliza el Administrador de ContainerControlledLifetimeManager, se garantiza que todos los métodos comparten la misma instancia de controlador de llamada.
El efecto del código es que cualquier tipo concreto que implementa ICalculator (es decir, está configurado para ser interceptadas y se ha resuelto a través de la unidad) seleccionará dos candidatos potenciales para la inserción: métodos Sub y prueba. Sin embargo, sólo los métodos con un tipo de valor devuelto de Int32 permanecerán en la regla de coincidencia aún más. Esto significa que, por ejemplo, prueba se se descarte si se trata de devolver un valor de tipo Double.
Agregar directivas a través de la configuración
Por último, el mismo concepto se puede expresar utilizando el archivo de configuración. Figura 6 muestra el contenido esperado de la <unity>sección.
Figura 6 de preparación de inyección de directivas en el archivo de configuración
public class NonNegativeCallHandler : ICallHandler
{
public IMethodReturn Invoke(IMethodInvocation input,
GetNextHandlerDelegate getNext)
{
// Perform the operation
var methodReturn = getNext().Invoke(input, getNext);
// Method failed, go ahead
if (methodReturn.Exception != null)
return methodReturn;
// If the result is negative, then throw an exception
var result = (Int32) methodReturn.ReturnValue;
if (result <0)
{
var exception = new ArgumentException("...");
var response = input.CreateExceptionMethodReturn(exception);
// Return exception instead of original return value
return response;
}
return methodReturn;
}
public int Order { get; set; }
}
<unity xmlns="https://schemas.microsoft.com/practices/2010/unity">
<assembly name="PolicyInjectionConfig"/>
<namespace name="PolicyInjectionConfig.Calc"/>
<namespace name="PolicyInjectionConfig.Handlers"/>
<sectionExtension ...
/>
<container>
<extension type="Interception" />
<register type="ICalculator" mapTo="Calculator">
<interceptor type="TransparentProxyInterceptor" />
<interceptionBehavior type="PolicyInjectionBehavior" />
</register>
<interception>
<policy name="non-negative">
<matchingRule name="rule1"
type="TypeMatchingRule">
<constructor>
<param name="typeName" value="ICalculator" />
</constructor>
</matchingRule>
<matchingRule name="rule2"
type="MemberNameMatchingRule">
<constructor>
<param name="namesToMatch">
<array type="string[]">
<value value="Sub" />
</array>
</param>
</constructor>
</matchingRule>
<callHandler name="handler1"
type="NonNegativeCallHandler">
<lifetime type="singleton" />
</callHandler>
</policy>
</interception>
</container>
</unity>
Según parece, si tiene varias reglas de coincidencia en una sola directiva, el resultado final es el operador booleano y se aplica a todos ellos (lo que significa que todos ellos debe cumplirse). Si se han definido varias directivas y, a continuación, se evalúa cada uno de ellos para que coincidan con: y controladores que se aplica, por separado. Por lo tanto, puede obtener los controladores que se aplica desde diferentes directivas.
Intercepción de un vistazo
En resumen, la interceptación es la forma en las mayoría de los entornos de IoC en Microsoft.NET Framework espacio implementar la orientación de aspecto. A través de interceptación, se le dará la posibilidad de ejecutar su propio código antes o después de cualquier método especificado en un tipo dado en cualquier ensamblado especificado. EntLib en el pasado, proporciona un bloque de aplicación específica, PIAB, para lograr esto. En EntLib 5.0, el motor subyacente de PIAB se ha movido a la unidad y implementa como un comportamiento especial para la intercepción de Low nivel Unity API que vimos en Mis dos columnas anteriores. El comportamiento de inserción de la directiva requiere el uso de un contenedor de unidad y no funcionará a través de la API de Low nivel interceptación.
La interceptación de Low nivel API, sin embargo, no le permite seleccionar a los miembros de tipo que desee para interceptar;tiene que escribir el código para hacer que personalmente. No obstante, con el comportamiento de inyección de directivas, pueden concentrarse en los detalles del comportamiento que desee y dejar que la biblioteca que se ocupe de averiguar qué métodos se aplica al según las reglas que le asigne.
Dino Esposito es el autor de la "programación de Microsoft ASP.NET (MVC)"(Microsoft Press, 2010) y es coautor de"Microsoft.NET: arquitectura de aplicaciones para la empresa "(Microsoft Press, 2008). Con residencia en Italia, Esposito participa habitualmente en conferencias y eventos del sector en todo el mundo. Puede participar en su blog en weblogs.asp.net/despos.
Gracias al siguiente experto técnico para la revisión de este artículo: Chris Tavares