Transactions - lobodava/artisan-orm GitHub Wiki
For the cases when it's necessary to lift up a transaction from database to application level, Artisan.Orm provides the RepositoryBase method BeginTransaction
:
public void BeginTransaction(Action<SqlTransaction> action)
public void BeginTransaction(IsolationLevel isolationLevel, Action<SqlTransaction> action)
This method:
- opens the connection, that is created in
RepositoryBase
constructor; - begins
SqlTransaction
and passes it as an argument toAction<SqlTransaction>
parameter; - begins
try catch finaly
block:- in
try
part runsAction<SqlTransaction>
parameter, - in
catch
part rollbacks the transaction, - in
final
part closes the connection and disposes the transaction.
- in
If the repository class of the executing method is inherited from the RepositoryBase
, then GetByCommand
and ExecuteCommand
methods bind the commands to the started transaction.
Example:
public void CheckRuleForUser(Int32 userId)
{
// begin transaction with RepeatableRead isolation level
BeginTransaction(IsolationLevel.RepeatableRead, tran =>
{
// read user by userId
var user = GetByCommand(cmd =>
{
cmd.UseProcedure("dbo.GetUserById");
cmd.AddIntParam("@Id", userId);
return cmd.ReadTo<User>();
});
// change the user in accordance with some rules
if (Array.IndexOf(user.RoleIds, 2) > -1 && Array.IndexOf(user.RoleIds, 3) == -1)
{
var newRoleIds = user.RoleIds.ToList();
newRoleIds.Add(3);
user.RoleIds = newRoleIds.ToArray();
}
// save the user
ExecuteCommand(cmd =>
{
cmd.UseProcedure("dbo.SaveUser");
cmd.AddTableRowParam("@User", user);
cmd.AddTableParam("@RoleIds", user.RoleIds);
});
// commit transaction
tran.Commit();
});
}
If to omit tran.Commit();
at the end, then the transaction will be rolled back - this can be used for testing of methods which change the data.
In order to allow an application transaction, a stored procedure should have a check for the outer transaction.
For example:
create procedure dbo.SaveUser
...
as
begin
declare @StartTranCount int;
begin try
set @StartTranCount = @@trancount;
-- Begin transaction only when there is no outer transaction
if @StartTranCount = 0 begin transaction;
begin -- save Users
...
end;
begin -- save UserRoles
...
end;
-- Commit transaction only when there is no outer transaction
if @StartTranCount = 0 commit transaction;
end try
begin catch
-- Rollback transaction only when there is no outer transaction
if xact_state() <> 0 and @StartTranCount = 0 rollback transaction;
declare @ErrorMessage nvarchar(4000) = dbo.GetErrorMessage();
raiserror (@ErrorMessage, 16, 1);
return;
end catch;
end;
See also: