![]() | |
![]() |
| | Thread Tools | Search this Thread | Display Modes |
#1
| |||||||
| |||||||
|
|
I'm having a problem with a Serviced Component Try Finally Dispose pattern. When running in a server application, disposing the object after it has called SetAbort throws: [COMException (0x8004e003): You made a method call on a COM+ component that has a transaction that has already aborted or in the process of |
|
I understand that when the object calls SetAbort, it is indicating that it is done and COM+ will deactivate the object. However, an IDisposible component should be disposed by the client that created it. That's just a good practice, right? This problem only occurs if the object is in a server application and it is not the object that started the transaction (I think). To reproduce the problem, create three projects. One class library project containing this code (library must have a strong name): EnterpriseServices.Transaction(EnterpriseServices. TransactionOption.Require |
|
Public Class ServicedComponentTestObject2 Inherits EnterpriseServices.ServicedComponent Public Function Test() As Boolean Dim result As Boolean Try If MsgBox("Throw exception in ServicedComponentTestObject2?", MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then Throw New Exception("Exception from ServicedComponentTestObject2") Else EnterpriseServices.ContextUtil.SetComplete() result = True End If Catch ex As Exception EnterpriseServices.ContextUtil.SetAbort() Throw ex End Try Return result End Function End Class Another class library project containing this code (library must have a strong name and a reference to the previous library): EnterpriseServices.Transaction(EnterpriseServices. TransactionOption.Require |
|
Public Class ServicedComponentTestObject Inherits EnterpriseServices.ServicedComponent Public Function Test() As Boolean Dim result As Boolean Try Dim test2 As ServicedComponentTestObject2 test2 = New ServicedComponentTestObject2 Try result = test2.Test() If result Then If MsgBox("Throw exception in ServicedComponentTestObject1?", MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then Throw New Exception("Exception from ServicedComponentTestObject") Else result = True End If End If Finally Try EnterpriseServices.ServicedComponent.DisposeObject (test2) Catch ex As Exception MsgBox(String.Concat("Failed to dispose of ServicedComponentTestObject2 object: ", vbCrLf, vbCrLf, ex.ToString)) End Try End Try Catch ex As Exception EnterpriseServices.ContextUtil.SetAbort() Throw ex End Try Return result End Function End Class And a command-line utility containing this code and referencing the library above: Module ServicedComponentTestApp Sub Main() Try Dim test As ServicedComponentTest.ServicedComponentTestObject test = New ServicedComponentTest.ServicedComponentTestObject Try If test.Test() Then MsgBox("Success") End If Finally Try EnterpriseServices.ServicedComponent.DisposeObject (test) Catch ex As Exception MsgBox(String.Concat("Failed to dispose of ServicedComponentTestObject object: ", vbCrLf, vbCrLf, ex.ToString)) End Try End Try Catch ex As Exception MsgBox(ex.ToString) End Try End Sub End Module Run the command line app and the two class libraries will register themselves in two COM+ packages. When prompted click Yes to the "Throw |
|
[COMException (0x8004e003): You made a method call on a COM+ component that has a transaction that has already aborted or in the process of |
|
Note that there is not difference in behavior between DisposeObject and Dispose. While the error is technically correct, shouldn't the client be able to dispose of the object? Using a try finally dispose pattern for an |
|
Is this a bug or am I missing something? |
#2
| |||
| |||
|
|
If this is by design then it is a poor design. If dispose throws an error, how do I know the object was successfully disposed? Why would the design stipulate that in one case, dispose |
|
The dispose method in ServicedComponent should simply teardown the proxy if the real object has been deactivated by COM+. Otherwise, the disposal |
|
Note that SetComplete also implies that COM+ should deactivate the object but calling dispose in this case never throws an error. Disposing of the object should never be related to the transactional outcome. "Stan" wrote: It is not a bug, it is by design. Wrap you dispose in try-cach try { o.Dispose(); } catch {} "Will" <Will (AT) discussions (DOT) microsoft.com> wrote in message news:C0B92499-1719-4123-BA2B-6E0BBD25618A (AT) microsoft (DOT) com... I'm having a problem with a Serviced Component Try Finally Dispose pattern. When running in a server application, disposing the object after it has called SetAbort throws: [COMException (0x8004e003): You made a method call on a COM+ component that has a transaction that has already aborted or in the process of aborting.] I understand that when the object calls SetAbort, it is indicating that it is done and COM+ will deactivate the object. However, an IDisposible component should be disposed by the client that created it. That's just a good practice, right? This problem only occurs if the object is in a server application and it is not the object that started the transaction (I think). To reproduce the problem, create three projects. One class library project containing this code (library must have a strong name): EnterpriseServices.Transaction(EnterpriseServices. TransactionOption.Require d, Timeout:=0)> _ Public Class ServicedComponentTestObject2 Inherits EnterpriseServices.ServicedComponent Public Function Test() As Boolean Dim result As Boolean Try If MsgBox("Throw exception in ServicedComponentTestObject2?", MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then Throw New Exception("Exception from ServicedComponentTestObject2") Else EnterpriseServices.ContextUtil.SetComplete() result = True End If Catch ex As Exception EnterpriseServices.ContextUtil.SetAbort() Throw ex End Try Return result End Function End Class Another class library project containing this code (library must have a strong name and a reference to the previous library): EnterpriseServices.Transaction(EnterpriseServices. TransactionOption.Require d, Timeout:=0)> _ Public Class ServicedComponentTestObject Inherits EnterpriseServices.ServicedComponent Public Function Test() As Boolean Dim result As Boolean Try Dim test2 As ServicedComponentTestObject2 test2 = New ServicedComponentTestObject2 Try result = test2.Test() If result Then If MsgBox("Throw exception in ServicedComponentTestObject1?", MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then Throw New Exception("Exception from ServicedComponentTestObject") Else result = True End If End If Finally Try EnterpriseServices.ServicedComponent.DisposeObject (test2) Catch ex As Exception MsgBox(String.Concat("Failed to dispose of ServicedComponentTestObject2 object: ", vbCrLf, vbCrLf, ex.ToString)) End Try End Try Catch ex As Exception EnterpriseServices.ContextUtil.SetAbort() Throw ex End Try Return result End Function End Class And a command-line utility containing this code and referencing the library above: Module ServicedComponentTestApp Sub Main() Try Dim test As ServicedComponentTest.ServicedComponentTestObject test = New ServicedComponentTest.ServicedComponentTestObject Try If test.Test() Then MsgBox("Success") End If Finally Try EnterpriseServices.ServicedComponent.DisposeObject (test) Catch ex As Exception MsgBox(String.Concat("Failed to dispose of ServicedComponentTestObject object: ", vbCrLf, vbCrLf, ex.ToString)) End Try End Try Catch ex As Exception MsgBox(ex.ToString) End Try End Sub End Module Run the command line app and the two class libraries will register themselves in two COM+ packages. When prompted click Yes to the "Throw exception in ServicedComponentTestObject2" prompt. Note that the exception is returned to ServicedComponentTestObject and ServicedComponentTestObject successfully disposes of the ServicedComponentTestObject2 object. Now change the package containing ServicedComponentTestObject2 to a service application (NOTE you will probably need to disable access checks) and rerun the app. Now when you answer yes to the "Throw exception in ServicedComponentTestObject2" prompt and ServicedComponentTestObject tries to dispose of ServicedComponentTestObject2, the DisposeObject method throws: [COMException (0x8004e003): You made a method call on a COM+ component that has a transaction that has already aborted or in the process of aborting.] Note that there is not difference in behavior between DisposeObject and Dispose. While the error is technically correct, shouldn't the client be able to dispose of the object? Using a try finally dispose pattern for an IDisposible object seems to be good programming but depending on how the component is served, the dispose may or may not work. This isn't good encapsulation because the client must know how the component is and whether it called SetAbort. Is this a bug or am I missing something? |
#3
| |||
| |||
|
|
This only happens when a) you dispose a none-root serviced component b) component aborts the transaction By design COM+ does not allow any calls into aborted context and when you call Dispose this rule is broken and COM+ throws CONTEXT_E_ABORTING There is nothing you can do about it other then wrapping the secondary component in try-catch... "Will" <Will (AT) discussions (DOT) microsoft.com> wrote in message news:7327A798-8B72-4884-BC57-82A32B1B3096 (AT) microsoft (DOT) com... If this is by design then it is a poor design. If dispose throws an error, how do I know the object was successfully disposed? Why would the design stipulate that in one case, dispose succeeds and in another it fails? The dispose method in ServicedComponent should simply teardown the proxy if the real object has been deactivated by COM+. Otherwise, the disposal of the ServicedComponent should release the proxy which should decrement the reference to the real object causing it to be released. Note that SetComplete also implies that COM+ should deactivate the object but calling dispose in this case never throws an error. Disposing of the object should never be related to the transactional outcome. "Stan" wrote: It is not a bug, it is by design. Wrap you dispose in try-cach try { o.Dispose(); } catch {} "Will" <Will (AT) discussions (DOT) microsoft.com> wrote in message news:C0B92499-1719-4123-BA2B-6E0BBD25618A (AT) microsoft (DOT) com... I'm having a problem with a Serviced Component Try Finally Dispose pattern. When running in a server application, disposing the object after it has called SetAbort throws: [COMException (0x8004e003): You made a method call on a COM+ component that has a transaction that has already aborted or in the process of aborting.] I understand that when the object calls SetAbort, it is indicating that it is done and COM+ will deactivate the object. However, an IDisposible component should be disposed by the client that created it. That's just a good practice, right? This problem only occurs if the object is in a server application and it is not the object that started the transaction (I think). To reproduce the problem, create three projects. One class library project containing this code (library must have a strong name): EnterpriseServices.Transaction(EnterpriseServices. TransactionOption.Require d, Timeout:=0)> _ Public Class ServicedComponentTestObject2 Inherits EnterpriseServices.ServicedComponent Public Function Test() As Boolean Dim result As Boolean Try If MsgBox("Throw exception in ServicedComponentTestObject2?", MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then Throw New Exception("Exception from ServicedComponentTestObject2") Else EnterpriseServices.ContextUtil.SetComplete() result = True End If Catch ex As Exception EnterpriseServices.ContextUtil.SetAbort() Throw ex End Try Return result End Function End Class Another class library project containing this code (library must have a strong name and a reference to the previous library): EnterpriseServices.Transaction(EnterpriseServices. TransactionOption.Require d, Timeout:=0)> _ Public Class ServicedComponentTestObject Inherits EnterpriseServices.ServicedComponent Public Function Test() As Boolean Dim result As Boolean Try Dim test2 As ServicedComponentTestObject2 test2 = New ServicedComponentTestObject2 Try result = test2.Test() If result Then If MsgBox("Throw exception in ServicedComponentTestObject1?", MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then Throw New Exception("Exception from ServicedComponentTestObject") Else result = True End If End If Finally Try EnterpriseServices.ServicedComponent.DisposeObject (test2) Catch ex As Exception MsgBox(String.Concat("Failed to dispose of ServicedComponentTestObject2 object: ", vbCrLf, vbCrLf, ex.ToString)) End Try End Try Catch ex As Exception EnterpriseServices.ContextUtil.SetAbort() Throw ex End Try Return result End Function End Class And a command-line utility containing this code and referencing the library above: Module ServicedComponentTestApp Sub Main() Try Dim test As ServicedComponentTest.ServicedComponentTestObject test = New ServicedComponentTest.ServicedComponentTestObject Try If test.Test() Then MsgBox("Success") End If Finally Try EnterpriseServices.ServicedComponent.DisposeObject (test) Catch ex As Exception MsgBox(String.Concat("Failed to dispose of ServicedComponentTestObject object: ", vbCrLf, vbCrLf, ex.ToString)) End Try End Try Catch ex As Exception MsgBox(ex.ToString) End Try End Sub End Module Run the command line app and the two class libraries will register themselves in two COM+ packages. When prompted click Yes to the "Throw exception in ServicedComponentTestObject2" prompt. Note that the exception is returned to ServicedComponentTestObject and ServicedComponentTestObject successfully disposes of the ServicedComponentTestObject2 object. Now change the package containing ServicedComponentTestObject2 to a service application (NOTE you will probably need to disable access checks) and rerun the app. Now when you answer yes to the "Throw exception in ServicedComponentTestObject2" prompt and ServicedComponentTestObject tries to dispose of ServicedComponentTestObject2, the DisposeObject method throws: [COMException (0x8004e003): You made a method call on a COM+ component that has a transaction that has already aborted or in the process of aborting.] Note that there is not difference in behavior between DisposeObject and Dispose. While the error is technically correct, shouldn't the client be able to dispose of the object? Using a try finally dispose pattern for an IDisposible object seems to be good programming but depending on how the component is served, the dispose may or may not work. This isn't good encapsulation because the client must know how the component is and whether it called SetAbort. Is this a bug or am I missing something? |
#4
| |||
| |||
|
|
Do you know if, under these specific conditions, the .NET related resources will be freed correctly even though the exception is thrown? |
#5
| |||
| |||
|
![]() |
| Thread Tools | Search this Thread |
| Display Modes | |
| |