I'm trying to get my head around WCF transactions but struggling with one concept. What is the difference in behaviour of
TransactionFlow(TransactionFlowOption.Mandatory)
and
OperationBehavior(TransactionScopeRequired = true)
As far as I can tell from the documentation they are kind of two ways of achieving the same thing, but I cannot really figure it out.
Cheers,
Stephen
When a transaction scope is required for a method, WCF will create a transaction to execute the method's logic. Transaction flows allow you to chain transactions, so that multiple service methods that require transactions can all be joined into a single transaction instead of separate transactions.
Consider the following simple setup:
[OperationBehavior(TransactionScopeRequired = true)]
public void ServiceMethod1()
{
// Executes ServiceMethod2 and ServiceMethod3
}
[OperationBehavior(TransactionScopeRequired = true)]
[TransactionFlow(TransactionFlowOption.Mandatory)]
public void ServiceMethod2()
[OperationBehavior(TransactionScopeRequired = true)]
[TransactionFlow(TransactionFlowOption.Mandatory)]
public void ServiceMethod3()
ServiceMethod1 requires a transaction, so WCF spins up a transaction and then calls ServiceMethod2 and ServiceMethod3. Although these services also require a transaction, they are flowed. So instead of spinning up new transactions, they adopt the same transaction that was created by ServiceMethod1. If anything fails along the way or the transaction isn't committed, the entire chain gets rolled back. If the transactions were not flowed, each method would spin up a transaction that acted on it's own and had no effect on other methods being called.
Related
My ISession object's FlushMode is FlushMode.Commit.
I use the unit of work and repository pattern as defined here:
http://nhforge.org/wikis/patternsandpractices/nhibernate-and-the-unit-of-work-pattern.aspx
I recall seeing some examples where some people call a Get() immediately followed by a Flush or a transaction commit. We're they just off their rocker, or is there a reason to do this?
From my test:
[TestMethod]
public void TestMethod1()
{
Employee e;
IRepository<Employee> empRepo;
using(UnitOfWork.Start(Enums.Databases.MyDatabase))
{
empRepo = new Repository<Employee>();
e = empRepo.GetByID(21);
}
Debug.WriteLine(e.UserName);
}
My GetByID repository function just calls Session.Get(id) and I can view the username in the output window (after the session is killed)... so whats the point of any sort of Flush or transaction commit after a Get() ? I would understand if there was a save in there somewhere.
NHibernate assumes that all database operations are done within transactions, so people use them explicitly instead of having NHibernate the RDBMS use them implicitly.
Ayende explains this in more detail in his post NH Prof Alerts: Use of implicit transactions is discouraged.
Edit: Learned something new today. It's not NHibernate using implicit transactions but the DB.
I have a system which after getting a message - enqueues it (write to a table), and another process polls the DB and dequeues it for processing. In my automatic tests I've merged the operations in the same process, but cannot (conceptually) merge the NH sessions from the two operations.
Naturally - problems arise.
I've read everything I could about getting the SQLite-InMemory-NHibernate combination to work in the testing world, but I've now ran into RANDOMLY failing tests, due to "no such table" errors. To make it clear - "random" means that the same test with the same exact configuration and code will sometimes fail.
I have the following SQLite configuration:
return SQLiteConfiguration
.Standard
.ConnectionString(x => x.Is("Data Source=:memory:; Version=3; New=True; Pooling=True; Max Pool Size=1;"))
.Raw(NHibernate.Cfg.Environment.ReleaseConnections, "on_close");
At the beginning of my test (every test) I fetch the "static" session provider, and kindly ask it to flush the existing DB clean, and recreate the schema:
public void PurgeDatabaseOrCreateNew()
{
using (var session = GetNewSession())
using (var tx = session.BeginTransaction())
{
PurgeDatabaseOrCreateNew(session);
tx.Commit();
}
}
private void PurgeDatabaseOrCreateNew(ISession session)
{
//http://ayende.com/Blog/archive/2009/04/28/nhibernate-unit-testing.aspx
new SchemaExport(_Configuration)
.Execute(false, true, false, session.Connection, null);
}
So yes, it's on a different session, but the connection is pooled on SQLite, so the next session I create will see the generated schema. Yet, while most of the times it works - sometimes the later "enqueue" operation will fail because it cannot see a table for my incoming messages.
Also - that seems to happen at max one or twice per test suite run; not all the tests are failing, just the first one (and sometimes another one. Not quite sure if it's the second or not).
The worst part is the randomness, naturally. I've told myself I've fixed this several times now, just because it simply "stopped failing". At random.
This happens on FW4.0, System.Data.SQLite x86 version, Win7 64b and 2008R2 (three differen machine in total), NH2.1.2, configured with FNH, on TestDriven.NET 32b precesses and NUnit console 32b processes.
Help?
Hi I'm pretty sure I have the exact same problem as you. I open and close multiple sessions per integration test. After digging through the SQLite connection pooling and some experimenting of my own, I've come to the following conclusion:
The SQLite pooling code caches the connection using WeakReferences, which isn't the best option for caching, since the reference to the connection(s) will be cleared when there is no normal (strong) reference to the connection and the GC runs. Since you can't predict when the GC runs, this explains the "randomness". Try and add a GC.Collect(); between closing one and opening another session, your test will always fail.
My solution was to cache the connection myself between opening sessions, like this:
public class BaseIntegrationTest
{
private static ISessionFactory _sessionFactory;
private static Configuration _configuration;
private static SchemaExport _schemaExport;
// I cache the whole session because I don't want it and the
// underlying connection to get closed.
// The "Connection" property of the ISession is what we actually want.
// Using the NHibernate SQLite Driver to get the connection would probably
// work too.
private static ISession _keepConnectionAlive;
static BaseIntegrationTest()
{
_configuration = new Configuration();
_configuration.Configure();
_configuration.AddAssembly(typeof(Product).Assembly);
_sessionFactory = _configuration.BuildSessionFactory();
_schemaExport = new SchemaExport(_configuration);
_keepConnectionAlive = _sessionFactory.OpenSession();
}
[SetUp]
protected void RecreateDB()
{
_schemaExport.Execute(false, true, false, _keepConnectionAlive.Connection, null);
}
protected ISession OpenSession()
{
return _sessionFactory.OpenSession(_keepConnectionAlive.Connection);
}
}
Each of my integrationtests inherits from this class, and calls OpenSession() to get a session. RecreateDB is called by NUnit before each test because of the [SetUp] attribute.
I hope this helps you or anyone else who gets this error.
Only thing that comes into mind that you are randomly leaving session open after the test. You must make sure any existing ISession is closed before you open another one. If you are not using the using() statement or calling Dispose() manually the session might still be alive somewhere causing those random exceptions.
I have a suite of integration tests that run inside transactions.
Sometimes it seems that NHibernate transactions are not being correctly rolled back. I can't work out what causes this.
Here is a slightly simplified overview of the base class that these integration test fixtures run with:
public class IntegrationTestFixture
{
private TransactionScope _transactionScope;
private ConnectionScope _connectionScope;
[TestFixtureSetUp]
public virtual void TestFixtureSetUp()
{
var session = NHibernateSessionManager.SessionFactory.OpenSession();
CallSessionContext.Bind(session);
_connectionScope = new ConnectionScope();
_transactionScope = new TransactionScope();
}
[TestFixtureTearDown]
public virtual void TestFixtureTearDown()
{
_transactionScope.Dispose();
_connectionScope.Dispose();
var session = CurrentSessionContext.Unbind(SessionFactory);
session.Close();
session.Dispose();
}
}
A call to the TransactionScope's commit method is never made, therefore how is it possible that data still ends up in the database?
Update I never really got my head around the way NHibernate treats transactions but I found that calling Session.Flush() within a transaction would sometimes result in the data remaining in the database, even if the transaction is then rolled back. I am not sure why you can't call Flush, but then roll back. This is a pity because during integration testing you want to be able to hit the database and flush is the only way to do this sometimes.
I had an issue with using the Identity generator that sounds similar. In a nutshell, when saving an object using an identity generator, it has to write to the database in order to get the identity value and can do this outside a transaction.
Ayende has a blog post about this.
I have a WCF service which is used to add tenders to the database, which is MS SQL Server 2005. WCF uses LINQ-to-SQL.
Each tender can have a lot of documents and a lot of items. The customers can add one object per service call. That is, the do something like this:
TendersServiceClient service = new TenderServiceClient();
service.BeginTransaction();
// Adding a new tender
service.AddTender(TenderDTO tenderInfo);
// Adding tender's documents
foreach (DocumentDTO documentInfo in documents)
service.AddTenderDocument(tenderInfo.TenderID, documentInfo);
// Adding tender's items
foreach (ItemDTO itemInfo in items)
service.AddTenderItem(tenderInfo.TenderID, itemInfo);
service.CommitTransaction();
Notice the BeginTransaction() and CommitTransaction(). That is, all the above procedure must either succeed completely or be rolled back completely. For example, if one of the items couldn't be inserted, then the whole tender shouldn't exist...
So the question is how do I implement this kind of transaction. The problem is that WCF is stateless, of course. So new DataContext is created for each service call. If I use a static DataContext instead, then I'll be able to use its built-in transactions capabilities, but then how can I handle other customers who can try to add another tender in the same time (they must be, of course, outside this transaction)?
So please help me with some kind of design pattern to achieve this. I am free to change the code both of the service and of the client, so feel free with your suggestions =)
Do you control the interface of the service?
If so, surely the elegant solution is for the service to accept an aggregate Tender object in a single method rather than having the chatty methods that you have now. The Tender would then have Items and Documents as subcollections, and the data access code could handle all the updates in single transaction much more easily.
Unless I am misunderstanding, it seems very similar to an Order/OrderDetails scenario, where the same logic applies.
First off, you would have to use transactional service calls here - and since you have a "initializing" call, a bunch of intermediary calls, and then possibly one to end all the calls, I would recommend you have a look at the "IsInitiating" and "IsTerminating" attributes on the OperationContract for the methods - this will allow you to specify one method to start your session, and one that will finish it.
Next, make sure to configure your service as a transactional service by putting the "TransactionFlow" attribute on either the service or all operations - whichever you prefer.
In your client code, you'll have to use System.Transactions to create a TransactionScope, which will wrap your service calls. This is a lightweight or a fully two-phased distributed transaction coordinator - depending on what your calls do in detail.
Something along those lines:
1) Mark your binding as transactional:
<bindings>
<wsHttpBinding>
<binding name="TransactionalWsHttp" transactionFlow="true" />
</wsHttpBinding>
</bindings>
2) Service contract:
[ServiceContract]
public interface ITenderService
{
// method to start your submission process
[OperationContract(IsInitiating=true, IsTerminating=false)]
[TransactionFlow(TransactionFlowOption.Mandatory]
public void StartTenderProcess();
// all your other methods "in between"
[OperationContract(IsInitiating=false, IsTerminating=false)]
[TransactionFlow(TransactionFlowOption.Mandatory]
public void AddTender()
[OperationContract(IsInitiating=false, IsTerminating=false)]
[TransactionFlow(TransactionFlowOption.Mandatory]
public void AddTenderDocument()
[OperationContract(IsInitiating=false, IsTerminating=false)]
[TransactionFlow(TransactionFlowOption.Mandatory]
public void AddTenderItem()
...
// method to end your submission process
[OperationContract(IsInitiating=false, IsTerminating=true)]
[TransactionFlow(TransactionFlowOption.Mandatory]
public void FinishTenderProcess();
}
3) In your client code:
using (TransactionScope ts = new TransactionScope())
{
serviceClient.StartTenderProcess();
.....
serviceClient.FinishTenderProcess();
ts.Complete(); // Transaction Commit
}
Does that help you for the time being to get you started ??
Marc
I'm trying to set up a integration test class that wraps each test in a transaction. This way I can rollback the transaction after each test instead of resetting the DB before each test.
I also want to be able to use transactions in the integration tests themselves.
I am using NHibernate and the Rhino Commons UnitOfWork for the the project. I am using MSTest for the tests.
I want something like this:
[TestInitialize]
public void TestInit() {
// create outer transaction scope
UnitOfWork.Start();
UnitOfWork.Current.BeginTransaction();
}
[TestCleanup]
public void TestCleanup() {
// rollback outer transaction
UnitOfWork.Current.Dispose();
}
[TestMethod]
public void IntegrationTest() {
using (UnitOfWork.Start(UnitOfWorkNestingOptions.CreateNewOrNestUnitOfWork)) {
UnitOfWork.Current.BeginTransaction();
// integration test code
UnitOfWork.Current.TransactionalFlush();
// possibly more transactions
}
}
This is the first time I have used NHibernate, Rhino Commons, and MSTest. I am not clear on the behavior of sessions with nested Rhino Commons UnitOfWorks. What I have here does not rollback the changes from the integration test.
I tried using TransactionScope from System.Transactions, but get the following error when the UnitOfWorks end:
System.InvalidOperationException: Disconnect cannot be called while a transaction is in progress..
So here are my questions:
Is there a way to get this behavior with UnitOfWork in Rhino Commons? If not, should I just reset the database before each test or is there another way to nest transactions that plays nicely with the UnitOfWork?
Thank you.
I believe UnitOfWork.Start().BeginTransaction() returns a RhinoTransaction. So to make it more clear you can try to rewrite the code as this:
using(IUnitOfWork uow = UnitOfWork.Start(UnitOfWorkNestingOptions.CreateNewOrNestUnitOfWork))
{
RhinoTransaction tx = uow.BeginTransaction();
.
.
.
.
tx.Rollback();
}
Be warned though, I have not tried the code above, let me know if it works.