Compartir a través de


Este artículo proviene de un motor de traducción automática.

Fundamentos

Aplicar fácilmente las transacciones A servicios

Juval Lowy

Descarga de código de la Galería de código de MSDN
Examinar el código en línea

Contenido

Estado de administración y transacciones
Transaccionales servicios por llamada
Administración de la instancia y transacciones
Servicios y VRMs basada en sesión
Servicios de duraderos transaccionales
El comportamiento transaccional
Agregar contexto a los enlaces de costes indirectos de PRODUCCIÓN
InProcFactory y transacciones

Un problema fundamental en la programación es la recuperación de errores. Tras producirse un error, la aplicación debe restaurar Sí en el estado que tenía antes de que el error tuvo lugar. Considere una aplicación que intenta realizar una operación con varias operaciones más pequeñas, potencialmente simultáneamente, en cada una de las operaciones individuales puede producirá un error o correctamente independientemente de los demás. Un error en cualquiera de las operaciones más pequeñas significa que el sistema está en un estado incoherente.

Tomar una aplicación de banca, por ejemplo, que transfiere fondos entre dos cuentas mediante una cuenta de asignación de crédito y cargarlo en el otro. Correctamente cargarlo una cuenta pero no crédito el otro es un estado incoherente, porque los fondos no pueden ser de ambos lugares en el mismo tiempo y si no se debe al correctamente asignación de crédito resultados con un estado incoherente igualmente en la que el dinero se ha desaparecido. Siempre es hacia arriba a la aplicación para recuperar del error mediante la restauración del sistema al estado original.

Mucho más fácil se dice que realizar para una serie de razones. En primer lugar, para una operación de gran tamaño, el número total de permutaciones de éxito parcial y error parcial Obtiene rápidamente fuera de mano. El resultado es frágil código que es muy costosa desarrollar y mantener y con bastante frecuencia realmente no funciona, puesto que los programadores a menudo sólo tratan con los casos de recuperación sencilla, que son los casos que están ambos en cuenta y saber cómo controlar. En segundo lugar, la operación compuesta puede formar parte de una operación mucho más grande, y incluso si el código ejecutado flawlessly, todavía es posible que tiene que deshacer si algo fuera de su control detecta un error. Esto implica estrecho acoplamiento entre las partes participantes en la administración y coordinación de las operaciones. Por último, también necesita aislar lo que hacer desde whomever otra es necesario para interactuar con el sistema, ya que si realizar una posterior de recuperación de un error mediante el resumen seguridad algunas de sus acciones, se pondrá alguien implícitamente en un estado de error.

Como puede ver, es prácticamente imposible escribir código de recuperación de errores sólido a mano. Este realización no es nuevo. Empezaron como software se utiliza en contextos de negocio (en el 1960s), era evidente que se tenía que ser una manera mejor de la administración de recuperación. Hay una manera mejor: las transacciones. Una transacción es un conjunto de operaciones donde un error en cualquier operación individual hace que todo el conjunto errores, como una operación atómica. Cuando se usan transacciones, no es necesario para escribir lógica de recuperación, ya que no hay nada para recuperar. Ambos todas las operaciones se realizó correctamente por lo que no hay nada para recuperar, o todos ellos no se pudo y no se pudo afectan al estado del sistema, por lo que hay también nada para recuperar.

Cuando se usan transacciones, es esencial para usar los administradores de recursos transaccionales. El administrador de recursos es capaz de deshacer todos los cambios realizados durante la transacción si anula la transacción y conservar los cambios si se confirma la transacción. El administrador de recursos también proporciona aislamiento; es decir, mientras hay una transacción en curso, el administrador de recursos impide todas otras partes (además de la transacción) de acceso a él y ver los cambios, que puede seguir deshacer. Este también significa que la transacción no debe nunca obtener acceso a los administradores que no sea de recursos, ya que los cambios realizados en los que no se deshacer si se anula la transacción y, por tanto, recuperación estará necesarios.

Tradicionalmente, los administradores de recursos estaban recursos duraderos, como bases de datos y las colas de mensajes. Sin embargo, en el artículo del problema de mayo de 2005 de MSDN Magazine titulado" ¿No se puede confirmar? Administradores de recursos volátil en .NET llevar transacciones en el tipo Common", Presenta la técnica de implementación de un administrador de propósito general de recursos volátil (VRM) denominado transaccional <t>:

public class Transactional<T> : ...
{
   public Transactional(T value);
   public Transactional();
   public T Value
   {get;set;}
   /* Conversion operators to and from T */
}

Si especifica cualquier parámetro de tipo serializable (como int o una cadena) para transaccional <t>, activar ese tipo en un administrador de recursos volátil auténtico que da de alta automática en la transacción de ambiente, confirma o revierta los cambios según en el resultado de la transacción y aísla los cambios actuales de todas las transacciones.

la figura 1 muestra el uso de transaccional <t>. Ya no es el ámbito completado, se anula la transacción, y los valores de número y la ciudad volver a su estado pre-transaction.

La figura 1 con <t> transaccional

Transactional<int> number = new Transactional<int>(3);
Transactional<string> city = new Transactional<string>("New York, ");

