Estoy usando Entity Framework Core con PostgreSQL. Quiero anular SaveChanges en mi DbContext para confirmar los cambios de EF y enviar un mensaje con Rebus dentro de la misma transacción de la base de datos. El plan es usar también PostgreSQL para el transporte, etc., pero tengo problemas simplemente alistando a Rebus en un ámbito de transacción usando el código a continuación.
public override int SaveChanges() { using (var tx = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) { tx.EnlistRebus(); var result = base.SaveChanges(); //TODO: _bus.Send("something happened"); tx.Complete(); return result; } } Ejecutar los resultados anteriores en PostgresException: 55000: prepared transactions are disabled , lo cual es cierto, ya que mi configuración de PostgreSQL no lo ha habilitado. Mi pregunta es por qué Rebus necesita causar un compromiso de 2 fases aquí. Tal vez me estoy perdiendo algo, aunque espero que no se necesite un 2PC, dado que tanto Entity Framework como Rebus usarán la misma instancia de base de datos relacional.
La llamada a EnlistRebus está en el paquete Rebus.TransactionScopes y no sabe qué implementación de transporte está en uso y podría ser segura.
¿Hay otra forma de hacer tanto la operación de la base de datos como el envío de Rebus transaccionalmente sin compromiso de 2 fases? Por supuesto, puedo usar una tabla separada para almacenar mi mensaje pendiente de SaveChanges y hacer que un trabajador separado extraiga de esa tabla y envíe el mensaje usando Rebus. Sospecho que este enfoque es el más sólido y directo.
Estoy usando Rebus 6.3.0, Rebus.PostgreAql 7.1.0, Rebus.TransactionScopes 6.0.0, Npgsql 4.1.3.1, Npgsql.EntityframeworkCore.PostgreSQL 3.1.4 y EF Core 3.1.4.
Rebus.PostgreSQL en realidad se inscribirá en la transacción ambiental por sí mismo sin Rebus.TransactionScopes.
Mirando el repositorio Rebus.PostgreSQL, vi que había un PR que parece resolver mi problema. Cita del PR en https://github.com/rebus-org/Rebus.PostgreSql/pull/14 :
Primero intenté hacer que https://github.com/rebus-org/Rebus.TransactionScopes funcionara, pero eso no parece hacer nada usando el transporte postgresql.
Para verificar que efectivamente publica el mensaje en la transacción ambiental, hice la siguiente modificación:
public override int SaveChanges() { using (var tx = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }, TransactionScopeAsyncFlowOption.Enabled)) { var result = base.SaveChanges(); _bus.Send(new MyMessage { CurrentDateTime = DateTime.Now}).Wait(); if (ShouldCrash) { throw new ArgumentException(); } tx.Complete(); return result; } } Si ShouldCrash se establece en true , no se publica ningún mensaje y no se realizan cambios en las entidades. Si ShouldCrash se establece en false , se realizan tanto la publicación de mensajes como el cambio de entidad.
Creo que esto funciona debido a lo siguiente de los documentos de Npgsql:
Tenga en cuenta que si abre y cierra conexiones a la misma base de datos dentro de una transacción ambiental, sin tener dos conexiones abiertas al mismo tiempo, Npgsql reutilizará internamente la misma conexión, evitando la escalada a una transacción distribuida completa.