GOTCHA 75 AutoComplete comes with undesirable side effects


GOTCHA #75 AutoComplete comes with undesirable side effects

The AutoComplete attribute in Enterprise Services provides a very convenient way to communicate the intent to commit or abort a transaction. When a method marked with that attribute is invoked, the method automatically votes to commit the transaction if the method is successful. If the method fails, which is indicated by throwing an exception, the vote is automatically set to abort. This sounds pretty logical, so what's the concern? Let's examine the code in Example 8-19.

Example 8-19. Effect of AutoComplete

C# (ESAutoComplete)

 //MyComp.cs as part of ESLib.dll using System; using System.EnterpriseServices; namespace ESLib {     [Transaction(TransactionOption.Required)]     public class MyComp : ServicedComponent     {         private string theMessage = "UnSet";         [AutoComplete]         public void SetInfo(string msg)         {             theMessage = msg;         }         [AutoComplete]         public string GetInfo()         {             return theMessage;         }     } } //Test.cs as part of ESUser.exe using System; using ESLib; namespace ESUser {     class Test     {         [STAThread]         static void Main(string[] args)         {             MyComp theComp = new MyComp();             theComp.SetInfo("hello");             Console.WriteLine(theComp.GetInfo());         }     } } 

VB.NET (ESAutoComplete)

 'MyComp.vb as part of ESLib.dll Imports System.EnterpriseServices <Transaction(TransactionOption.Required)> _ Public Class MyComp     Inherits ServicedComponent     Private theMessage As String = "UnSet"     <AutoComplete()> _     Public Sub SetInfo(ByVal msg As String)         theMessage = msg     End Sub     <AutoComplete()> _     Public Function GetInfo() As String         Return theMessage     End Function End Class 'Test.vb as part of ESUser.exe Imports ESLib Module Test     Sub Main()         Dim theComp As New MyComp         theComp.SetInfo("hello")         Console.WriteLine(theComp.GetInfo())     End Sub End Module 

In this example, you create an instance of MyComp in the Main() method of the Test class. (The MyComp class stores a field named theMessage and initializes it to "UnSet.") You then call SetInfo() to set theMessage to "hello," then call GetInfo() to read it back. When the above code is executed you get the output shown in Figure 8-22.

Figure 8-22. Output from Example 8-19


What went wrong? Why didn't you get the expected result of "hello?"

The client is not dealing directly with the MyComp object, but with an invisible proxy. When the SetInfo() method is called, the method executes on the actual component. At the end of the method, due to the AutoComplete attribute, the transaction is committed by an internal call to SetComplete().

However, this call has a side effect. It not only sets the vote, it also sets the ContextUtil.DeactivateOnReturn property to TRue. As a result, the object is deactivated upon the return from the SetInfo() method. In other words, as soon as the method completes, the object is destroyed (kind of like working for the Mafia). When the GetInfo() method is invoked again using the proxy reference, another instance of the object is created automatically. theMessage is initialized to "UnSet" in this newer object, and that's what GetInfo() returns to you.

Let's consider the code changes in Example 8-20.

Example 8-20. Safely communicating intent to commit

C# (ESAutoComplete)

 //MyComp.cs as part of ESLib.dll using System; using System.EnterpriseServices; namespace ESLib {     [Transaction(TransactionOption.Required)]     public class MyComp : ServicedComponent     {         private string theMessage = "UnSet";         //[AutoComplete]         public void SetInfo(string msg)         {             ContextUtil.MyTransactionVote                 = TransactionVote.Abort;             theMessage = msg;             // If something is wrong, throw exception             // and the vote will remain in Abort.             ContextUtil.MyTransactionVote                 = TransactionVote.Commit;         }         //[AutoComplete]         public string GetInfo()         {             ContextUtil.MyTransactionVote                 = TransactionVote.Abort;             // If something is wrong, throw exception             // and the vote will remain in Abort.             ContextUtil.MyTransactionVote                 = TransactionVote.Commit;             return theMessage;         }     } } 

VB.NET (ESAutoComplete)

 'MyComp.vb as part of ESLib.dll Imports System.EnterpriseServices <Transaction(TransactionOption.Required)> _ Public Class MyComp     Inherits ServicedComponent     Private theMessage As String = "UnSet"     '<AutoComplete()> _     Public Sub SetInfo(ByVal msg As String)         ContextUtil.MyTransactionVote _             = TransactionVote.Abort         theMessage = msg         ' If something is wrong, throw exception         ' and the vote will remain in Abort.         ContextUtil.MyTransactionVote _             = TransactionVote.Commit     End Sub     '<AutoComplete()> _     Public Function GetInfo() As String         ContextUtil.MyTransactionVote _             = TransactionVote.Abort         ' If something is wrong, throw exception         ' and the vote will remain in Abort.         ContextUtil.MyTransactionVote _             = TransactionVote.Commit         Return theMessage     End Function End Class 

In the methods of the component, you first set the transaction vote to Abort. If the method is successful, you change the vote to Commit. If the method is not successful, you throw an exception, leaving the transaction vote as Abort. You do not use the AutoComplete attribute on the methods. The advantage of this is that the object is not automatically deactivated without your explicit intent. The output after the above code change is shown in Figure 8-23.

Figure 8-23. Output after code change in Example 8-20


The AutoComplete attribute comes with a side effect. Understand its impact on an object's lifetime. It is better to explicitly set the transaction vote to Abort or Commit, rather than using the AutoComplete attribute. Use AutoComplete only if you really want the object to be discarded when the method completes.

IN A NUTSHELL

Instead of using the AutoComplete attribute, directly set the transaction vote in your code. Avoid the side effect of AutoComplete that automatically deactivates the component.

SEE ALSO

Gotcha #74, "ServicedComponents implemented inconsistently on XP and 2003."



    .NET Gotachas
    .NET Gotachas
    ISBN: N/A
    EAN: N/A
    Year: 2005
    Pages: 126

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net