city.Value += "NY"; //Can use with or without transactions
using(TransactionScope scope = new TransactionScope())
{
   city.Value = "London, ";
   city.Value += "UK";
   number.Value = 4;
   number.Value++;
}
Debug.Assert(number == 3); //Conversion operators at work
Debug.Assert(city == "New York, NY");

La figura 1 con <t> transaccional

Transactional<int> number = new Transactional<int>(3);
Transactional<string> city = new Transactional<string>("New York, ");

city.Value += "NY"; //Can use with or without transactions
using(TransactionScope scope = new TransactionScope())
{
   city.Value = "London, ";
   city.Value += "UK";
   number.Value = 4;
   number.Value++;
}
Debug.Assert(number == 3); //Conversion operators at work
Debug.Assert(city == "New York, NY");

Además de transaccional <t>, también ha ofrece una matriz transaccional, así como las versiones transaccionales para todas las colecciones de System.Collections.Generic, tales como TransactionalDictionary <K,T>. Estas colecciones están polimórficas con sus cousins no transaccionales y se utilizan exactamente de la misma manera.

Estado de administración y transacciones

El único propósito de la programación transaccional es dejar el sistema en un estado coherente. En el caso de Windows Communication Foundation (WCF), el estado del sistema consta de los administradores de recursos además el estado en la memoria de las instancias de servicio. Mientras que los administradores de recursos administrar automáticamente su estado como un producto del resultado de la transacción, no es el caso de objetos en memoria o variables estáticas.

La solución a este problema de administración de estado es desarrollar un servicio como un servicio de estado, tenga en cuenta y administrar de forma proactiva su estado. Entre las transacciones, el servicio debe almacenar su estado en un administrador de recursos. Al principio de cada transacción, el servicio debe recuperar su estado desde el recurso y por esto inscribir por lo que el recurso en la transacción. Al final de la transacción, el servicio debe guardar su estado de nuevo en el administrador de recursos. Nuevamente, esta técnica se proporciona para recuperación automática de estado. Los cambios realizados en el estado de instancia se confirmar o deshacer como parte de la transacción.

Si se confirma la transacción, la próxima vez que el servicio obtiene su estado tendrá el post-transaction estado. Si se anula la transacción, la próxima vez tendrá su estado pre-transaction. En cualquier caso, el servicio tendrá un estado coherente listo para tener acceso a una nueva transacción.

Hay problemas restantes dos cuando se escribe servicios transaccionales. El primero es cómo puede saber el servicio cuando las transacciones de comienzo y finalización, para que pueda obtener y guarde su estado. El servicio puede formar parte de una mucho transacción más grande que abarca múltiples servicios y equipos. En cualquier momento entre llamadas, puede finalizar la transacción. ¿Quién llamará el servicio, permitiendo que saber para guardar su estado? El segundo problema tiene que ver con el aislamiento. Distintos clientes pueden llamar al servicio simultáneamente, en las transacciones diferentes. ¿Cómo puede aislar el servicio de cambio de una transacción realizado a su estado por otra transacción? Si la otra transacción era obtener acceso a su estado y hacer funcionar según sus valores, esa transacción sería incoherente si anula la transacción original y se revierten los cambios.

La solución a dos problemas es igualar los límites de método con límites de transacción. Al principio de cada llamada de método, el servicio debe leer su estado desde el administrador de recursos y al final de cada llamada de método, el servicio debe guardar su estado al administrador de recursos. Hacerlo garantiza que si se termina una transacción entre llamadas de método, estado del servicio se bien conservar o revertir con él. Además, leer y almacenar el estado en el administrador de recursos en cada método llamar direcciones el desafío de aislamiento porque el servicio simplemente permite el administrador de recursos aislar el acceso al estado entre las transacciones simultáneas.

Puesto que el servicio equivale método límites con límites de transacción, la instancia de servicio también debe votar en resultado de la transacción al final de cada llamada de método. Desde la perspectiva del servicio, finaliza la transacción una vez que el método devuelve. En WCF, esto se realiza automáticamente mediante la propiedad TransactionAutoComplete del atributo OperationBehavior. Cuando esta propiedad está establecida en true, si no había ningún excepciones no controladas en la operación, WCF se votar automáticamente para confirmar. Si se produjo una excepción no controlada, WCF se votar para anular. Dado que TransactionAutoComplete valor predeterminado es true, cualquier transacción método usa la finalización automática de forma predeterminada, así:

//These two definitions are equivalent:
[OperationBehavior(TransactionScopeRequired = true,
                   TransactionAutoComplete = true)]   
public void MyMethod(...)
{...}

[OperationBehavior(TransactionScopeRequired = true)]   
public void MyMethod(...)
{...}

Para obtener más sobre programación de WCF transaccional, consulte mi columna cimientos" Propagación de transacción de WCF"en el de 2007 emitir.

Transaccionales servicios por llamada

Con un servicio por llamada, una vez que se devuelve la llamada, se destruye la instancia. Por consiguiente, el administrador de recursos utilizado para almacenar el estado entre llamadas debe ser fuera del ámbito de la instancia. El cliente y el servicio deben también coinciden en el que las operaciones son responsables de crear o quitar la instancia del administrador de recursos.

