We have been struggling to get this working: Should the below code work?
The first call
myTestA.Save() should work
myTestB.Save causes a truncation error on the table, and my thoughts are that A should roll back.
This is a SQL 2005 back end and the table and proc are:
Create
TABLE [dbo].[TestTran]([TestTranID] [int]
IDENTITY(1,1) NOT NULL,[Name] [varchar]
(5) COLLATE Latin1_General_CI_AS NOT NULL)
Create
Proc dbo.InsertTestTran @myvalue intas
if
@myvalue = 1 begin Insert [TestTran] (Name) values( 'Test1111111111111111') ENDelse
Insert [TestTran] (Name) values( 'Test1')This is the c# code:
using System;
using
System.Collections.Generic;using
System.Text;using
Csla;using
Csla.Data;using
System.Data.SqlClient;using
System.Data;namespace
ADFTest{
class Program{
private static SqlConnection _Connection; static void Main(string[] args){
try{
string mystring = getDBConnection SqlConnection myConn = new SqlConnection(mystring);myConn.Open();
_Connection = myConn;
TestTransaction();
}
catch{ }
}
[
Transactional(TransactionalTypes.TransactionScope)] static void TestTransaction(){
ClassA myTestA = new ClassA(); ClassB myTestB = new ClassB();myTestA.Save();
myTestB.Save();
}
public static SqlConnection GetDBConnection(){
return _Connection;}
}
class ClassA : BusinessBase<ClassA>
{
public static ClassA NewObject(){
return DataPortal.Create<ClassA>();}
public override object GetIdValue(){
return 1;}
[
Transactional(TransactionalTypes.TransactionScope)] protected override void DataPortal_Insert(){
SqlCommand sqlCmd2 = new SqlCommand("dbo.InsertTestTran", Program.GetDBConnection());sqlCmd2.CommandType =
CommandType.StoredProcedure; SqlParameter myparam2 = new SqlParameter("@myvalue", '0');sqlCmd2.Parameters.Add(myparam2);
sqlCmd2.ExecuteNonQuery();
}
}
class ClassB : BusinessBase<ClassB>{
public static ClassB NewObject(){
return DataPortal.Create<ClassB>();}
public override object GetIdValue(){
return 1;}
[
Transactional(TransactionalTypes.TransactionScope)] protected override void DataPortal_Insert(){
// This will cause a Truncation error in the database: SqlCommand sqlCmd2 = new SqlCommand("dbo.InsertTestTran", Program.GetDBConnection());sqlCmd2.CommandType =
CommandType.StoredProcedure; SqlParameter myparam2 = new SqlParameter("@myvalue", '1');sqlCmd2.Parameters.Add(myparam2);
sqlCmd2.ExecuteNonQuery();
}
}
}
The Transactional attribute is only useful when applied to a DataPortal_XYZ method of a root object. It has absolutely no meaning on any other method.
So you are using it on your static factory method, where it is entirely ignored. And you use it on both DP_XYZ methods - but they are treated as separate data portal calls and so run in separate transactional contexts.
You need to have ONE root object that is invoked through the data portal, and then that object does the insert/update of the other (child) object. Put the Transactional attribute on the DP_XYZ methods of that one root object and you should get the desired result.
The Transactional attribute is designed to make the common case
trivial. That is the case where you have one root object with child objects and
they should all be saved within the context of a single transaction.
If that is not your scenario, you should not use the Transactional
attribute, and instead should create your own TransactionScope object.
Let’s face it, the Transactional attribute really saves
you a whopping 2 lines of code in your root object’s DataPortal_XYZ
method. That’s all! It isn’t worth getting hung up on it, or
worrying about paying a huge cost if you need more control. Just don’t
use Transactional, and do wrap all code in your root object’s
DataPortal_XYZ methods in a using block with a TransactionScope object.
Rocky
Copyright (c) Marimer LLC