Question : nested transactionscopes

hi experts

see the code below.
this code gets executed whenever I save a row or a table to the database.
A ((ISaveable)Entity).Save(); method can also call another Save() method for child tables or child rows.
so the InternalUpdateEntity() method gets called multiple times and multiple transactionscopes are used.

Now I have to questions/Problems.
1. will they all use the same transactionscope or always a new one and does this even make sense. Should I better just create one transactionscope?

2. when I get a concurrency error in a nested transactionscope. my application will throw a transactionaborted exception, despite the fact that I actually catch exactly this exception?
What could cause that?

thanks in advance for your help
I'm using VS 2008, C#, .NET 3.5, Win Forms

1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
private void InternalUpdateEntity(DataRow[] Rows, ValidationErrorCollection vaeCollection, UpdateAction UpdateAction, Boolean DoCommit)
        {
            object Entity = null;

            if(Rows.Length == 1)
                Entity = Rows[0];
            else if(Rows.Length > 1 && Rows[0].Table != null)
                Entity = Rows[0].Table;
            else
                return;

            if(vaeCollection.IsCollectionValid && Entity != null)
            {
                Boolean ConcurrencyError = false;
                
                using(TransactionScope Ts = ( (this.ParentTransaction == null) ? new TransactionScope() : new TransactionScope(this.ParentTransaction)))
                {
                    try
                    {
                        if(this.ParentTransaction == null)
                            this.ParentTransaction = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);

                        if(UpdateAction == UpdateAction.Update)
                        {
                            if(Entity is ISaveable || Entity is ISaveMultipleRows)
                            {
                                if(Entity is ISaveMultipleRows && Rows.Length > 1)
                                    ((ISaveMultipleRows)Entity).Save(Rows);
                                else
                                    ((ISaveable)Entity).Save();
                            }
                        }
                        else if(UpdateAction == UpdateAction.Delete)
                        {
                            if(Entity is IDeleteable || Entity is IDeleteMultipleRows)
                            {
                                if(Entity is IDeleteMultipleRows && Rows.Length > 1)
                                {
                                    ((IDeleteMultipleRows)Entity).DeleteObject(Rows);

                                    if(DoCommit)
                                        ((IDeleteMultipleRows)Entity).CommitDeletedObject(Rows);
                                }
                                else
                                {
                                    ((IDeleteable)Entity).DeleteObject();

                                    if(DoCommit)
                                        ((IDeleteable)Entity).CommitDeletedObject();
                                }
                            }
                        }

                        Ts.Complete();
                    }
                    catch(TransactionAbortedException ex)
                    {
                        if(this.DatabaseUpdateError != null)
                        {
                            string Message = "Fehler\r\n";

                            if(ex.InnerException != null)
                                Message += ex.InnerException.Message + "\r\nStacktrace:" + ex.InnerException.StackTrace;
                            else
                                Message += ex.Message + "\r\nStacktrace:" + ex.StackTrace;

                            this.DatabaseUpdateError(new ExceptionEventArgs(Message));
                        }
                        else if(ex.InnerException != null)
                            throw ex.InnerException;
                        else
                            throw ex;
                    }
                    catch(DBConcurrencyException dex)
                    {
                        if(ConcurrencyErrorOccured != null)
                            this.ConcurrencyErrorOccured(dex.Row, new EventArgs());
                        //ConcurrencyError = true;
                        Ts.Dispose();
                        return;
                    }
                }
        }
}
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
private void InternalUpdateEntity(DataRow[] Rows, ValidationErrorCollection vaeCollection, UpdateAction UpdateAction, Boolean DoCommit)
        {
            object Entity = null;

            if(Rows.Length == 1)
                Entity = Rows[0];
            else if(Rows.Length > 1 && Rows[0].Table != null)
                Entity = Rows[0].Table;
            else
                return;

            if(vaeCollection.IsCollectionValid && Entity != null)
            {
                Boolean ConcurrencyError = false;
                
                using(TransactionScope Ts = (new TransactionScope())
                {
                    try
                    {
                       
                        if(UpdateAction == UpdateAction.Update)
                        {
                            if(Entity is ISaveable || Entity is ISaveMultipleRows)
                            {
                                if(Entity is ISaveMultipleRows && Rows.Length > 1)
                                    ((ISaveMultipleRows)Entity).Save(Rows);
                                else
                                    ((ISaveable)Entity).Save();
                            }
                        }
                        else if(UpdateAction == UpdateAction.Delete)
                        {
                            if(Entity is IDeleteable || Entity is IDeleteMultipleRows)
                            {
                                if(Entity is IDeleteMultipleRows && Rows.Length > 1)
                                {
                                    ((IDeleteMultipleRows)Entity).DeleteObject(Rows);

                                    if(DoCommit)
                                        ((IDeleteMultipleRows)Entity).CommitDeletedObject(Rows);
                                }
                                else
                                {
                                    ((IDeleteable)Entity).DeleteObject();

                                    if(DoCommit)
                                        ((IDeleteable)Entity).CommitDeletedObject();
                                }
                            }
                        }

                        Ts.Complete();
                    }
                    catch(TransactionAbortedException ex)
                    {
                        if(this.DatabaseUpdateError != null)
                        {
                            string Message = "Fehler\r\n";

                            if(ex.InnerException != null)
                                Message += ex.InnerException.Message + "\r\nStacktrace:" + ex.InnerException.StackTrace;
                            else
                                Message += ex.Message + "\r\nStacktrace:" + ex.StackTrace;

                            this.DatabaseUpdateError(new ExceptionEventArgs(Message));
                        }
                        else if(ex.InnerException != null)
                            throw ex.InnerException;
                        else
                            throw ex;
                    }
                    catch(DBConcurrencyException dex)
                    {
                        if(ConcurrencyErrorOccured != null)
                            this.ConcurrencyErrorOccured(dex.Row, new EventArgs());
                        //ConcurrencyError = true;
                        Ts.Dispose();
                        return;
                    }
                }
        }
}

Answer : nested transactionscopes

check out this nice article:

http://www.15seconds.com/issue/060413.htm
Random Solutions  
 
programming4us programming4us