Porque podría haber muchas instancias del tipo servicio mismo acceso al misma administrador de recursos, cada operación debe contener algún parámetro que permite que la instancia de servicio buscar su estado en el administrador de recursos y enlazar con él. SE llama a dicho parámetro el identificador de instancia. El cliente también debe llamar a una operación dedicada para quitar el estado de la instancia del almacén. Tenga en cuenta que los requisitos de comportamientos para un objeto de transaccional de estado, tenga en cuenta y un objeto por cada llamada al son el mismo: ambos recuperar y guardar su estado en los límites del método. Con un servicio por cada llamada al, cualquier administrador de recursos puede utilizarse para almacenar el estado de servicio. Puede utilizar una base de datos o se puede utilizar un VRM, como se muestra en la figura 2 .

La Figura 2 cada llamada A servicio con un VRM

[ServiceContract]
interface IMyCounter
{
   [OperationContract]
   [TransactionFlow(TransactionFlowOption.Allowed)]
   void Increment(string instanceId);

   [OperationContract]
   [TransactionFlow(TransactionFlowOption.Allowed)]
   void RemoveCounter(string instanceId);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
class MyService : IMyCounter
{
   static TransactionalDictionary<string,int> m_StateStore = 
                               new TransactionalDictionary<string,int>();

   [OperationBehavior(TransactionScopeRequired = true)]
   public void Increment(string instanceId)
   {
    if(m_StateStore.ContainsKey(instanceId) == false)
      {
         m_StateStore[instanceId] = 0;
      }
      m_StateStore[instanceId]++;
      Trace.WriteLine(m_StateStore[instanceId]); 
   }
   [OperationBehavior(TransactionScopeRequired = true)]
   public void RemoveCounter(string instanceId)
   {
    if(m_StateStore.ContainsKey(instanceId))
      {
         m_StateStore.Remove(instanceId);
      }
   }
}

//Client side:
MyCounterClient proxy = new MyCounterClient();

using(TransactionScope scope = new TransactionScope())
{
   proxy.Increment("MyInstance");
   scope.Complete();
}    

//This transaction will abort since the scope is not completed 
using(TransactionScope scope = new TransactionScope())
{
   proxy.Increment("MyInstance");
} 

using(TransactionScope scope = new TransactionScope())
{
   proxy.Increment("MyInstance");
   proxy.RemoveCounter("MyInstance");
   scope.Complete();
}

proxy.Close();

//Traces:
1
2
2

Administración de la instancia y transacciones

WCF obliga a la instancia de servicio para igualar los límites de método con límites de transacción y que el estado, tenga en cuenta, lo que significa para purgar todos los su estado de instancia en los límites del método. De forma predeterminada, una vez finalizada la transacción, en WCF se destruye la instancia de servicio, garantizar no están leftovers en la memoria que puede poner en peligro la coherencia.

El ciclo de vida de cualquier servicio transaccional se controla mediante la propiedad ReleaseServiceInstanceOnTransactionComplete del atributo ServiceBehavior. Cuando ReleaseServiceInstanceOn­TransactionComplete está establecida en true (valor predeterminado), elimina de la instancia de servicio una vez que el método finaliza la transacción, en efecto activación cualquier servicio WCF en un servicio por cada llamada al como hasta que la instancia modelo de programación se refiere.

No se originó este enfoque heavy-handed con WCF. Todos los distribuyen transaccionales modelos de programación en la plataforma de Microsoft, empezaron desde MTS, a través de COM + y Enterprise Services, igualar un objeto transaccional con un objeto por cada llamada al. Los arquitectos de estas tecnologías simplemente no de confianza a los desarrolladores administrar correctamente el estado del objeto en la cara de transacciones, algo que es ambos complicadas y un modelo de programación no intuitivo. La principal desventaja es que todos los desarrolladores que desean beneficiarse de las transacciones tienen que adopte la programación no trivial por llamada modelo (consulte la figura 2 ), mientras que la mayoría de los desarrolladores sientan mucho más en la facilidad con el familiar basada en sesión estado modelo de programación de objetos de Microsoft .NET Framework regulares.

Personalmente han siempre pensó que equating transacciones con la creación de instancias por llamada es un mal necesario, y aún, conceptualmente, se distorsione. Sólo uno debe elija el modo instancing por llamada cuando la escalabilidad es necesaria y, lo ideal es que las transacciones deben separarse de la administración de instancia de objeto y independientemente de la aplicación de escalabilidad.

Si la aplicación se requiere para ajustar el tamaño, a continuación, elegir por llamada y usan transacciones funcionarán muy bien juntos. Sin embargo, si no necesita escalabilidad (que es probablemente el caso común con la mayoría de las aplicaciones) se deben permite los servicios para ser basada en sesión, estado y transacciones. El resto de esta columna presenta la solución al problema de habilitación y conservar el modelo de programación basada en sesión al utilizar las transacciones con los servicios comunes.

Servicios y VRMs basada en sesión

WCF podrá mantener la sesión semántica con un servicio transaccional estableciendo ReleaseServiceInstanceOn­TransactionComplete en false. En este caso WCF permanecerá fuera de la vista y permitirá que el desarrollador de servicio preocuparse de administrar el estado de la instancia del servicio de transacciones. El servicio por sesión todavía debe igualar el método límites con límites de transacción ya esté todas las llamadas a métodos de una transacción diferente y puede finalizar una transacción entre llamadas de método en la misma sesión. Mientras que puede administrar manualmente dicho estado sólo como con una por cada llamada al servicio (o utilizar las características de algunos otro WCF avanzada fuera del ámbito de esta columna), puede utilizar VRMs para los miembros de servicio, tal como se muestra en la figura 3 .

La figura 3 mediante VRMs por servicio transaccional por sesión

[ServiceBehavior(ReleaseServiceInstanceOnTransactionComplete = false)]
class MyService : IMyContract
{
   Transactional<string> m_Text = new Transactional<string>("Some initial value");
   TransactionalArray<int> m_Numbers = new TransactionalArray<int>(3);

