Descripción de Enterprise Services (COM+) en .NET
Shannon Pahl
Microsoft Corporation
Abril de 2002
Resumen: proporciona detalles técnicos detrás de la integración de los servicios de Microsoft .NET y COM+ y describe los servicios disponibles para el código administrado. (26 páginas impresas)
Contenido
Introducción
Transacciones
Implementación
Componentes con servicio
Duración del objeto
Seguridad
Componentes remotos
Conclusión
Introducción
En este artículo se requiere cierta familiaridad con los servicios microsoft®.NET Framework y COM+. La familiaridad con Enterprise Services no es necesaria, pero sería útil. Para obtener una buena experiencia sobre estos temas, consulte:
- Libro de Derek Beyer, Programación COM+ de C# (Professional Mindware, 2001)
- Artículo de Tim Ewald, Integración con COM+: Cómo .NET Enterprise Services puede ayudarle a crear aplicaciones distribuidas
- Artículo de Jeffrey Richter, parte 2: Microsoft .NET Framework entrega la plataforma para una web integrada Service-Oriented
COM proporciona una manera de escribir aplicaciones basadas en componentes. Es bien conocido que el trabajo de fontanería necesario para escribir componentes COM es significativo y repetitivo. COM+ no es tanto sobre una nueva versión de COM; en su lugar, COM+ proporciona una infraestructura de servicios para los componentes. Los componentes se compilan y luego se instalan en aplicaciones COM+ para crear aplicaciones de servidor escalables que logren un alto rendimiento con facilidad de implementación. (Si un componente no necesita usar ningún servicio, no debe colocarse en una aplicación COM+). La escalabilidad y el rendimiento se logra mediante el diseño de aplicaciones desde el principio para usar servicios como transacciones, agrupación de objetos y semántica de actividad.
El .NET Framework proporciona otra manera de escribir aplicaciones basadas en componentes y tiene las ventajas sobre el modelo de programación COM de mejor compatibilidad con herramientas, Common Language Runtime (CLR) y una sintaxis de codificación mucho más sencilla. Se puede acceder a la infraestructura de servicios COM+ desde código administrado y no administrado. Los servicios en código no administrado se conocen como servicios COM+. En .NET, estos servicios se conocen como servicios Enterprise. Derivar una clase de ServicedComponent indica que se necesitarán servicios para un componente. (Si un componente no necesita usar ningún servicio, no debería derivarse de ServicedComponent). La compatibilidad con herramientas ha mejorado para permitir que los programadores escriban aplicaciones basadas en servidor, pero los mismos problemas de escalabilidad y rendimiento siguen estando en el ámbito de las buenas prácticas de programación. La idea básica de los servicios es design para el rendimiento y la escalabilidad desde el principio y aprovechar Enterprise Services para implementar fácilmente esos patrones de diseño cuando corresponda.
Podría afirmarse que el diseño de infraestructura de servicios realmente tiene poco que ver con COM o incluso con componentes: los servicios COM+ ahora se pueden aplicar a los componentes COM, a los componentes de .NET e incluso a otras entidades que no se consideran componentes, como páginas ASP o bloques de código arbitrarios (vea la característica Servicios sin componentes COM+ en Microsoft Windows ® XP).
Todos los servicios COM+ disponibles actualmente están disponibles para objetos .NET y COM. Algunos de estos servicios incluyen, transacciones, agrupación de objetos y cadenas de construcción, JIT, sincronización, seguridad basada en roles, CRM y BYOT. Para obtener una lista completa de los servicios en Microsoft Windows 2000, consulte Servicios proporcionados por COM+ en el SDK de plataforma. Microsoft Windows XP incluye una nueva versión de COM+, es decir, COM+ 1.5, que tiene servicios adicionales que también se pueden usar con componentes de .NET.
Transacciones
Para escribir aplicaciones administradas que usan servicios, las clases que requieren servicios deben derivar de ServicedComponent y usar varios atributos personalizados para especificar los servicios reales necesarios. En esta sección se presentan estos conceptos y cómo afectan a la escritura de código administrado. En secciones posteriores se proporciona una explicación más detallada.
Supongamos que se ha escrito una cuenta de clase (el código real aparece más adelante) y se encuentra en el ensamblado BankComponent. Esta clase se puede usar de la siguiente manera:
BankComponent Client
using system;
using BankComponent;
namespace BankComponentClient
{
class Client
{
public static int Main()
{
Account act = new Account();
act.Post(5, 100);
act.Dispose();
return 0;
}
}
}
Para compilar el cliente, la referencia debe agregarse al espacio de nombres BankComponent. Además, se debe agregar una referencia para el ensamblado System.EnterpriseServices, en el espacio de nombres BankComponentClient, el cliente llama a Dispose() y al constructor ServicedComponent, que son métodos definidos en System.EnterpriseServices, no en el ensamblado que contiene BankComponent. Se trata de un requisito general de .NET cuando una clase derivada no invalida todos los métodos de clase base.
El código del servidor BankComponent muestra la implementación de la clase Account en .NET, que usa transacciones. La clase Account deriva de la clase System.EnterpriseServices.ServicedComponent. El atributo Transaction marca la clase como que requiere una transacción. Los servicios Synchronization y JIT se configuran automáticamente porque se usa el atributo Transaction. El atributo AutoComplete se usa para especificar que el tiempo de ejecución debe llamar automáticamente a la función SetAbort para la transacción si se produce una excepción no controlada durante la ejecución del método; De lo contrario, se llama a una función SetComplete . El atributo ApplicationName asocia este ensamblado a una aplicación COM+ que almacena los datos de configuración del servicio para esta aplicación. En el código se resaltan otras modificaciones necesarias para esta clase.
Servidor BankComponent
using System.EnterpriseServices;
[assembly: ApplicationName("BankComponent")]
[assembly: AssemblyKeyFileAttribute("Demos.snk")]
namespace BankComponentServer
{
[Transaction(TransactionOption.Required)]
public class Account : ServicedComponent
{
[AutoComplete]
public bool Post(int accountNum, double amount)
{
// Updates the database, no need to call SetComplete.
// Calls SetComplete automatically if no exception is thrown.
}
}
}
El código del espacio de nombres del servidor BankComponent muestra lo fácil que es usar servicios COM+ en .NET. A continuación se muestra un resumen del proceso completo de codificación a implementación:
Escriba el ensamblado del servidor.
Compile el ensamblado:
Firme el ensamblado. Se puede generar un archivo de clave una vez para el proyecto. No es necesario generar uno para cada compilación. Se puede crear una clave mediante el símbolo del sistema de Microsoft .NET y sn.exe:
sn –k Demos.snkCompile el código. Se debe agregar una referencia para System.EnterpriseServices.
Implemente la aplicación.
Un ensamblado que use componentes con servicio debe registrarse con el catálogo COM+. La clase ServicedComponent y los atributos personalizados son los dos conceptos clave para obtener acceso a los servicios COM+ desde código administrado. La configuración del servicio se almacena en el catálogo COM+. Los objetos residen y se ejecutan dentro de CLR. El objeto administrado y su contexto COM+ asociado se muestran en la figura 1 y se volverán más claros en las dos secciones siguientes.
.gif)
Figura 1. Servicios asociados a componentes administrados
Con los componentes com+, debe configurar el catálogo manualmente, pero con componentes con servicio, el catálogo se puede actualizar en función de los atributos del código. Un ensamblado se puede registrar explícitamente mediante la herramienta de línea de comandos regsvcs.exe o escribiendo scripts que acceden a una API administrada. A continuación se proporcionan más detalles en la sección de detalles de implementación. Durante el desarrollo, la implementación de XCopy se proporciona como una comodidad simplemente copiando el ensamblado en el directorio de la aplicación. Cada vez que una aplicación cliente crea instancias de clases derivadas de ServicedComponent, el tiempo de ejecución detecta si ya ha registrado o no el ensamblado en una aplicación COM+. Si no se ha registrado, se busca el ensamblado en el directorio local y, si se encuentra, todos los componentes con servicio del ensamblado se registran en una aplicación COM+ y la activación puede continuar. Esto se conoce como registro diferido, pero no funciona para todos los escenarios. Por ejemplo, los ensamblados marcados como una aplicación de servidor COM+ requieren un registro explícito (consulte a continuación) y el registro diferido no funciona para clientes no administrados que llaman a componentes administrados con servicio. El registro diferido es útil durante el tiempo de desarrollo; de lo contrario, use scripts, código o RegSvcs para registrar el ensamblado.
Posiblemente coloque el ensamblado en la GAC. Consulte la sección implementación para obtener más detalles.
Ejecute el cliente.
Implementación
Los atributos personalizados son uno de los dos conceptos clave para acceder a servicios COM+ desde código administrado. Los atributos personalizados se usan para especificar los servicios necesarios, como el atributo personalizado Transaction en la lista de código anterior. Estos atributos almacenan las opciones de configuración de un servicio en los metadatos del ensamblado. Los atributos personalizados se usan al hacer que algún código cargue el ensamblado y use la reflexión, cree instancias de los atributos y llame a métodos en el atributo para extraer la configuración del servicio almacenada en el atributo. A continuación, la información se puede escribir en el catálogo COM+. El código que realiza estos y otros pasos se incluye en EnterpriseServices.RegistrationHelper. Para facilitar el proceso de registro, todas las formas de registro usan el componente EnterpriseServices.RegistrationHelper. Este componente es accesible como una clase administrada, así como un objeto COM.
.gif)
Ilustración 2. Registro de componentes con servicio
Conceptualmente, RegistrationHelper realiza los pasos siguientes:
- Usa RegistrationServices.RegisterAssembly para registrar el ensamblado en el Registro. Por lo tanto, las clases aparecen en el Registro como componentes COM escritos en código administrado y tienen la clave InprocServer32 que apunta a mscoree.dll. Si una clase administrada no implementa ninguna interfaz, los métodos públicos de la clase no aparecen en el catálogo COM+, a menos que se use ClassInterfaceAttribute. Esto significa que la configuración del servicio asociada al nivel de método no se puede almacenar en el catálogo. Sin embargo, algunos servicios COM+ se pueden configurar en el nivel de método y requieren que el componente exponga una interfaz tal como se ve en el catálogo COM+. Por ejemplo, la seguridad basada en roles com+ en el nivel de método requiere un componente para implementar una interfaz para configurar el servicio. Más información sobre este problema en la sección de seguridad.
- Genera una biblioteca de tipos COM desde el ensamblado mediante TypeLibConverter. ConvertAssemblyToTypeLib.
- Registra la biblioteca de tipos. Hasta ahora, esto es muy igual que RegAsm.exe /tlb.
- Busca o crea una aplicación COM+. El nombre se extrae del atributo ApplicationName, el nombre del ensamblado o el nombre o GUID de la aplicación proporcionado.
- Usa la biblioteca de tipos para configurar la aplicación COM+ mediante las API de administración de COM+.
- Recorre todos los atributos personalizados y usa IConfigurationAttribute para escribir datos de configuración para el servicio determinado en el catálogo COM+.
RegistrationHelper intentará realizar estos pasos dentro de una transacción mediante RegistrationHelperTx, una clase dentro de una aplicación COM+ que se crea cuando se instala .NET. Por lo tanto, si se produce un error en el registro, el catálogo COM+ y el registro se restaurarán en su estado original. Actualmente, las bibliotecas de tipos generadas permanecerán en el disco (o en la GAC si el ensamblado estaba en la GAC). Si el ensamblado que se registra hace referencia a otros ensamblados que también usan servicios COM+, todos los ensamblados del gráfico de dependencias se someten a los mismos pasos que se enumeran anteriormente.
Dado que RegistrationHelper accede al catálogo COM+, requiere permisos de código no administrados y derechos de administrador en el equipo. Por lo tanto, lo mismo sucede con los clientes en RegistrationHelper, es decir, el registro diferido, RegSvcs o el código o los scripts. Esto también implica que no se puede registrar el código descargado de Internet o almacenado en un recurso compartido de red.
Es posible codificar combinaciones de atributos incompatibles, como requerir una transacción y establecer la sincronización en deshabilitada. Estas combinaciones se detectan actualmente durante el tiempo de registro al escribirlas en el catálogo COM+ y no durante el tiempo de compilación. Algunos atributos tienen dependencias implícitas en otros atributos, por ejemplo, cuando se usa solo el atributo Transaction, esto equivale a usar los atributos Transaction, JustInTimeActivation y Synchronization. Cuando se registra un componente administrado, los valores predeterminados del catálogo COM+ se usan a menos que se usen atributos para sobrescribir los valores predeterminados "no configurados". Por ejemplo, si un componente está registrado y no tiene especificado un atributo Transaction , el valor predeterminado no configurado para la configuración de transacción en el catálogo se establece en TransactionOption.Disabled. Este enfoque permite a un desarrollador quitar un atributo del código si el componente ya no lo requiere y, a continuación, cuando el ensamblado se vuelve a registrar, la entrada de catálogo para la transacción se restablece correctamente. En la documentación en línea se especifica una lista detallada de estos valores predeterminados no configurados. Los valores configurados predeterminados son los valores predeterminados en los parámetros de un atributo, por ejemplo, simplemente usando el atributo [Transaction] indica TransactionOption.Required.
Dado que los datos de configuración de los servicios en clases administradas se almacenan en el catálogo COM+, algunas entradas de catálogo también se pueden modificar administrativamente después de registrar un ensamblado. Algunos servicios no deben modificarse de esta manera. Por ejemplo, deshabilitar el servicio de transacciones en el catálogo puede hacer que el código funcione incorrectamente. La configuración específica de la implementación, como las cadenas de construcción de objetos y los roles de seguridad, se pueden manipular después del registro. Es posible que la implementación de XCopy de ensamblados que contengan componentes con servicio no sea suficiente cuando se realice la configuración posterior al registro. La característica de importación y exportación de aplicaciones COM+ ayuda a distribuir el estado actual de la aplicación. En la sección comunicación remota se proporciona más información sobre la importación y exportación.
En algunos casos, el catálogo no se consulta para los datos de configuración, pero se extrae exclusivamente de los metadatos del ensamblado. Los casos son para autocompletar, JIT, agrupación de objetos (aunque el tamaño del grupo se extrae del catálogo) y el atributo de método seguro. En las secciones que describen esos servicios se describen más detalles sobre este problema.
El proceso de registro de un ensamblado generará automáticamente los GUID que requiere COM+. Si el ensamblado no está firmado, los GUID se generan solo en función de los nombres de tipo y espacio de nombres. Por lo tanto, se pueden generar GUID no únicos si el ensamblado no está firmado. Se impone un destino similar a los ensamblados de .NET que ni siquiera usan servicios COM+, sino que requieren nombres de tipo únicos. Por lo tanto, deben firmarse los objetos assemblies que usen servicios COM+. Se producirá un error en el registro si los ensamblados no están firmados. El registro también implica que una clase de .NET que usa servicios COM+ tiene un almacén de datos de configuración global. Aunque es posible copiar ensamblados privados en varios directorios de aplicaciones, en última instancia, todas estas aplicaciones hacen referencia a un dato de configuración para un componente con servicio. Por lo tanto, cambiar los datos de configuración en el catálogo COM+ afecta a todas las aplicaciones que usan la clase . Esto es evidente con varias vroot en una configuración de Microsoft ASP.NET que todos componen una copia del mismo ensamblado que usa componentes con servicio. Una manera de tener varias configuraciones para la misma aplicación COM+ es usar particiones COM+ en Microsoft Windows .NET. Para usar el servicio de particiones COM+ en .NET, no use el atributo ApplicationID para instalar el mismo componente en varias particiones, COM+ requiere identificadores de aplicación únicos.
En general, la GAC se usa siempre que un cliente necesita tener acceso a ensamblados que no están en el mismo directorio que el directorio de la aplicación cliente o si el ensamblado se carga en otro proceso, que no se encuentra en el directorio del cliente. Conceptualmente, los ensamblados privados que usan componentes con servicio son realmente ensamblados compartidos, su uso de los datos de configuración compartidos. Si ApplicationActivationOption está establecido en biblioteca, es posible usar transacciones en una clase de un ensamblado y usar ese ensamblado en un cliente si todos los ensamblados se cargan desde el mismo directorio. Cuando un ensamblado que usa ApplicationActivationOption se establece en el servidor, dllhost.exe carga el ensamblado, lo que probablemente no está en el mismo directorio que el cliente. Los ensamblados que usan componentes con servicio en aplicaciones de servidor COM+ deben colocarse en la GAC. Es posible que los ensamblados que usan componentes con servicio en las aplicaciones de bibliotecas COM+ no tengan que colocarse en la GAC (a menos que se encuentren en directorios diferentes). La única excepción es cuando se hospeda con ASP.NET: los ensamblados no deben colocarse en la GAC para permitir que la instantánea funcione correctamente.
Para quitar una aplicación .NET que usa componentes con servicio, quite el ensamblado de la GAC (si se ha registrado con la GAC), desconscriba el ensamblado de COM+ mediante regsvcs.exe elimine el ensamblado y las bibliotecas de tipos asociadas.
Control de versiones
Es posible corregir los GUID que COM+ requiere mediante un atributo GUID. Sin embargo, se recomienda que el control de versiones se use en lugar de usar explícitamente GUID. Cada vez que se crea una nueva firma de método o cuando las clases están decoradas con atributos de servicio diferentes, se debe incrementar el número de versión principal o secundaria del ensamblado. El registro se debe realizar una vez para una versión determinada. Al registrar una nueva versión del ensamblado, se generan nuevos GUID para esa versión y los componentes se registran en la misma aplicación COM+ con el mismo nombre de componente. Por lo tanto, los componentes aparecerán varias veces en la aplicación COM+. Sin embargo, cada componente tiene identificadores únicos proporcionados por los GUID. Cada instancia hace referencia a una versión determinada del componente. Esto suele observarse al compilar aplicaciones .NET mediante Microsoft Visual Studio ® .NET. El entorno agrega el atributo [assembly: AssemblyVersion("1.0.*")] a un proyecto. Cada nueva compilación del proyecto generará un nuevo número de compilación y, por tanto, se generan nuevos GUID cuando se vuelva a registrar el ensamblado. Por lo tanto, puede ser preferible incrementar manualmente los números de compilación cuando corresponda. Los clientes se enlazarán a un ensamblado mediante la directiva de versión de CLR y, por lo tanto, se usará la versión correcta de la clase en la aplicación COM+. Algunos escenarios en paralelo al escribir ensamblados (servidores administrados) que usan componentes con servicio son: (algunos aspectos de la activación que se usan a continuación se describen en la sección siguiente).
- Cliente administrado, servidor administrado, no se usan GUID fijos en el ensamblado.
- El cliente cargará el ensamblado especificado por la directiva de versión.
- Cliente administrado, servidor administrado, GUID fijos usados.
- Si el cliente activa una clase y usa la directiva de versión para obtener una versión anterior del ensamblado, el GUID fijo del código se usará durante la activación para extraer la información del servicio del catálogo. Por lo tanto, la información del último ensamblado registrado que usa este GUID se usará para crear el objeto , que de hecho puede ser la versión más reciente y, por lo tanto, habría una excepción de conversión de tipos al intentar convertir desde el objeto creado realmente (v2) a la referencia en el código (v1).
- El cliente administrado, el servidor administrado, ningún GUID fijo, solo cambia el número de compilación.
- Aunque se generarán nuevos GUID, la biblioteca de tipos seguirá teniendo el mismo número de versión, ya que las bibliotecas de tipos solo tienen dos números para la versión. Esto puede seguir funcionando, pero si la versión 2 está instalada en la versión 1, se desinstala la versión 1, se anulará el registro de la biblioteca de tipos de la versión 2. Solución 1: La próxima versión de la .NET Framework (V1.1) soluciona este problema al permitir que la biblioteca de tipos se controle con versiones independientes del ensamblado. Esto implicaba que al cambiar el número de versión del ensamblado, se debe cambiar la versión de la biblioteca de tipos. Solución 2: use solo los números de versión principal y secundaria.
- El cliente no administrado, el servidor administrado, no se usan GUID fijos.
- El cliente usará un GUID para crear el componente. La interoperabilidad resolverá el GUID en un nombre y se aplicará la directiva de versión. Si la versión 1 y la versión 2 de un ensamblado están en una máquina y se usa la directiva para llegar a la versión 2, el cliente no administrado obtendrá la versión 2.
- Instale la versión 1, instale la versión 2, desinstale la versión 1. Ahora el cliente no puede crear el componente a menos que haya una directiva de versión para redirigir a la versión 2. Además, las entradas del Registro deben existir para la información de registro de la versión 1. Una manera de crear información del Registro para una versión desinstalada 1 es usar la característica de alias COM+ en Windows XP.
El control de versiones se aplica a todos los componentes de la misma aplicación COM+, es decir, no hay ninguna manera automática de versionar la propia aplicación. Por ejemplo, los roles de la aplicación no pueden tener versiones mediante la directiva de versión. Use el atributo de nombre de aplicación para realizar el control de versiones de la aplicación.
Componentes con servicio
Activación
La infraestructura de Enterprise Services se basa en el concepto de contexto. Un contexto es un entorno para objetos con requisitos de ejecución similares. Los servicios se pueden aplicar durante la activación o durante la interceptación de llamadas de método. Aunque los servicios COM+ están escritos en código no administrado, la integración de servicios COM+ con .NET es mucho más profunda que simplemente usar la tecnología de interoperabilidad COM en .NET. Sin derivar de ServicedComponent, el proceso de registro no tendría el efecto deseado.
Los componentes con servicio se pueden activar y hospedar en diversas combinaciones. Como se muestra en la figura 3, esta explicación hará referencia a tres casos, en proceso (mismo dominio de aplicación), entre dominios de aplicación (mismo proceso) y activaciones entre procesos. La importancia de estos casos es los límites que se cruzan al realizar llamadas en componentes. La activación en proceso da lugar a un posible límite entre contextos, el caso entre dominios de la aplicación tiene límites entre contextos y entre dominios de la aplicación, mientras que el caso entre procesos se ocupa de los límites entre máquinas o entre procesos y entre contextos.
.gif)
Figura 3. Hosts de activación para componentes con servicio
La implementación de componentes con servicio se basa en la comunicación remota de .NET, que proporciona un mecanismo extensible para conectar los servicios escritos en código no administrado o administrado. Los componentes con servicio derivan de ContextBoundObject e implementan varias interfaces, como IDisposable. La cadena de activación de CLR se personaliza fácilmente mediante atributos personalizados derivados de ProxyAttribute. La interceptación se puede personalizar escribiendo servidores proxy reales personalizados. Cuando se requiere una nueva clase derivada de componentes con servicio, la cadena de activación se personaliza para que la llamada de activación llame realmente a un contenedor de C++ administrado para CoCreateInstance. Esto permite que COM+ configure los contextos y servicios no administrados en función de la información almacenada en el catálogo COM+ de un ensamblado registrado previamente. También es la fase en la que se implementa el registro diferido. Durante el registro del ensamblado, la clave InprocServer32 apunta a mscoree.dll, por lo que redirige la instancia de COM+ CreateInstance al tiempo de ejecución para crear el objeto administrado real. Por lo tanto, durante la activación, se crea un objeto proxy real personalizado. La versión en proceso de este proxy se conoce como proxy de componente con servicio o SCP. Esto se representa en la figura 4.
.gif)
Figura 4. Ruta de acceso de activación
La ruta de acceso de retorno de la llamada de activación serializa las referencias administradas desde código administrado, a través de COM+ no administrado y de nuevo en código administrado (la ruta inversa de la línea 1 de la figura 4). Dependiendo de dónde se creó el objeto real, el lado cliente anula las referencias a la referencia en el formulario correspondiente. En el caso de la activación en proceso, la figura 5 indica que la referencia no está desactivada como una referencia directa al proxy transparente (TP). Las referencias entre dominios de aplicación se anulan como proxy de comunicación remota de .NET. Las referencias entre procesos o máquinas cruzadas (figura 6) requieren que se desmarque más: la interoperabilidad COM realiza llamadas en IManagedObject implementadas por ServicedComponent durante la activación y la desmarshación. El proxy de componente con servicio remoto (RSCP) realiza llamadas en IServicedComponentInfo durante la activación para obtener el URI del objeto de servidor, lo que implica que se realizan dos llamadas remotas durante la activación. Cuando se requiere seguridad basada en roles COM+ en el nivel de método, estas interfaces deben estar asociadas a un rol para que el desenlace se realice correctamente cuando la infraestructura realiza llamadas en estas interfaces. En la sección de seguridad se describen las implicaciones que tiene la activación entre procesos y la serialización en la configuración de la seguridad basada en roles.
.gif)
Figura 5. Infraestructura para llamadas en proceso
.gif)
Figura 6. Infraestructura para llamadas fuera del proceso
Por lo tanto, la cadena de activación se ha personalizado para crear un proxy real personalizado (que se usa para la interceptación) y para crear los contextos no administrados, dejando COM+ con solo la infraestructura de contexto necesaria para realizar la semántica de los servicios de interceptación. El contexto COM+ ahora está asociado a un objeto administrado, no a un objeto COM.
Interception
En la figura 7 se muestra la infraestructura de llamada al método en proceso. El proxy personalizado (SCP) permite interceptar llamadas administradas. Durante la activación, el identificador de contexto COM+ se almacena en el SCP. Cuando un objeto administrado llama a un componente con servicio, el identificador de contexto almacenado en el SCP de destino se compara con el identificador de contexto del contexto actual. Si los identificadores de contexto son los mismos, la llamada se ejecuta directamente en el objeto real. Cuando los identificadores de contexto son diferentes, el SCP realiza una llamada a COM+ para cambiar los contextos y representar el servicio de escribir la llamada al método. Para las llamadas en proceso, esto es similar a AppDomain.DoCallBack, pero con AppDomain siendo COM+. La función "DoCallBack" entra primero en COM+ (paso 2 en la figura 7), que cambia el contexto y representa el servicio y, a continuación, llama a la función de devolución de llamada en el SCP. El SCP realiza la serialización de datos y llama al método en el objeto real. Cuando se cierra el método, la ruta de acceso de retorno permite que COM+ represente la semántica para salir de una llamada de método (paso 5 en la figura 7). COM+ solo se usa para representar el servicio. La serialización de datos y la llamada al método se realiza dentro del entorno de ejecución de .NET*,* de modo que no se necesite la conversión de tipos como String a BSTR al llamar a métodos. La serialización de datos sería necesaria si se usaba la interoperabilidad COM para las llamadas en proceso*.* La llamada para representar el servicio en código no administrado no es una llamada de interoperabilidad COM para las llamadas en proceso.
.gif)
Ilustración 7. Infraestructura para llamadas en proceso
Las llamadas a métodos estáticos no se reenvía a los servidores proxy transparentes y reales. Por lo tanto, los métodos estáticos no pueden utilizar servicios de interceptación; en su lugar, se les llama dentro del contexto del cliente. Se llamará a métodos internos dentro del contexto correcto, lo que significa que un cliente que llama a un método interno en un objeto configurado para una nueva transacción formará parte de una nueva transacción. Sin embargo, dado que los servicios de nivel de método requieren una interfaz en el catálogo COM+ (más sobre este tema siguiente y en la seguridad), los métodos internos no se pueden configurar para los servicios de nivel de método. Los servicios se pueden aplicar a las propiedades, pero los atributos de nivel de método (como AutoComplete) deben colocarse en los métodos captador y establecedor individualmente.
El atributo AutoComplete es una manera cómoda de usar transacciones sin escribir ningún código para acceder al servicio. Como alternativa, se puede usar ContextUtil.SetAbort o ContextUtil.SetComplete. Este servicio se puede configurar en el explorador de COM+ estableciendo una casilla en las propiedades del método. Sin embargo, los objetos administrados no necesitan implementar interfaces. Esto también es cierto para los componentes con servicio. Cuando el método no se declara en una interfaz, la configuración de los servicios de nivel de método no se puede escribir en el catálogo en el registro; la configuración solo se puede almacenar en metadatos. Cuando no existe ninguna interfaz para el método, el cambio de contexto se realiza desde el SCP mediante la información de configuración almacenada en IRemoteDispatch.RemoteDispatchAutoDone cuando el atributo AutoComplete está presente. Cuando AutoComplete no está presente, se usa IRemoteDispatch.RemoteDispatchNotAutoDone. IRemoteDispatch es una interfaz implementada por ServicedComponent. Los clientes no administrados solo pueden llamar a componentes con servicio que no tengan interfaces mediante IDispatch (enlazado en tiempo de ejecución) y, por lo tanto, no se puede aplicar la semántica de Autocompletar debido a la ausencia de un proxy real en ese caso. Incluso cuando se usan interfaces, la configuración de AutoCompletar sigue controlada por los metadatos de los clientes administrados. La llamada al método DCOM se realiza en RemoteDispatchAutoDone solo en el caso fuera del proceso. Los componentes fuera de proceso no usan el mecanismo DoCallBack, en su lugar, DCOM se puede usar para entregar la llamada y representar el servicio. Si el método está en una interfaz, se llama al método de interfaz en el componente con servicio remoto mediante DCOM; de lo contrario, la llamada se envía a la interfaz IRemoteDispatch en ServicedComponent. Esto significa que incluso las llamadas como Dispose() se llaman a través de DCOM, las implicaciones de las cuales se describen más adelante.
Contextos
La clase ContextUtil se usa para tener acceso al contexto de objeto COM+ asociado y sus propiedades. Esto proporciona una funcionalidad similar al objeto devuelto por CoGetObjectContext en código no administrado. El contexto de objeto administrado asociado a un componente con servicio tiene un propósito diferente al contexto de objeto no administrado asociado. Esto se manifiesta escribiendo tres objetos administrados, uno con Transacciones necesarias (actuando como raíz), los otros dos no derivan del componente con servicio (actuando como objetos secundarios que ejemplifican objetos administrados ágiles de contexto). Los componentes que no son de servicio se comportarán como si fueran componentes con servicios compatibles con transacciones, es decir, pueden realizar llamadas a administradores de recursos y usar ContextUtil.SetAbort si es necesario. Cuando se crea el objeto raíz, se crea el contexto no administrado asociado y se asocia al subproceso actual. Cuando se realiza una llamada a los objetos secundarios, ya que no están asociados a un contexto no administrado, no se necesita ningún cambio de contexto COM+, por lo que el subproceso sigue manteniendo el identificador de contexto no administrado de la raíz. Cuando un objeto secundario llama a en administradores de recursos, los administradores de recursos extraerán a su vez el contexto no administrado del subproceso que ejecuta el objeto secundario, que es el contexto no administrado del objeto raíz. Confiar en esto es peligroso y en versiones futuras, el contexto no administrado puede combinarse con el contexto administrado y, por lo tanto, los objetos secundarios se asociarán con un contexto administrado potencialmente diferente; los administradores de recursos no seleccionarán el contexto del objeto raíz. Por lo tanto, actualizar a una nueva versión de .NET podría interrumpir el código que depende de este tipo de comportamiento.
Resultados de rendimiento
En esta sección, el rendimiento del cliente administrado, la solución de componentes de servicio de servidor administrado se compara con la solución de cliente o servidor no administrado. El caso en proceso se describe en la tabla siguiente. Un ServicedComponent configurado para transacciones se escribió en C# con un único método que simplemente agrega números. Se usó una implementación de C++ correspondiente para la comparación. Esta comparación muestra la diferencia entre las soluciones administradas frente a las no administradas sin realizar ningún trabajo real. Las activaciones en proceso son aproximadamente 3,5 veces más lentas en la solución administrada y las llamadas a métodos son aproximadamente 2 veces más costosas cuando hay un cambio de contexto. Sin embargo, al comparar las llamadas a métodos de componentes con servicio que requieren cambio de contexto frente a las que no lo hacen, hay aproximadamente 3 órdenes de diferencia de magnitud que indican el éxito de la infraestructura de interceptación de componentes en proceso. En el caso de las soluciones fuera de proceso, las activaciones son aproximadamente 2 veces más costosas y las llamadas a métodos entre contextos aproximadamente 3 veces más costosas.
En la tabla 1 se muestran los tiempos escalados para la activación en proceso y las llamadas a métodos mediante soluciones administradas frente a soluciones no administradas.
Tabla 1. Activación en proceso y llamadas de método
| Solución administrada | Solución no administrada | |
| Activación | 35 | 10 |
| Llamada al método cross-context-do-nothing | 2 | 1 |
| Llamada al método cross-context-do-work | 200 | 100 |
Las activaciones son aproximadamente un orden de magnitud más caro que las llamadas de método al método "do-nothing". Agregar en algún trabajo para obtener simplemente una transacción DTC (pero no hacer nada con él) nivela los tiempos de llamada de activación y método al mismo orden de magnitud. Cuando la llamada al método simplemente abre una conexión de base de datos agrupada, el trabajo de llamada al método es entonces un orden de magnitud mayor que la llamada al método "do-nothing" y la activación combinada, lo que demuestra que la sobrecarga de la infraestructura de componentes con servicio es teórica cuando se agrega trabajo real al experimento.
Duración del objeto
Activación Just-In-Time
Por lo general, el servicio Just-In-Time (JIT) no se usa de forma aislada. Se usa implícitamente con el servicio de transacciones y con más frecuencia con la agrupación de objetos. Sin embargo, este ejemplo ayuda a resaltar algunos temas interesantes. En el código siguiente, se escribe una clase .NET que usa solo el servicio JIT.
using System;
using System.EnterpriseServices;
[assembly: AssemblyKeyFile("Demos.snk")]
[assembly: ApplicationName("JITDemo")]
namespace Demos
{
[JustInTimeActivation]
public class TestJIT : ServicedComponent
{
public TestJIT()
{ // First to get called
}
[AutoComplete]
public void DoWork ()
{ // Show doneness using ..
// 1. The autocomplete attribute or
// 2. ContextUtil.DeactivateOnReturn = true or
// 3. ContextUtil.SetComplete();
}
public override void Dispose(bool b)
{ // Optionally override this method and do your own
// custom Dispose logic. If b==true, Dispose() was called
// from the client, if false, the GC is cleaning up the object
}
}
}
La clase se deriva de ServicedComponent y usa el atributo JIT para indicar el servicio específico necesario. Para invalidar los métodos Activate y Deactivate en código no administrado, se requiere la clase para implementar la interfaz IObjectControl. En su lugar, la clase ServicedComponent tiene métodos virtuales que se pueden invalidar para controlar los eventos Activate y Deactivate. Sin embargo, ni ServicedComponent ni su proxy real, SCP, implementan IObjectControl. En su lugar, el SCP crea un desmontaje de proxy cuando COM+solicita la interfaz IObjectControl. Las llamadas de COM+ en el desmontaje se reenvía a los métodos virtuales de ServicedComponent. El bit DeactivateOnReturn se establece mediante el atributo AutoComplete en el método , llamando a ContextUtil.SetComplete(), ContextUtil.SetAbort() o estableciendo ContextUtil.DeactivateOnReturn. Suponiendo que el bit DeactivateOnReturn se establece durante cada llamada al método, la secuencia de llamadas de método sería: el constructor de la clase, Activate, la llamada al método real, Deactivate, Dispose(true) y, finalmente, el finalizador de la clase si existe. La misma secuencia se repite cuando se realiza otra llamada al método . Una buena práctica de diseño consiste en invalidar solo los métodos Activate y Deactivate para saber cuándo se está sacando el objeto y volver a colocarlo en el grupo de objetos. La lógica restante de Activate y Deactivate debe colocarse en el constructor de la clase y los métodos Dispose(bool). El bit DeactivateOnReturn se puede establecer mediante uno de los métodos siguientes:
- El cliente usa el estado del objeto solo para una sola llamada de método. En la entrada al método , se crea un nuevo objeto real y se adjunta al SCP. Al salir del método, el objeto real se desactiva, primero realizando llamadas a Dispose(true) seguido del finalizador de objetos reales si existe alguno. Sin embargo, el contexto de COM+ asociado, SCP y TP permanecen activos. El código de cliente seguirá manteniendo su referencia a lo que cree que es un objeto real (el proxy transparente). La siguiente llamada de método realizada por el cliente en la misma referencia dará lugar a que se cree un nuevo objeto real y se adjunte al SCP para atender la llamada al método (vea la sección sobre la agrupación de objetos para quitar el requisito de crear un nuevo objeto). Para desactivar el objeto real, el objeto real debe indicar la doneness cuando se cierra una llamada al método. Esto se puede lograr mediante:
- Atributo AutoComplete en un método de la clase
- una de las dos llamadas de método en la clase ContextUtil, DeactivateOnReturn o SetComplete
- El cliente realiza varias llamadas de método en el mismo objeto sin desactivar el objeto después de cada llamada de método estableciendo el bit de doneness en false antes de salir del método. Por ejemplo, determinar el ámbito de un componente con servicio que usa JIT en el nivel de formulario y tener dos botones de formulario llaman a métodos en la misma instancia de objeto haciendo que los métodos establezcan explícitamente el bit de doneness en false. En algún momento, el bit de doneness debe establecerse en true. Este enfoque implica que existe un contrato entre el cliente y el objeto . El cliente puede hacerlo implícita o explícitamente:
- El cliente sabe llamar a un método determinado en el objeto cuando se realiza para desactivar el objeto. La implementación del método usa las ideas de la opción 1. Se puede llamar de nuevo a la referencia de objeto mediante la misma secuencia de llamada, lo que implica que se creará un nuevo objeto real.
- El cliente destruye explícitamente el objeto cuando llama al método Dispose() en el objeto . Dispose() es un método definido en ServicedComponent y llama a Dispose(true), el finalizador de la clase (si existe) y, a continuación, elimina el contexto COM+ asociado. En este caso, no se pueden realizar más llamadas a métodos en la referencia de objeto. Si se intenta, se producirá una excepción. Si hay muchos clientes que usan el mismo objeto, llamar a Dispose() solo se debe realizar cuando el último cliente haya terminado con el objeto . Sin embargo, la naturaleza sin estado de los objetos JIT conduce a prácticas de diseño hacia una sola instancia por modelo de cliente.
- El objeto nunca establece su bit de doneness en true y el cliente nunca llama a Dispose(). El objeto real, los servidores proxy y el contexto se destruyen cuando tiene lugar la recolección de elementos no utilizados. El orden de llamada de método iniciado por el GC sería Deactivate, Dispose(false) y, a continuación, el finalizador de clases (si existe).
Todos los componentes con servicio tienen un contexto COM+ asociado, que se almacena como referencia en el SCP (o RSCP en el caso remoto). La referencia solo se libera cuando se realiza la recolección de elementos no utilizados o si el cliente llama a Dispose(). Es mejor no confiar en el GC para limpiar el contexto: el contexto COM+ se mantiene en un identificador del sistema operativo y es posible que parte de la memoria retrase la liberación de estos identificadores hasta que se produzca un GC. Además, aunque ServicedComponent no tiene un finalizador, el SCP implementa un finalizador, lo que significa que la referencia de contexto COM+ nunca se recopilará en una primera recolección de elementos no utilizados. De hecho, cuando se llama al finalizador en el SCP, el contexto sigue sin destruirse por el subproceso del finalizador, en su lugar, el trabajo de destrucción de contextos se quita del subproceso del finalizador y se coloca en una cola interna. Esto se hizo porque se encontró que el subproceso de finalizador puede consumirse por trabajo en determinados entornos de esfuerzo en los que los componentes con servicio se crean rápidamente, se usan y salen del ámbito. En su lugar, un subproceso interno atiende la cola y destruye contextos antiguos. Además, cualquier subproceso de aplicación que cree un nuevo ServicedComponent intentará primero quitar un elemento de la cola y destruir un contexto antiguo. Por lo tanto, llamar a Dispose() desde el cliente anulará el contexto COM+ antes mediante el subproceso de cliente y liberará los recursos de identificador y memoria que consume el contexto. A veces Dispose() puede producir excepciones. Un caso es si el objeto reside en un contexto de transacción no raíz que se ha anulado: la llamada Dispose() puede observar una excepción de CONTEXT_E_ABORTED. Otro caso se explica en la agrupación de objetos.
Desde un punto de vista del rendimiento, es mejor no implementar un finalizador en una clase derivada ServicedComponent y, en su lugar, colocar esta lógica en el método Dispose(bool). Aunque el SCP implementa un finalizador, se llama al finalizador del objeto real mediante reflexión.
Una buena práctica de diseño para usar JIT es:
- Coloque el código de activación y finalización personalizados en los métodos constructor y Dispose(bool), no implemente un finalizador y use un único patrón de llamada indicando la doneness mediante el atributo AutoComplete en el método .
- Llame a Dispose() desde el cliente cuando el cliente haya terminado con el objeto .
La discusión ha asumido que el cliente se administra y el componente está en proceso. Cuando el componente está fuera de proceso: (Se describen más detalles en la sección comunicación remota)
- El GC solo limpiará los objetos cuando el tiempo de concesión de comunicación remota de .NET haya expirado para los objetos activados por el cliente.
- Como se indicó anteriormente, al llamar a métodos en componentes fuera de proceso, DCOM se usa para cambiar el contexto y entregar la llamada al método. Si jiT ha desactivado el componente anteriormente y, a continuación, se realiza una llamada a Dispose(), se escribirá el contexto del servidor y se volverá a crear el objeto real para atender la llamada DCOM y, por último, se desactivará de nuevo. En el caso de los componentes en proceso, si se ha desactivado el objeto real, no se intenta cambiar al contexto correcto antes de atender la llamada Dispose() (que volvería a activar el componente), en su lugar, solo se destruye el contexto.
Agrupación de objetos
La premisa básica de la agrupación de objetos es la reutilización de objetos. La agrupación de objetos se usa con más frecuencia con JIT. Esto se aplica tanto a los componentes COM agrupados como a los componentes de .NET agrupados.
using System;
using System.EnterpriseServices;
[assembly: AssemblyKeyFile("Demos.snk")]
[assembly: ApplicationName("OPDemo")]
namespace Demos
{
[ObjectPooling(MinPoolSize=2, MaxPoolSize=50, CreationTimeOut=20)]
[JustInTimeActivation]
public class DbAccount : ServicedComponent
{
[AutoComplete]
public bool Perform ()
{ // Do something
}
public override void Activate()
{ // .. handle the Activate message
}
public override void Deactivate()
{ // .. handle the Deactivate message
}
public override bool CanBePooled()
{ // .. handle the CanBe Pooled message
// The base implementation returns false
return true;
}
}
}
Como sucede cuando se usa JIT, la agrupación de objetos se puede usar de una de estas dos maneras:
- Patrón de llamada única. En el código, el objeto se recupera del grupo cuando el cliente intenta realizar una llamada de método y se devuelve al grupo al salir de la llamada de método único suponiendo que JIT se usa con la agrupación de objetos y el bit de doneness se establece en true durante la llamada al método. Aquí también se aplican los mismos enfoques de llamada única para usar JIT. Solo se llama al constructor una vez cuando se crea el objeto y se coloca en el grupo. El orden de llamada de método al usar JIT y los objetos agrupados es: Activate, la llamada al método, Deactivate y CanBePooled. Si CanBePooled devuelve true, el objeto se vuelve a colocar en el grupo (aunque el contexto permanece activo como se ha descrito anteriormente). El mismo orden de llamada de método se repite para las llamadas a métodos posteriores (sin volver a llamar al constructor) después de extraer un objeto arbitrario del grupo (los componentes con servicio no pueden usar constructores con parámetros). Por último, si el cliente llama a Dispose() en el objeto agrupado, solo se destruye el contexto en el caso en proceso. En el caso fuera del proceso, como se indicó anteriormente, la llamada a Dispose() puede volver a activar el objeto. Si el objeto está agrupado, se debe obtener uno del grupo, lo que significa que Dispose() puede producir una excepción con CO_E_ACTIVATION_TIMEOUT.
- Patrón de varias llamadas. Con métodos similares de llamada de varios métodos resaltados en el servicio JIT, el objeto se puede volver a colocar en el grupo solo después de varias llamadas de método en el objeto . Sin embargo, si el cliente no llama a Dispose y JIT no se usa, no hay ninguna manera de asegurarse de que los objetos secundarios del objeto agrupado que requieren finalización se puedan reactivar cuando el objeto vuelve a colocarse en el grupo por parte del GC. Cuando el objeto agrupado se recolecte de elementos no utilizados, tampoco habría ninguna garantía en Desactivar qué miembros siguen siendo válidos. En la próxima versión de la .NET Framework (V1.1), no se llama a canBePooled y Deactivate y el objeto no se vuelve a colocar en el grupo. Con este enfoque hay un modelo más coherente: en Desactivar objetos secundarios están activos, en Dispose(), no se garantiza que los objetos secundarios estén activos. Por lo tanto, es fundamental que se llame a Dispose() para los objetos agrupados que no usan JIT; de lo contrario, el objeto no se devolverá al grupo.
Es aceptable que un administrador modifique el tamaño y los tiempos de espera del grupo una vez implementado y registrado el ensamblado. Los cambios de tamaño del grupo surten efecto cuando se reinicia el proceso. En Windows XP o superior, el tamaño del grupo se aplica a cada dominio de aplicación dentro del proceso. En Windows 2000, el tamaño del grupo es un proceso amplio con un objeto agrupado que reside en el dominio de aplicación predeterminado, lo que significa que si se requiere un objeto agrupado desde otro dominio de aplicación dentro del mismo proceso, el cliente se comunica eficazmente entre el dominio de aplicación al objeto agrupado. Una realización de esto es usar objetos .NET agrupados definidos en una aplicación de biblioteca COM+ desde dentro de una aplicación de ASP.NET donde cada vroot de IIS se hospeda en dominios de aplicación independientes.
Los componentes con servicio no pueden usar constructores con parámetros.
Seguridad
Seguridad de acceso del código (CAS)
La seguridad .NET Framework permite que el código tenga acceso a los recursos solo si tiene permiso para hacerlo. Para expresar esto, el .NET Framework usa el concepto de permisos, que representa el derecho para que el código acceda a los recursos protegidos. El código solicita los permisos que necesita. El .NET Framework proporciona clases de permisos de acceso de código. Como alternativa, se pueden escribir clases de permisos personalizadas. Estos permisos se pueden usar para indicar al .NET Framework qué debe permitirse hacer el código e indicar qué deben hacer los llamadores del código. Cualquier ruta de acceso de código a través de System.EnterpriseServices solicita permisos de código no administrados.
La seguridad de acceso al código en .NET es más útil en las aplicaciones en las que el código se descarga desde la web y es posible que el autor no sea de plena confianza. Normalmente, las aplicaciones que usan componentes con servicio son de plena confianza, requieren seguridad para fluir entre varios procesos y habilitar la configuración de roles en tiempo de implementación. Estas son características expuestas por la seguridad basada en roles de COM+.
Cualquier ruta de acceso de código a través de System.EnterpriseServices requiere permisos de código no administrados. Esto implica lo siguiente:
- Se requiere el permiso de código no administrado para activar y realizar llamadas entre contextos en componentes con servicio.
- Si se pasa una referencia a un componente con servicio al código que no es de confianza, no se puede llamar a los métodos definidos en ServicedComponent desde el código que no es de confianza. Sin embargo, los métodos personalizados definidos en una clase derivada de ServicedComponent se pueden llamar desde código que no es de confianza en algunas circunstancias: las llamadas desde código que no son de confianza se pueden realizar en esos métodos personalizados que no requieren cambio de contexto, servicios de interceptación y si la implementación del método no realiza llamadas a miembros de System.EnterpriseServices.
Además, en la versión 1 de .NET, la pila de seguridad no se copia cuando se realiza un modificador de subproceso para que no se usen permisos de seguridad personalizados en componentes con servicio.
seguridad de Role-Based (RBS)
System.EnterpriseServices proporciona servicios de seguridad a objetos .NET que reflejan la funcionalidad de los mecanismos de seguridad COM+. Cuando se usa una aplicación de servidor COM+ para hospedar los componentes, las características de RBS requieren que el protocolo de transporte DCOM se use para activar los componentes desde un cliente remoto. En la sección siguiente se proporcionan más detalles sobre la comunicación remota. Por lo tanto, el contexto y la identidad de la llamada de seguridad en COM+ están disponibles para el código administrado. Además, CoImpersonateClient, CoInitializeSecurity y CoRevertClient son llamadas conocidas que se usan generalmente en el lado servidor, mientras que CoSetProxyBlanket se usa generalmente en el lado cliente.
Algunas opciones de seguridad no se almacenan en metadatos mediante atributos, por ejemplo, agregando usuarios a roles y estableciendo la identidad de seguridad del proceso. Sin embargo, los atributos de nivel de ensamblado se pueden usar para configurar lo que aparece en la pestaña de seguridad del explorador COM+ para una aplicación de servidor COM+:
Habilitación de la autorización para la aplicación (ApplicationAccessControlAttribute(bool)). Esto es necesario para admitir RBS.
Nivel de seguridad (ApplicationAccessControlAttribute(AccessChecksLevelOption)). Si se establece en AccessChecksLevelOption.Application, los usuarios asignados a roles de la aplicación se agregan al descriptor de seguridad del proceso y se desactiva la comprobación de roles específicos en los niveles de componente, método e interfaz. Por lo tanto, las comprobaciones de seguridad solo se realizan en el nivel de aplicación y las aplicaciones de biblioteca dependen del proceso de host para la seguridad de nivel de proceso. Si el atributo se establece en AccessChecksLevelOption.ApplicationComponent, los usuarios asignados a roles de la aplicación se agregan al descriptor de seguridad del proceso y las comprobaciones de seguridad basadas en roles se realizan en la aplicación. Además, las comprobaciones de acceso también deben estar habilitadas para cada componente que requiera RBS aplicando el atributo ComponentAccessControl en la clase . En una aplicación de biblioteca, las comprobaciones de seguridad basadas en roles se realizan como si fuera una aplicación de servidor. La propiedad de seguridad se incluye en el contexto de todos los objetos de la aplicación y el contexto de llamada de seguridad está disponible. Si un objeto tiene una configuración incompatible con el contexto de su creador, se activa en su propio contexto. La seguridad basada en roles mediante programación se basa en la disponibilidad del contexto de llamada de seguridad.
Para que cualquier comprobación de acceso significativa funcione para las aplicaciones de biblioteca COM+, elija realizar comprobaciones de acceso en el nivel de proceso y componente.
Las selecciones de suplantación y autenticación corresponden a las propiedades ImpersonationLevel y Authentication del atributo ApplicationAccessControl.
El atributo SecurityRole se puede aplicar al nivel de ensamblado, clase o método. Cuando se aplica en el nivel de ensamblado, los usuarios de ese rol pueden activar cualquier componente de la aplicación. Cuando se aplica al nivel de clase, los usuarios de ese rol pueden, además, llamar a cualquier método en el componente. Los roles de nivel de aplicación y clase se pueden configurar en metadatos o de forma administrativa mediante el acceso al catálogo COM+.
Configuración de RBS en el nivel de ensamblado mediante metadatos:
[assembly: ApplicationAccessControl(true, AccessCheckLevel=AccessChecksLevelOption.ApplicationComponent)] // adds NTAuthority\everyone to this role [assembly:SecurityRole("TestRole1",true)] // add users to roles administratively [assembly:SecurityRole("TestRole2")]Configuración de RBS en el nivel de clase en metadatos:
[assembly: ApplicationAccessControl(true, AccessCheckLevel=AccessChecksLevelOption.ApplicationComponent)] … [ComponentAccessControl()] [SecurityRole("TestRole2")] public class Foo : ServicedComponent { public void Method1() {} }RBS en el nivel de ensamblado o clase se puede configurar administrativamente porque esas entidades existen en el catálogo COM+ después de que se haya registrado el ensamblado. Sin embargo, como se ha explicado anteriormente, los métodos de clase no aparecen en el catálogo COM+. Para configurar RBS en métodos, la clase debe implementar métodos de una interfaz y debe usar el atributo SecureMethod en el nivel de clase, o SecureMethod o SecurityRole en el nivel de método. Además, los atributos deben aparecer en la implementación del método de clase, no en el método de interfaz en la definición de la interfaz.
La manera más fácil de usar RBS en métodos es aplicar el atributo SecureMethod en el nivel de clase y, a continuación, configurar roles (ya sea administrativamente o colocando el atributo SecurityRole en métodos).
[assembly: ApplicationAccessControl(true, AccessCheckLevel=AccessChecksLevelOption.ApplicationComponent)] Interface IFoo { void Method1(); void Method2(); } [ComponentAccessControl()] [SecureMethod] public class Foo : ServicedComponent, IFoo { // Add roles to this method administratively public void Method1() {} // "RoleX" is added to the catalog for this method SecurityRole("RoleX") public void Method2() {} }El uso de SecureMethod en el nivel de clase permite configurar todos los métodos de todas las interfaces de la clase con roles en el catálogo COM+de forma administrativa. Si la clase implementa dos interfaces cada una con el mismo nombre de método y roles se configuran administrativamente, los roles deben configurarse en ambos métodos tal y como aparecen en el catálogo COM+(a menos que la clase implemente el método específico, por ejemplo, IFoo.Method1). Sin embargo, si se usa el atributo SecurityRole en el método de clase, todos los métodos con el mismo nombre de método se configuran automáticamente con ese rol cuando se registra el ensamblado.
El atributo SecureMethod también se puede colocar en el nivel de método.
[assembly: ApplicationAccessControl(true, AccessCheckLevel=AccessChecksLevelOption.ApplicationComponent)] Interface IFoo { void Method1(); void Method2(); } [ComponentAccessControl()] public class Foo : ServicedComponent, IFoo { // Add roles to this method administratively [SecureMethod] // Or use SecurityRole (translates to SecureMethod++) public void Method1() {} public void Method2() {} }En el ejemplo, IFoo y ambos métodos aparecen en el catálogo COM+ y, por lo tanto, los roles se pueden configurar en cualquiera de los métodos administrativamente; sin embargo, el nivel de método RBS solo se aplica en Method1. Use SecureMethod o SecurityRole en todos los métodos que se necesitarán para participar en la seguridad de nivel de método RBS o colocar SecureMethod en el nivel de clase, como se ha descrito anteriormente.
Cada vez que se configura RBS en el nivel de método, se requiere el rol Marshaller: cuando se realizan llamadas a métodos y RBS no está configurado en métodos, la infraestructura de componentes con servicio realiza llamadas en IRemoteDispatch. Cuando se realizan llamadas a métodos y se configura RBS en métodos (cuando el atributo SecureMethod está presente), la llamada al método se realiza mediante DCOM mediante la interfaz asociada al método . Por lo tanto, DCOM garantizará que RBS se aplique en el nivel de método. Sin embargo, como se describe en las secciones Activación e interceptación, la interoperabilidad COM y el RSCP realizarán llamadas en IManagedObject (para permitir que los activadores remotos serializarán la referencia en su espacio) e IServicedComponentInfo (para consultar el objeto remoto). Estas interfaces están asociadas a componentes con servicio. Dado que el componente está configurado para realizar comprobaciones de nivel de método, es necesario asociar un rol a estas interfaces si la infraestructura realiza estas llamadas correctamente.
Por lo tanto, se agrega un rol Marshaller a la aplicación cuando se registra el ensamblado y los usuarios deben agregarse administrativamente a este rol. A menudo, todos los usuarios de la aplicación se agregan a este rol. Esto es algo diferente de COM+ no administrado, donde la configuración de RBS en métodos no requiere este paso de configuración adicional. Agregar automáticamente "Todos" a este rol durante el registro es un posible agujero de seguridad, ya que cualquier persona ahora podría activar (pero no llamar) componentes donde antes de que no tuvieran los derechos para activarlos. El rol Marshaller también se agrega a la interfaz IDisposable para permitir que los clientes eliminen el objeto. Una alternativa al rol Marshaller es que los usuarios agreguen los roles pertinentes a cada una de las tres interfaces mencionadas.
Componentes remotos
La clase ServicedComponent contiene MarshalByRefObject en su árbol de herencia y, por tanto, se puede acceder a ella desde clientes remotos. Hay muchas variaciones de cómo exponer componentes con servicio de forma remota. Se puede acceder a los componentes con servicio de forma remota mediante:
- El canal HTTP con componentes con servicio llamados desde o escritos en ASP.NET ofrece buenas opciones de seguridad y cifrado, junto con escalabilidad y rendimiento conocidos. Cuando se usa con SOAP, existen más opciones de interoperabilidad. Los componentes con servicio se pueden hospedar en IIS/ASP.NET como una aplicación de biblioteca COM+. Si se usa una aplicación de servidor COM+, el host de IIS/ASP.NET puede acceder a los componentes mediante DCOM.
- Una manera alternativa de exponer un componente con servicio como punto de conexión SOAP se describe en Servicios web COM+: La Check-Box Enrutar a servicios web XML.
- DCOM cuando los componentes con servicio se hospedan en Dllhost. Esta opción ofrece un rendimiento y seguridad óptimos y la capacidad de pasar contextos de servicio entre equipos. La pregunta de diseño principal al elegir una tecnología de comunicación remota debe ser si los servicios deben fluir entre máquinas o no. Por ejemplo, dentro de una granja de servidores donde se crea una transacción en un equipo y es necesario que la transacción continúe en otro equipo, DCOM es el único protocolo que se puede usar para lograrlo. Sin embargo, si los clientes simplemente necesitan llamar a un ServicedComponent remoto, los enfoques de canal HTTP o punto de conexión SOAP son buenas alternativas.
- Un canal de comunicación remota de .NET (por ejemplo, un canal TCP o personalizado). Para usar el canal TCP, necesita un proceso que escuche en un socket. En general, se usa un proceso personalizado para escuchar en un socket y, a continuación, hospedar componentes con servicio como una biblioteca COM+ o una aplicación de servidor. Como alternativa, Dllhost se puede usar como agente de escucha. Cualquiera de los enfoques es menos probable que se use y requeriría escribir un agente de escucha de socket personalizado con un rendimiento, escalabilidad y seguridad probados. Por lo tanto, las soluciones ASP.NET o DCOM son los mejores enfoques para la mayoría de los proyectos.
Para acceder a un componente con servicio de forma remota mediante DCOM y hospedado en Dllhost, primero asegúrese de que el ensamblado se registra en una aplicación de servidor COM+ y se coloca en la GAC en el equipo servidor. A continuación, use el recurso de exportación de aplicaciones COM+ para crear un archivo MSI para un proxy de aplicación. Instale el proxy de aplicación en el cliente. Incrustado en el proxy de aplicación es el ensamblado administrado. El instalador también registrará el ensamblado y lo colocará en la GAC en el equipo cliente. Por lo tanto:
- El .NET Framework debe instalarse en el cliente y en el servidor. Esto es necesario en el equipo cliente aunque solo los clientes no administrados accedan a los componentes con servicio remoto. En Windows plataformas 2000, también se requiere Service Pack 3.
- Después de desinstalar el proxy, el ensamblado también debe quitarse de la GAC.
La infraestructura después de activar el componente de servidor en código administrado desde el lado cliente se muestra en la figura 6.
El uso de DCOM implica que CLR se hospeda en Dllhost, lo que significa que el archivo de configuración de la aplicación dllhost.exe.config reside en el directorio system32. Esto también implica que el archivo de configuración se aplica a todos los procesos dllhost de la máquina. En la próxima versión de la .NET Framework (V1.1), el directorio raíz de la aplicación COM+ se puede establecer en la aplicación COM+ y ese directorio se usa para detectar los archivos de configuración y los ensamblados de la aplicación.
En el caso de los objetos activados por el cliente, cada vez que se solicita el URI de un objeto, se crea una concesión de duración para ese objeto. Como se ha descrito anteriormente en la sección Activación, el proxy de componente con servicio remoto solicita un URI. Esto también puede ocurrir cuando un componente con servicio en proceso existente se serializa en un proceso remoto; se solicita un URI cada vez que .NET serializa un objeto MBR fuera de un dominio de aplicación. El URI se usa para garantizar que las identidades de objeto de .NET son únicas y para evitar el encadenamiento de proxy. Por lo tanto, cuando un cliente administrado activa un componente con servicio remoto, se usan tiempos de concesión en el objeto de servidor. Tenga en cuenta que los clientes no administrados no tienen un proxy de componente con servicio remoto en el lado cliente y, por lo tanto, no solicitan el URI del objeto. En su lugar, los clientes no administrados usan DCOM para garantizar la identidad del objeto. Por lo tanto, los tiempos de concesión en los componentes con servicio no se usan cuando se activan desde clientes no administrados.
Cuando los tiempos de concesión intervienen en los componentes con servicio, se recomienda establecer los valores de tiempo de espera InitialLeaseTime y RenewOnCallTime en un valor pequeño, posiblemente incluso tan pequeño como 10 segundos. Los componentes con servicio se destruyen mediante Dispose() o que el GC limpie los objetos. Cuando se llama a Dispose(), el proxy de componente con servicio remoto liberará la referencia que tiene en el proxy DCOM y, a continuación, se pondrá a disposición del siguiente GC. El objeto de servidor procesará la llamada Dispose (o se crea un nuevo objeto de servidor para atender la llamada remota a Dispose()), destruirá el contexto COM+ asociado y, a continuación, se pondrá a disposición del siguiente GC, pero solo cuando se agote el tiempo de espera de concesión. Cuando el cliente no llama a Dispose(), el servidor primero tendrá que esperar a que el GC del lado cliente libere la referencia al proxy DCOM y, a continuación, haga que el contexto COM+ esté disponible para el siguiente GC después de que el tiempo de concesión haya expirado. Por lo tanto, llame a Dispose() y, además, reduzca el tiempo de concesión predeterminado. Incluso si el cliente permanece activo y expira el tiempo de concesión, la referencia de DCOM al objeto de servidor mantendrá activo el objeto de servidor. Sin embargo, la referencia DCOM no siempre se usa para mantener activo el componente con servicio. Cuando el cliente accede al objeto a través de un canal de comunicación remota clR o servicios SOAP de COM+, solo la referencia segura que se debe a la concesión mantendrá activo el componente con servicio.
Conclusión
En este artículo se han analizado solo algunos de los servicios disponibles para el código administrado. Todos los servicios COM+ están disponibles para código administrado, como niveles de aislamiento de transacciones, inicialización de procesos, servicios sin componentes y reciclaje de procesos. El .NET Framework ahora proporciona acceso igual a todos los servicios COM+ de una manera coherente y lógica. Además, varias partes innovadoras del .NET Framework, como ASP.NET, Microsoft ADO.NET y Mensajería, se integran profundamente con .NET Enterprise Services, haciendo uso de servicios como transacciones y agrupación de objetos. Esta integración proporciona una arquitectura coherente y un modelo de programación. El espacio de nombres System.EnterpriseServices proporciona el modelo de programación para agregar servicios a clases administradas.