I am using nhibernate and the nhibernate profile what keeps throwing this alert.
Use of implicit transactions is discouraged"
I actually wrap everything in a transaction through ninject
public class NhibernateModule : NinjectModule
{
public override void Load()
{
Bind<ISessionFactory>().ToProvider<NhibernateSessionFactoryProvider>().InSingletonScope();
Bind<ISession>().ToMethod(context => context.Kernel.Get<ISessionFactory>().OpenSession()).InRequestScope()
.OnActivation(StartTransaction)
.OnDeactivation(CommitTransaction);
}
public void CommitTransaction(ISession session)
{
if (session.Transaction.IsActive)
{
session.Transaction.Commit();
}
}
public void StartTransaction(ISession session)
{
if (!session.Transaction.IsActive)
{
session.BeginTransaction();
}
}
}
So this should wrap everything in a transaction and it seems to work with anything that is not lazy loading.
If it is lazy loading though I get the error. What am I doing wrong.
This is, in fact, still an implicit transaction, or relatively close to it. The injector is blissfully ignorant of everything that's happened between activation and deactivation and will happily try to commit all your changes even if the state is incorrect or corrupted.
What I see is that you're essentially trying to cheat and just have Ninject automatically start a transaction at the beginning of every request, and commit the transaction at the end of every request, hoping that it will stop NH from complaining. This is extremely bad design for several reasons:
You are forcing a transaction even if the session is not used at all (i.e. opening spurious connections).
There is no exception handling - if an operation fails or is rolled back, the cleanup code simply ignores that and tries to commit anyway.
This will wreak havoc if you ever try to use a TransactionScope, because the scope will be completed before the NH transaction is.
You lose all control over when the transactions actually happen, and give up your ability to (for example) have multiple transactions within a single request.
The NH Profiler is exactly right. This isn't appropriate use of NH transactions. In fact, if you're lazy loading, the transaction might end up being committed while you're still iterating the results - not a good situation to be in.
If you want a useful abstraction over the transactional logic and don't want to have to twiddle with ISession objects then use the Unit Of Work pattern - that's what it's designed for.
Otherwise, please code your transactions correctly, with a using clause around the operations that actually represent transactions. Yes, it's extra work, but you can't cheat your way out of it so easily.
Related
How does one write integration tests in JUnit which use JOOQ as data access layer, and which rollback after test completion?
It seems that Jooq provides only very limited transactional management. It offers a method
DSLContext.transaction(TransactionalRunnable transactional)
This method will rollback a transaction if exception is thrown by the passed lambda. But it is not obvious how to make this work with JUnit integration tests. What I really want is an equivalent of #Transactional interface without using Spring.
#Transactional
class UserApiTest
{
UserApi api;
#Test
public void testUpdateUserLastName() {
User user = getUserByUsername();
user.setLastName("NewLastName");
api.updateUser(user);
assertEquals("NewLastName", user.getLastName());
// database should be unchanged after test completion because of automatic rollback
}
}
class UserApiImpl implements UserApi
{
private final DSLContext db;
#Override
public void updateUser(LegacyUser user) {
UserRecord userRecord = db.newRecord(USER, user);
db.executeUpdate(userRecord);
}
}
Any suggestions would be appreciated.
A better approach than rolling back
First off, I would recommend you do not roll back your transactions in such integration tests, but instead, reset the database to a known state after your test. There are so many ways a rollback can fail, such as:
Calling stored procedures that have autonomous transactions that commit anyway
Integration testing some service that commits the transaction anyway
Rollbacks causing trouble because of the transaction model of the database
If you reset the database to a well known state, you will avoid all of the above problems, which are likely going to cost you much more in test maintenance.
Use something like https://www.testcontainers.org instead. Here's an example showing how to set it up: https://github.com/jOOQ/jOOQ/tree/main/jOOQ-examples/jOOQ-testcontainers-example
Rolling back at the end of a test
You shouldn't use jOOQ for this. You could use Spring's JUnit extensions, and make all your tests #Transactional. This way, whenever you have an assertion error, you will automatically roll back your transaction. Also, your test will automatically be transactional.
This works in simple scenarios, but again, I'm sure you'll run into one of the aforementioned issues.
I'm trying to find a good article/examples of how DDD entities treat errors (and what would be considered exceptional errors and what wouldn't) and how they pass them up to the calling application layer (which usually wraps operations in a transaction that would need to be rolled back).
Currently I'm thinking to consider all errors that would break the transaction of an aggregate (such as validation) to be exceptions. This way I can rollback the transaction in a "catch" block. For example:
SomeApplicationService:
// start transaction here
// ...
try
{
$user = $userRepository->userOfId($id);
$user->doSomething();
$user->doSomethingElse(); // <-- imagine an error thrown here
$userRepository->save($user);
}
catch (CustomFriendlyForUIException $e)
{
// Custom Friendly for UI error
// Rollback transaction and add error/message to UI payload DTO
// ...
}
catch (AnotherCustomException $e)
{
// Not friendly to UI, so use general error message for UI
// Rollback transaction and add error/message to UI payload DTO
// ...
}
catch (Exception $e)
{
// Catch all other exceptions, use general error message for UI
// Rollback transaction and add error/message to UI payload DTO
// ...
}
// end transaction
Is this the correct approach, or am I missing something?
Usually, there are two types of error:
business errors that have a business meaning. For instance, StockFullError, ProductNotAvailableError, etc. These errors are expected because they are explicitly created in the code.
These kind of error can be implemented using exceptions or using the
functional way: explicitly show errors in the return type. For
instance: Either<Error, T>.
Exception make the code simpler. Handling error the functional programming way make the code easier to reason about and more predictable. I would advise the latter.
infrastructure errors are errors linked to the database, network, external services, etc. These error are usually unexpected.
Error happen. And when error happen, you don't want the application to be in an inconsistent state. For instance, you want to avoid to have half of the aggregate stored in the datastore.
Aggregate integrity
The key problem here is to maintain the aggregate integrity. The critical point is when the aggregate has to be written in the datastore. I can see two situations:
The operation to persist the aggregate is atomic
As soon as your datastore can provide atomic operations, there is no worry to have about the data integrity because the result of an atomic operation is either a failure or a success. There is no partial writes.
That means that you can just let your http layer handle exception and return 500 for instance.
The operation to persist the aggregate is not atomic
Sometimes, ensuring atomicity is not possible.
One aggregate in several tables
If you need to store your aggregate in several tables, there are some solutions:
Transactions. Surrounding the code in a transaction could be a solution. However transactions have some disadvantages. If the computation is too long or if this part of the code is called too often, it would slow down the application.
Unit Of Work is an old pattern. The idea is to register operations that you want to do: add, update, delete, etc. Eventually, UOW applies the changes to the database using a transaction. See Fowler's article for more information.
SAGAs: sometimes a big transaction is not possible
When you send an email and save something in the database, you can't include the email in the transaction.
In that case, you can use SAGAs. The idea is that, sometime, you can't have one single big transaction to enforce atomicity. However, it is usually easy to have several small transactions.
The idea of a SAGA is to associate every single transaction to a compensating transaction. For instance given the "transaction": send an email to confirm that the product is bought, the compensating "transaction" could be sent an email to apologise with a coupon.
Every transaction small transaction is run. If one of these fails, compensating transaction are run. Eventually, this enables to get atomicity.
I have an interesting use case where certain exception types mean "This message is no longer valid and should be ignored" but this code doesn't have any awareness of the Bus in order to call Bus.DoNotContinueDispatchingCurrentMessageToHandlers().
I loathe boilerplate code like try/catch blocks that need to be present in every single message handler. So I started implementing a UnitOfWork to handle and swallow the exception, but I can't find a way to tell the framework that "Yes, this code generated an exception, but forget about that and just complete the transaction."
Bus.DoNotContinueDispatchingCurrentMessageToHandlers() does not work. I tried having an ITransport injected and calling AbortHandlingCurrentMessage() but that caused the entire universe to blow up. Even stepping through the source code I seem to be at a loss.
Note that it very well may be that this is a horrible idea, because faking that there is no exception when there is in fact an exceptional case would cause the transaction to commit, causing who knows how many bad unknown side effects. So it would be preferable to have a method that still rolls back the transaction but discards the message. But I would be interested in a potential "Yes I know what I'm doing, commit the transaction regardless of the exception" option as well.
As of NServiceBus version 4.4 you can control this by injecting a behavior into our handler pipeline.
This let's you control which exceptions to mute.
class MyExceptionFilteringBehavior : IBehavior<HandlerInvocationContext>
{
public void Invoke(HandlerInvocationContext context, Action next)
{
try
{
//invoke the handler/rest of the pipeline
next();
}
//catch specific exceptions or
catch (Exception ex)
{
//modify this to your liking
if (ex.Message == "Lets filter on this text")
return;
throw;
}
}
There are several samples of how this works:
http://docs.particular.net/samples/pipeline/
That said I totally agree with Ramon that this trick should only be used if you can't change to design to avoid this.
A dirty solution would be having a unit of work test the exception, put the message id in a shared 'ignore' bag (concurrent dictionary in memory, db, what works for you) , let it fail so that everything is rolled back, in the retry have a generic message handler compare the message ID and let that call Bus.DoNotContinueDispatchingCurrentMessageToHandlers()
If you do not want to work with a unit of work then you could try to use the AppDomain.FirstChanceException.
I wouldn't advice any of these as good solution :-)
Why would you like to 'swallow' unhandled exceptions?
If you want to ignore an exception then you should catch these in the handler and then just return and log this.
What I'm more interested in is what about state? You maybe have already writen to a store. Shouldn't these writes be rolled back? If you swallow an exception the transaction commits.
It seems to me you are running in a kind of 'at least once delivery' environment. THen you need to store some kind of message id somewhere.
Or is it an action initiated by several actors based on a stale state? In that case you need to have first/last write wins construction that just ignores a command based on a stale item.
If you handl an event then swallowing a exception seems not correct. They usually say that you can ignore commands but that you always have to handle events.
Shouldn't this be part of validation? If you validate a command then you can decide to ignore it.
I was wondering if we can some how extend NHiberate to throw an exception if a piece of code attempted to save an object to a database without beginning a transaction? Since beginning a transaction is a requirement for calling Save() to work properly, I can't see a programmer calling Save() without beginning a transaction in the first place.
Solution is not in the exception throwing. It is about keeping us (developers on the project) aware of what and where we are doing
1) Project shared approach. Firstly, explain to the team how the Architecture of application works. All team members should now, that Unit of Work, or Session per Request patterns are in place.
2) The FlushMode. Secondly, explain the concept of the Session object. ISession. Save() or Update() is far away from SQL execute INSERT or UPDATE. What count is the ISession.Flush(). And (based on the first step) we can decide when that Flush() happens.
I would suggest setting the FlushMode to on Commit or None (with explict call). And then, if a team member would like to Execute any write command - one standard place, one common way (used on the project) will guide hin/her.
The call Session.Flush() should/will/must be wrapped in a transaction.
I'd like to ensure, that when I'm persisting any data to the database, using Fluent NHibernate, the operations are executed inside a transaction. Is there any way of checking that a transaction is active via an interceptor? Or any other eventing mechanism?
More specifically, I'm using the System.Transaction.TransactionScope for transaction management, and just want to stop myself from not using it.
If you had one place in your code that built your session, you could start the transaction there and fix the problem at a stroke.
I haven't tried this, but I think you could create a listener implementing IFlushEventListener. Something like:
public void OnFlush(FlushEvent #event)
{
if (!#event.Session.Transaction.IsActive)
{
throw new Exception("Flushing session without an active transaction!");
}
}
It's not clear to me (and Google didn't help) exactly when OnFlush is called. There also may be an implicit transaction that could set IsActive to true.
If you had been using Spring.Net for your transaction handling, you could use an anonymous inner object to ensure that your DAOs/ServiceLayer Objects are always exposed with a TransactionAdvice around their service methods.
See the spring documentation for an example.
To what end? NHProf will give you warnings if you're not executing inside a transaction. Generally you should be developing with this tool open anyway...