   [OperationBehavior(TransactionScopeRequired = true)]
   public void MyMethod()
   {
      m_Text.Value = "This value will roll back if the transaction aborts";

      //These will roll back if the transaction aborts
      m_Numbers[0] = 11;
      m_Numbers[1] = 22;
      m_Numbers[2] = 33;
   }
}

La figura 3 mediante VRMs por servicio transaccional por sesión

[ServiceBehavior(ReleaseServiceInstanceOnTransactionComplete = false)]
class MyService : IMyContract
{
   Transactional<string> m_Text = new Transactional<string>("Some initial value");
   TransactionalArray<int> m_Numbers = new TransactionalArray<int>(3);

   [OperationBehavior(TransactionScopeRequired = true)]
   public void MyMethod()
   {
      m_Text.Value = "This value will roll back if the transaction aborts";

      //These will roll back if the transaction aborts
      m_Numbers[0] = 11;
      m_Numbers[1] = 22;
      m_Numbers[2] = 33;
   }
}

El uso de VRMs permite un modelo de programación con estado: tiene acceso la instancia de servicio simplemente a su estado como si no hay transacciones implicados. Los cambios realizados en el estado se confirmar o deshacer la transacción. Sin embargo, PUEDO encontrar la figura 3 para tener un experto en modelo de programación y, por tanto, tiene sus propia dificultades. Requiere estar familiarizado con VRM, definición cabo un meticuloso sistema de miembros, así como la disciplina para siempre configurar todas las operaciones para requerir transacciones y, para deshabilitar la liberación de la instancia después de finalizar.

Servicios de duraderos transaccionales

En la entrega de 2008 de octubre de esta columna " Administración de estado con servicios duraderos"), Presenta la compatibilidad que WCF ofrece para servicios duraderos. Un servicio duradero recupera su estado desde el almacén configurado y lo guarda a continuación, su estado en ese almacén en cada operación. El almacén de estado puede o no es un administrador de recursos transaccionales.

Si el servicio es transaccional, por supuesto debe utilizar sólo almacenamiento transaccional y dar de alta en la transacción de cada operación. De este modo, si se anula una transacción, el almacén de estado se volver a su estado pre-transaction. Sin embargo, WCF no sabe si un servicio está diseñado para propagar sus transacciones en el almacén de estado y de forma predeterminada inscribir no el almacenamiento en la transacción incluso si el almacenamiento es un jefe de recursos transaccionales, como SQL Server 2005 o SQL Server 2008. Para indicar a WCF para propagar la transacción y inscribir el almacenamiento subyacente, establezca la propiedad de SaveStateInOperationTransaction del atributo DurableService en true:

[Serializable]
[DurableService(SaveStateInOperationTransaction = true)]
class MyService: IMyContract
{...}

SaveStateInOperationTransaction predeterminados en false, por tanto, almacenamiento de estados no participará en la transacción. Insisten puesto que sólo un servicio transaccional puede beneficiarse de tener el conjunto de SaveStateInOperation­Transaction en true, si es true, a continuación, WCF se en que todas las operaciones en el servicio tienen TransactionScopeRequired establecido en true o tienen el flujo de transacciones obligatorio. Si la operación está configurada con TransactionScopeRequired establecida en true, la transacción de ambiente de la operación se la utilizará para inscribir el almacenamiento.

El comportamiento transaccional

En el caso del atributo DurableService, la palabra duradera es " una misnomer " puesto que no indica necesariamente un comportamiento duradero aquí. Todo lo significa que WCF se automáticamente deserializar el estado del servicio de un almacenamiento configurado y, a continuación, serializarlo volver en cada operación. De forma similar, el comportamiento de proveedor de persistencia no significa necesariamente persistencia, puesto que cualquier proveedor que deriva de la clase de proveedor abstracta recomendada será suficiente.

El hecho de que la infraestructura de servicio duradero es, en realidad, una infraestructura de serialización habilitado me lo aprovechar en una técnica para administrar el estado del servicio en la superficie de transacciones, al depender por debajo de un administrador de recursos volátil, sin necesidad de la instancia de servicio nada sobre él. Esto aún más simplifica el modelo de programación transaccional de WCF y proporciona la ventaja de que el modelo de programación superior de las transacciones para objetos simple y los servicios comunes.

El primer paso fue definir dos fábricas transaccional proveedor en la memoria denominadas TransactionalMemoryProviderFactory y TransactionalInstanceProviderFactory. El TransactionalMemory­ProviderFactory utiliza un TransactionalDictionary estático <ID,T> para almacenar las instancias de servicio. El diccionario se comparte entre todos los clientes y las sesiones. Mientras se ejecuta el host, TransactionalMemory­ProviderFactory permite a los clientes conectar y desconectar desde el servicio. Cuando se utiliza TransactionalMemoryProviderFactory debe designar una operación completando que quita el estado de la instancia del almacén mediante la propiedad CompletesInstance del atributo DurableOperation de.

