I'm using NHibernate 3.1.0 with the MySql Connector 6.3.5. As a general rule my repository methods are wrapped in an NHibernate transaction. However the service or application code calling the repository methods might also require a transaction scope - therefore the mixing of NHibernate transactions with .NET's TransactionScope. A simulated test looks like this:
[Test]
public void CanPerformNestedSave()
{
using (var tx = new TransactionScope())
{
var user = new AdminUser { Email = "user#test.com", Name = "Test User 1", Password = "123" };
using (ISession session = OpenSession())
{
using (var tx = session.BeginTransaction())
{
entity.ModifiedAt = DateTime.Now;
session.SaveOrUpdate(entity);
tx.Commit();
return entity;
}
}
tx.Complete();
}
}
The test fails with the following error:
NHibernate.TransactionException : Begin failed with SQL exception
----> System.InvalidOperationException : Nested transactions are not supported.
I've scoured the web to find a solution to this scenario and hopefully the community on StackOverflow can help.
I've blogged about this here.
In the blog post NServiceBus creates the outer TransactionScope for the handlers and the Nhibernate session and transactions are used inside the handler.
Related
I have a grails service that calls stored procedures using Groovy SQL.
I am using dataSource for initializing the connection.
My question is: Do I need to manually close the connection or will it be handled by Groovy or GORM (since I am using def dataSource)?
Here is how my service is structured.
class MyService {
static transactional = Boolean.FALSE
private static final String STATEMENT_ONE_SQL = "{ call sp_One(?) }"
private static final String STATEMENT_TWO_SQL = "{ call sp_Two(?,?) }"
def dataSource
Sql sql
#PostConstruct
def initSql() {
sql = new Sql(dataSource)
}
List<GroovyRowResult> callSpOne(Integer id) {
List<GroovyRowResult> results = sql.rows(STATEMENT_ONE_SQL, [id])
return results
}
List<GroovyRowResult> callSpTwo(Integer id, String name) {
List<GroovyRowResult> results = sql.rows(STATEMENT_TWO_SQL, [id, name])
return results
}
Basing on official docs
Finally, we should clean up:
sql.close()
If we are using a DataSource and we haven't enabled statement caching, then strictly speaking the final close() method isn't required - as all connection handling is performed transparently on our behalf; however, it doesn't hurt to have it there as it will return silently in that case.
If instead of newInstance you use withInstance, then close() will be called automatically for you.
I am using Transactionscope with linqtosql. I have the following code:
public bool Save(int Id, List<Student> students, List<Subject> subjects)
{
var isDataSaved = false;
using (TransactionScope scope = new TransactionScope())
{
try
{
// Save the students
SaveStudents(students);
// Save the subjects
SaveSubjects(subjects);
scope.Complete();
isDataSaved = true;
}
catch (Exception ex)
{
throw ex;
}
return isDataSaved;
}
}
In both the methods I am using the two tables : Students, Subjects present in the same database. The datacontext object dependency is setup using the constructor as mentioned in the below class:
public class StudentsRepository:IStudentsRepository
{
public StudentsRepository()
{
_dataContext = new SchoollDetailsDataContext(connectionString);
}
}
The same datacontext object is used in both the methods SaveStudents and SaveSubjects.
I am getting an exception : MSDTC on server 'servername' is unavailable.
As per my initial analysis this kind of error will occur where a database operation will be in my local database and another will be in a remote database.
Can anyone help me to know is there anything I am missing here?
You need to turn the MSDTC service on.
Start-->Control Panel --> Administrative Tools --> services.
Find the service Distributed Transaction Coordinator and start it.
By default its startup property is set to Manual so its switched off.
In short, what I am trying solve is how to recover from certain database errors in a Grails application using Hibernate and continue on with the transaction skipping over the failed row updates that are part of a batch of changes.
The application uses Grails 2.3.11 but I have also tried with version 1.3.8 with similar failed results.
Basically there is a Grails service class that iterates over a list of imported records and attempts to update associated master records appropriately. In certain situations exceptions might occur during the domain.save(flush:true) call e.g. org.hibernate.exception.DataException thrown due to issues like (Data truncation: Data too long for column ...).
At this point I have tried:
Disabling transactions
Using domainObj.withTransaction() for each individual record
Trying various #Transactional annotations
Calling domain.clearErrors() and domain.discard() after catching the exception
Tried using a nested service with Transactional annotation with noRollbackFor as shown below
A number of other approaches but nothing I've tried has worked
Example code:
#Transactional
class UpdateService {
public updateBatch(Integer batchId) {
...
list.each { record ->
record.value = 123
try {
nestedService.saveDomain()
} catch (e) {
record.clearErrors()
record.discard()
}
}
batch.status = "POSTED"
batch.save()
}
}
#Transactional
class NestedService {
#Transactional(propagation = Propagation.REQUIRED, noRollbackFor = RuntimeException.class)
public void saveDomain(domainObj) throws RuntimeException {
if (domainObj.validate() && domainObj.save(flush:true) {
log.info "domain $domain was saved"
}
}
}
Once an error occurs I can't seem to clear out the hibernate session. On each subsequent record being updated I receive the error:
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction
where it indicates the original failed domain id.
Revision:
Vahid, Thanks for the suggestions. I have tried that. I realized one issue is that I am passing objects across transactional boundaries. So I experimented with the NestedService class do something along the lines of:
#Transactional(propagation = Propagation.REQUIRE_NEW)
public void saveDomain(domainObj) {
def newObj = new Domain.get(domainObj.id)
newObj.properties = domainObj.properties
if (newObj.validate() && newObj.save(force:true) ) { ... }
I expected that to work but the original domainObj still fails even though I'm not calling the save on it. Very strange...
A simple approach would be to loop and then use validate(). If it does fail, then just store the id of the failed entity and proceed.
if(!domainObject.validate()){
// store Id for trying it again later ?
}else{
// Save
}
When using Castle.Facilities.AutoTx facility with [Transaction(TransactionScopeOption.RequiresNew)] attribute, the expected new System.Transactions.CommittableTransaction is not created.
You can easily test it with the following unit test
using System.Transactions;
using Castle.Facilities.AutoTx.Testing;
using Castle.MicroKernel.Registration;
using Castle.Transactions;
using Castle.Windsor;
using NUnit.Framework;
namespace Castle.Facilities.AutoTx.Tests
{
public class TransService
{
private readonly NewTransService _s2;
public TransService(NewTransService s2)
{
_s2 = s2;
}
[Transaction]
public virtual string DoInTrans()
{
var currentTransaction = System.Transactions.Transaction.Current;
Assert.That(currentTransaction != null, "The current transaction mustn't be null.");
string transId = currentTransaction.TransactionInformation.LocalIdentifier;
_s2.DoInNewTrans(transId);
return transId;
}
}
public class NewTransService
{
[Transaction(TransactionScopeOption.RequiresNew)]
public virtual string DoInNewTrans(string parentTransId)
{
var currentTransaction = System.Transactions.Transaction.Current;
Assert.That(currentTransaction != null, "The current transaction mustn't be null.");
string transId = currentTransaction.TransactionInformation.LocalIdentifier;
Assert.AreNotEqual(parentTransId, transId, "Ambient transaction must differ from parent");
return transId;
}
}
public class SingleThread_NewAmbient
{
private WindsorContainer _Container;
[SetUp]
public void SetUp()
{
_Container = new WindsorContainer();
_Container.AddFacility<AutoTxFacility>();
_Container.Register(Component.For<TransService>());
_Container.Register(Component.For<NewTransService>());
}
[TearDown]
public void TearDown()
{
_Container.Dispose();
}
[Test]
public void Automatically_Starts_New_CommitableTransaction()
{
using (var scope = new ResolveScope<TransService>(_Container))
scope.Service.DoInTrans();
}
}
}
Am I misunderstanding the purpose of [Transaction(TransactionScopeOption.RequiresNew)] or is it a bug?
I have been digging into the Castle.Transactions source code and I was able to fix the behavior by changing following piece of code in Castle.Transactions.TransactionManager.ITransactionManager.CreateTransaction(ITransactionOptions transactionOptions):
if (activity.Count == 0)
tx = new Transaction(new CommittableTransaction(new TransactionOptions
...
to
if (activity.Count == 0 || transactionOptions.Mode == TransactionScopeOption.RequiresNew)
tx = new Transaction(new CommittableTransaction(new TransactionOptions
...
Can someone from Castle experts / owners check this?
Author here,
I think your code is great and would merge a PR with it if it doesn't break other tests. =)
The reason that RequiresNew isn't well supported is because it's in 99% of the cases an anti-pattern. You're after encapsulating your unit of work in a transaction; and your unit of work should correspond 1-1 with the business operation that should be consistent.
Now, if you have a transaction going on the current thread, like you'd have in your case if you need to use 'RequiresNew', then you're either after reading dirty data or spawning an unrelated transaction (from a business-operation perspective). Hence, you should be doing it in another thread.
Since transactions are 'ambient' and not explicit in the control flow in a programming language like C#, you're left with your 'call context slots' to save the transaction reference in; but from your code's point of view these don't exist; what you have are partial functions that only work in a transactional context. If you spawn a second transaction; how are you going to coordinate that with the current transaction? It's hard; and likely to lead to problems.
An aside
In other transactional systems, like geteventstore.com you get an explicit transaction identifier -- you have one in System.Transactions, too, but it's not explicit in the API/ABI/ADO.Net to the database, hence you can't use it the same way. With an explicit transaction identifier you can solve failures when it becomes 'in doubt', i.e. the 'generals problem'; you can't with System.Transactions. Instead you must mount the transaction MMC on the DTC in question and manually roll it back or forward.
I am trying learn how to best use the Reactive Extensions library and have set up simple test WPF application to view a logging database table. In a ViewModel class I am populating an ObservableCollection with the first 100 log entries from a Linq to Sql DataContext and I'm trying to use Rx to keep the UI responsive.
The following snippet works unless the database is unavailable at which point the app throws an exception and crashes. Where would be the best place to handle database connection exceptions and why are they not handled by the OnError method of the Observer?
ObservableCollection<LogEntry> _logEntries = new ObservableCollection<LogEntry>();
DataContext dataContext = new DataContext( "connection string" );
(from e in dataContext.LogEntries
select e).Take( 100 ).ToObservable()
.SubscribeOn( Scheduler.ThreadPool )
.ObserveOnDispatcher()
.Subscribe( _logEntries.Add, ex => System.Diagnostics.Debug.WriteLine( ex.ToString() ) );
Try this instead of ToObservable:
public static IObservable<T> SafeToObservable(this IEnumerable<T> This)
{
return Observable.Create(subj => {
try {
foreach(var v in This) {
subj.OnNext(v);
}
subj.OnCompleted();
} catch (Exception ex) {
subj.OnError(ex);
}
return Disposable.Empty;
});
}
In general though, this isn't a great use of Rx since the data source isn't very easy to Rx'ify - in fact, the code will execute most of the work on the UI thread, send it out to random worker threads, then send it back (i.e. completely wasted work). Task + Dispatcher.BeginInvoke might suit you better here.