Por otro lado, TransactionalInstanceProviderFactory, coincide con cada sesión con una instancia dedicada de transaccional <t>. No es necesario para una operación completando ya que el estado del servicio se recopilan elementos no utilizados después de cerrar la sesión.

A continuación, define el atributo TransactionalBehavior, que se muestra en la figura 4 . TransactionalBehavior es un atributo de comportamiento de servicio que realiza estas configuraciones. En primer lugar, inserta en la descripción de servicio un atributo DurableService con SaveStateIn­OperationTransaction establecido en true. En segundo lugar, se agrega el uso de marcas TransactionalMemoryProviderFactory ni TransactionalInstance­ProviderFactory para el comportamiento persistente de acuerdo con en el valor de la propiedad AutoCompleteInstance. Si AutoCompleteInstance está establecida en true (valor predeterminado) utiliza TransactionalInstance­ProviderFactory. Por último, si la propiedad TransactionRequiredAllOperations se establece en true (valor predeterminado), TransactionalBehavior establecerá Transaction­ScopeRequired en true en todos los servicios operación comportamientos, con lo que todas las operaciones con una transacción de ambiente. Cuando se establece explícitamente en false, el desarrollador de servicio puede elegir qué operaciones será transaccionales.

Figura 4 el atributo TransactionalBehavior

[AttributeUsage(AttributeTargets.Class)]
public class TransactionalBehaviorAttribute : Attribute,IServiceBehavior
{
   public bool TransactionRequiredAllOperations
   {get;set;}

   public bool AutoCompleteInstance
   {get;set;}

   public TransactionalBehaviorAttribute()
   {
      TransactionRequiredAllOperations = true;
      AutoCompleteInstance = true;   
   }
   void IServiceBehavior.Validate(ServiceDescription description,
                                  ServiceHostBase host) 
   {
      DurableServiceAttribute durable = new DurableServiceAttribute();
      durable.SaveStateInOperationTransaction = true;
      description.Behaviors.Add(durable);

      PersistenceProviderFactory factory;
      if(AutoCompleteInstance)
      {
         factory = new TransactionalInstanceProviderFactory();
      }
      else
      {
         factory = new TransactionalMemoryProviderFactory();
      }

      PersistenceProviderBehavior persistenceBehavior = 
                                new PersistenceProviderBehavior(factory);
      description.Behaviors.Add(persistenceBehavior);

      if(TransactionRequiredAllOperations)
      {
         foreach(ServiceEndpoint endpoint in description.Endpoints)
         {
            foreach(OperationDescription operation in endpoint.Contract.Operations)
            {
               OperationBehaviorAttribute operationBehavior =  
                  operation.Behaviors.Find<OperationBehaviorAttribute>();
               operationBehavior.TransactionScopeRequired = true;
            }
         }
      }
   }
   ...
} 

Cuando se utiliza el atributo TransactionalBehavior con los valores predeterminados, el cliente no es necesario para administrar o interactuar de ningún modo con el identificador de instancia. Todo lo que es necesario para el cliente que hacer es utilizar el proxy sobre uno de los enlaces de contexto y permiten el enlace de administrar el IDENTIFICADOR de instancia, tal como se muestra en la figura 5 . Tenga en cuenta que el servicio está interactuando con un entero normal como la variable de miembro. La cosa interesante es que causa del comportamiento duradero, la instancia aun así, se desactiva por supuesto, como un servicio por cada llamada al método límites, pero el modelo de programación de un objeto .NET común es.

La figura 5 mediante el atributo TransactionalBehavior

[ServiceContract]
interface IMyCounter
{
   [OperationContract]
   [TransactionFlow(TransactionFlowOption.Allowed)]
   void Increment();
}

[Serializable]
[TransactionalBehavior]
class MyService : IMyCounter
{
   int m_Counter = 0;

   public void Increment()
   {
      m_Counter++;
      Trace.WriteLine(m_Counter);
   }
}
//Client side:
MyCounterClient proxy = new MyCounterClient();

using(TransactionScope scope = new TransactionScope())
{
   proxy.Increment();
   scope.Complete();
}    

//This transaction will abort since the scope is not completed 
using(TransactionScope scope = new TransactionScope())
{
   proxy.Increment();
} 

using(TransactionScope scope = new TransactionScope())
{
   proxy.Increment();
   scope.Complete();
}

proxy.Close();

//Traces:
1
2
2

La figura 5 mediante el atributo TransactionalBehavior

[ServiceContract]
interface IMyCounter
{
   [OperationContract]
   [TransactionFlow(TransactionFlowOption.Allowed)]
   void Increment();
}

[Serializable]
[TransactionalBehavior]
class MyService : IMyCounter
{
   int m_Counter = 0;

   public void Increment()
   {
      m_Counter++;
      Trace.WriteLine(m_Counter);
   }
}
//Client side:
MyCounterClient proxy = new MyCounterClient();

using(TransactionScope scope = new TransactionScope())
{
   proxy.Increment();
   scope.Complete();
}    

//This transaction will abort since the scope is not completed 
using(TransactionScope scope = new TransactionScope())
{
   proxy.Increment();
} 

using(TransactionScope scope = new TransactionScope())
{
   proxy.Increment();
   scope.Complete();
}

proxy.Close();

//Traces:
1
2
2

Agregar contexto a los enlaces de costes indirectos de PRODUCCIÓN

TransactionalBehavior requiere un enlace que admita el protocolo de contexto. Aunque WCF proporciona compatibilidad con contexto para la básica, servicios Web (WS) y los enlaces de TCP, falta de esa lista es la comunicación entre procesos (IPC; también llamadas canalizaciones), enlace. Sería útil disponen de compatibilidad que con el IPC enlace ya que podría permitir el uso de TransactionalBehavior a través de costes indirectos de PRODUCCIÓN, generando las ventajas de costes indirectos de PRODUCCIÓN para las llamadas profundo. Con ese fin, he definido la clase NetNamedPipeContextBinding:

public class NetNamedPipeContextBinding : NetNamedPipeBinding
{
   /* Same constructors as NetNamedPipeBinding */

   public ProtectionLevel ContextProtectionLevel
   {get;set;}
}

NetNamedPipeContextBinding sirve exactamente igual que su clase base. Puede utilizar este enlace mediante programación como cualquier otro enlace integrado. Sin embargo, cuando se utiliza un enlace personalizado en el archivo .config la aplicación, debe informar a WCF donde se define el enlace personalizado. Aunque puede hacerlo en cada aplicación, la opción más fácil es hacer referencia a la clase auxiliar NetNamedPipe­ContextBindingCollectionElement en machine.config afecte a todas las aplicaciones en el equipo, tal como se muestra aquí:

<!--In machine.config-->
<bindingExtensions>
   ...
   <add name = "netNamedPipeContextBinding" 
        type = "ServiceModelEx.                NetNamedPipeContextBindingCollectionElement,
                ServiceModelEx"
   />
</bindingExtensions>

También puede utilizar NetNamedPipeContextBinding en sus aplicaciones de flujo de trabajo.

la figura 6 enumera un extracto de la implementación de NetNamedPipeContextBinding y sus clases auxiliares (se puede encontrar la implementación completa en la descarga de código de este mes). Los constructores de NetNamed­PipeContextBinding todo delegar la construcción real a los constructores base de NetNamedPipeBinding y la inicialización única tienen es establecer el nivel de protección de contexto en predeterminada para ProtectionLevel.EncryptAndSign.

Figura 6 implementación NetNamedPipeContextBinding

public class NetNamedPipeContextBinding : NetNamedPipeBinding
{
   internal const string SectionName = "netNamedPipeContextBinding";

   public ProtectionLevel ContextProtectionLevel
   {get;set;}

   public NetNamedPipeContextBinding()
   {
      ContextProtectionLevel = ProtectionLevel.EncryptAndSign;
   }
   public NetNamedPipeContextBinding(NetNamedPipeSecurityMode securityMode) : 
                                                      base(securityMode)
   {
      ContextProtectionLevel = ProtectionLevel.EncryptAndSign;
   }
   public NetNamedPipeContextBinding(string configurationName)
   {
      ContextProtectionLevel = ProtectionLevel.EncryptAndSign;
      ApplyConfiguration(configurationName);
   }

   public override BindingElementCollection CreateBindingElements()
   {
      BindingElement element = new ContextBindingElement(ContextProtectionLevel,
                            ContextExchangeMechanism.ContextSoapHeader);

      BindingElementCollection elements = base.CreateBindingElements();
      elements.Insert(0,element);

      return elements;
   }

   ... //code excerpted for space
}

Figura 6 implementación NetNamedPipeContextBinding

public class NetNamedPipeContextBinding : NetNamedPipeBinding
{
   internal const string SectionName = "netNamedPipeContextBinding";

   public ProtectionLevel ContextProtectionLevel
   {get;set;}

   public NetNamedPipeContextBinding()
   {
      ContextProtectionLevel = ProtectionLevel.EncryptAndSign;
   }
   public NetNamedPipeContextBinding(NetNamedPipeSecurityMode securityMode) : 
                                                      base(securityMode)
   {
      ContextProtectionLevel = ProtectionLevel.EncryptAndSign;
   }
   public NetNamedPipeContextBinding(string configurationName)
   {
      ContextProtectionLevel = ProtectionLevel.EncryptAndSign;
      ApplyConfiguration(configurationName);
   }

   public override BindingElementCollection CreateBindingElements()
   {
      BindingElement element = new ContextBindingElement(ContextProtectionLevel,
                            ContextExchangeMechanism.ContextSoapHeader);

      BindingElementCollection elements = base.CreateBindingElements();
      elements.Insert(0,element);

      return elements;
   }

   ... //code excerpted for space
}

El corazón de cualquier clase de enlace es el método CreateBindingElements. NetNamedPipeContextBinding tiene acceso a su colección de enlace de base de los elementos de enlace y agrega a la ContextBinding­Element. Insertar este elemento en la colección agrega compatibilidad con el protocolo de contexto.

El resto de la implementación es simple Contabilidad para habilitar la configuración administrativa. Se llama al método ApplyConfiguration mediante el constructor que toma el nombre de configuración de enlace de sección. ApplyConfiguration utiliza la clase ConfigurationManager para analizar fuera del archivo .config la sección netNamedPipeContextBinding y de una instancia de NetNamedPipeContextBinding­Element. Ese elemento de enlace, a continuación, se utiliza para configurar la instancia de enlace llamando a su método ApplyConfiguration.

Los constructores de NetNamedPipeContextBinding­Element agregar a su clase base colección Properties de las propiedades de configuración de una propiedad única para el nivel de protección de contexto. En OnApply­Configuration (que se denominan como resultado de NetNamedPipeContextBinding.ApplyConfiguration llamada Apply­Configuration), el método configura en primer lugar su elemento base y, a continuación, establece el nivel de protección contexto según en el nivel configurado.

El tipo de NetNamedPipeContextBindingCollectionElement se utiliza para enlazar NetNamedPipeContextBinding con la NetNamed­PipeContextBindingElement. Este modo, al agregar NetNamedPipeContextBindingCollectionElement como una extensión de enlace, el administrador de la configuración sabe que escriba para crear instancias y proporcionar con los parámetros de enlace.

InProcFactory y transacciones

El atributo TransactionalBehavior permite tratar casi todas las clases de la aplicación como transacciones sin poner en peligro en el modelo programación de .NET familiar. La desventaja es que WCF nunca se ha diseñado para utilizarse en un nivel muy granular, habrá que crear, abrir y cerrar múltiples hosts, y su archivo de aplicación .config será difícil de administrar con puntuaciones de secciones de servicio y cliente. Para solucionar estos problemas, en mi libro programación WCF, 2nd Edition he definido una clase denominada InProcFactory, que le permite crear una instancia una clase de servicio a través de WCF:

public static class InProcFactory
{
   public static I CreateInstance<S,I>() where I : class
                                         where S : I;
   public static void CloseProxy<I>(I instance) where I : class;
   //More members
}

Cuando se utiliza InProcFactory, usar WCF en el nivel de clase sin nunca recurrir administración el host o tener cliente o servicio archivos .config explícitamente. Para que el modelo de programación de TransactionalBehavior sea accesible en cada nivel de clase, la clase InProcFactory utiliza NetNamedPipeContextBinding con flujo de transacciones habilitada. Uso de las definiciones de la figura 5 , InProcFactory permite el modelo de programación de la figura 7 .

La figura 7 combinar TransactionalBehavior con InProcFactory

IMyCounter proxy = InProcFactory.CreateInstance<MyService,IMyCounter>();

using(TransactionScope scope = new TransactionScope())
{
   proxy.Increment();
   scope.Complete();
}    

//This transaction will abort since the scope is not completed 
using(TransactionScope scope = new TransactionScope())
{
   proxy.Increment();
} 
using(TransactionScope scope = new TransactionScope())
{
   proxy.Increment();
   scope.Complete();
}

InProcFactory.CloseProxy(proxy);

//Traces:
Counter = 1
Counter = 2
Counter = 2

El modelo de programación de la figura 7 es idéntico a la de clases de C# sin formato, sin ninguna sobrecarga de la propiedad, y aún el código completo se beneficia de las transacciones. Verá como un paso fundamental para el futuro, en memoria sí será transaccional y será posible para cada objeto que transaccional.

Figura 8 muestra la implementación de la InProcFactory con algún código eliminado por brevedad. Llama del InProcFactory estático al constructor una vez por dominio de aplicación, asignación de cada una nueva dirección base única con un GUID. Esto permite InProcFactory se utilizar varias veces en el mismo equipo, en dominios de aplicación y los procesos.

Figura 8 de la clase InProcFactory

public static class InProcFactory
{
   struct HostRecord
   {
      public HostRecord(ServiceHost host,string address)
      {
         Host = host;
         Address = new EndpointAddress(address);
      }
      public readonly ServiceHost Host;
      public readonly EndpointAddress Address;
   }
   static readonly Uri BaseAddress = new Uri("net.pipe://localhost/" + 
                                             Guid.NewGuid().ToString());
   static readonly Binding Binding;
   static Dictionary<Type,HostRecord> m_Hosts = new Dictionary<Type,HostRecord>();

   static InProcFactory()
   {
      NetNamedPipeBinding binding = new NetNamedPipeContextBinding();
      binding.TransactionFlow = true;
      Binding = binding;
      AppDomain.CurrentDomain.ProcessExit += delegate
                                             {
                         foreach(HostRecord hostRecord in m_Hosts.Values)
                                                {
                                                 hostRecord.Host.Close();
                                                }
                                             };
   }


public static I CreateInstance<S,I>() where I : class
                                         where S : I
   {
      HostRecord hostRecord = GetHostRecord<S,I>();
      return ChannelFactory<I>.CreateChannel(Binding,hostRecord.Address);
   }
   static HostRecord GetHostRecord<S,I>() where I : class
                                          where S : I
   {
      HostRecord hostRecord;
      if(m_Hosts.ContainsKey(typeof(S)))
      {
         hostRecord = m_Hosts[typeof(S)];
      }
      else
      {
         ServiceHost host = new ServiceHost(typeof(S),BaseAddress);
         string address = BaseAddress.ToString() + Guid.NewGuid().ToString();
         hostRecord = new HostRecord(host,address);
         m_Hosts.Add(typeof(S),hostRecord);
         host.AddServiceEndpoint(typeof(I),Binding,address);
         host.Open();
      }
      return hostRecord;
   }
   public static void CloseProxy<I>(I instance) where I : class
   {
      ICommunicationObject proxy = instance as ICommunicationObject;
      Debug.Assert(proxy != null);
      proxy.Close();
   }
}

InProcFactory internamente administra un diccionario que asigna los tipos de servicio a una instancia de host determinado. Cuando se llama a CreateInstance para crear una instancia de un tipo determinado, buscará en el diccionario, utilizando un método auxiliar llamado GetHostRecord. Si el diccionario no contiene el tipo de servicio, este método auxiliar que crea una instancia de host para él y agrega un extremo a ese host, con un nuevo GUID como nombre de canalización única. CreateInstance a continuación, toma la dirección del extremo del registro del host y utiliza ChannelFactory <t> para crear el proxy.

En su constructor estático, que se llama al primer uso de la clase, InProcFactory se suscribe al evento proceso Salir para cerrar todos los hosts cuando se cierra el proceso. Por último, para ayudar a los clientes de cerrar el servidor proxy, InProcFactory proporciona el método CloseProxy, que consulta el servidor proxy para ICommunicationObject y lo cierra. Para saber cómo puede sacar partido de memoria transaccional, vea la barra lateral perspectiva " Qué es la memoria transaccional?. "

¿Qué es transaccional memoria?

Es posible que haya oído hablar de memoria transaccional, la nueva tecnología para administrar datos compartidos que muchos reclamar resolverán todos los problemas que encontrar al crear código simultáneo. Es posible que haya oído también hablar que memoria transaccional promete más que se pueden entregar y nada más que un toy de investigación. La verdad se encuentra en algún lugar entre estos dos extremos.

Memoria transaccional permite evitar la administración de bloqueos individuales. En su lugar, puede estructurar el programa en bloques secuenciales bien definidos, las unidades de trabajo o las transacciones, como se llaman en el mundo de la base de datos. A continuación, puede dejar que el sistema en tiempo de ejecución subyacente, compilador, hardware o una combinación proporcionan las garantías de aislamiento y la coherencia deseadas.

Normalmente, el sistema de memoria transaccional subyacente proporciona control de concurrencia optimista de una base en su lugar un grained. En lugar de bloqueo siempre un recurso, el sistema de memoria transaccional supone que no hay ningún conflicto. También detecta cuándo estos supuestos se incorrecta y, a continuación, deshace los cambios provisionales realizados en la transacción. Dependiendo de la implementación, el sistema de memoria transaccional puede, a continuación, intentar vuelva a ejecutar el bloque de código hasta que puede completar sin contención. De nuevo, el sistema es capaz de detectar y administrar la contención sin necesidad de especificar o código de creatividad interrupción de y reintentar mecanismos. Cuando tenga control optimista de concurrencia específicos, la contención de administración y la no es necesario especificar y administrar bloqueos específicos, es posible a pensar en resolver el problema de forma serie al utilizar componentes que aprovechan de concurrencia.

Memoria transaccional promete ofrecer composición, una operación que no se pueden llevar a cabo fácilmente los mecanismos de bloqueo existentes. Para redactar varias operaciones o varios objetos juntos, normalmente deberá aumentar la granularidad del bloqueo, normalmente, ajustando estas operaciones o los objetos juntos en un bloqueo. Memoria transaccional administra automáticamente específicos bloqueo en nombre de su código además de proporcionar la prevención de interbloqueo, que se proporcione composición sin perjudicando escalabilidad ni introducir los interbloqueos.

No existen hoy en día implementaciones comerciales a gran escala de la memoria transaccional. Soluciones de software de prueba con bibliotecas, extensiones de lenguaje o las directivas del compilador han publicado en academia y a Internet. Existe hardware que puede proporcionar memoria limitada transaccional en algunos entornos de gama alta, muy simultáneas, pero el software que aprovecha este hardware oculta su uso explícito. La comunidad de investigación es muy entusiasmada memoria transaccional, y debe espera ver algunas de esta investigación lo que en productos sea más accesibles a través de la década siguiente.

La columna adjunta describe la creación de administradores de recursos volátil que puede utilizar con tecnologías de transacción actual para proporcionar atomicidad, las características de ejecución de todo o nada, y la facilidad de uso posterior, calidad y otras ventajas proporciona que programación transaccional. Memoria transaccional proporciona una funcionalidad similar, pero para cualquier tipo de datos arbitrarios, usa bastante ligera en tiempo de ejecución software o hardware primitivas y se centra en proporcionar escalabilidad, el aislamiento y la composición, así como atomicidad sin que sea necesario para crear sus propios recursos de administrador. Cuando hay memoria transaccional ampliamente disponible, los programadores se beneficiarse de no sólo las ventajas de administradores de recursos volátil como un modelo de programación más simple pero también se da cuenta de la escalabilidad mejoras incluyen acerca de un administrador de memoria transaccional.

Dana — Groff, director de programas, Microsoft paralelo equipos equipo de plataforma

Envíe sus preguntas y comentarios a mmnet30@Microsoft.com.

Juval Lowy es arquitecto de software con proporcionar WCF formación y consultoría de arquitectura de IDesign. Su libro reciente es Programming WCF Services, 2nd Edition (o ' Reilly, 2008). También es director regional de Microsoft para la Silicon Valley. Póngase en contacto con Juval en www.IDesign. NET.