HighTechTalks DotNet Forums  

Re: Bug in ServicedComponent.Dispose?

Dotnet Framework (Component Services) microsoft.public.dotnet.framework.component_services


Discuss Re: Bug in ServicedComponent.Dispose? in the Dotnet Framework (Component Services) forum.



Reply
 
Thread Tools Search this Thread Display Modes
  #1  
Old   
Stan
 
Posts: n/a

Default Re: Bug in ServicedComponent.Dispose? - 06-23-2004 , 11:43 AM






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

Quote:
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.]
Quote:
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)> _
Quote:
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)> _
Quote:
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:
Quote:
[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.]
Quote:
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.
Quote:
Is this a bug or am I missing something?




Reply With Quote
  #2  
Old   
Stan
 
Posts: n/a

Default Re: Bug in ServicedComponent.Dispose? - 06-23-2004 , 01:02 PM






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

Quote:
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?
Quote:
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.
Quote:
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?







Reply With Quote
  #3  
Old   
Will
 
Posts: n/a

Default Re: Bug in ServicedComponent.Dispose? - 06-23-2004 , 01:16 PM



Stan,

Thanks for the insight and feedback. I thought that was the case. Still seems that the .NET Framework should work around this or consume the error itself.

Do you know if, under these specific conditions, the .NET related resources will be freed correctly even though the exception is thrown?

Will

"Stan" wrote:

Quote:
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?








Reply With Quote
  #4  
Old   
Stan
 
Posts: n/a

Default Re: Bug in ServicedComponent.Dispose? - 06-23-2004 , 02:37 PM



Quote:
Do you know if, under these specific conditions, the .NET related
resources will be freed correctly even though the exception is thrown?

Garbage collector by design does not dispose the serviced components. MS
took this functionality out when they found that GC dispose takes too much
resources. So, always dispose yourself




Reply With Quote
  #5  
Old   
Ed
 
Posts: n/a

Default Re: Bug in ServicedComponent.Dispose? - 09-14-2004 , 07:31 AM



So when a transaction aborts you should not call Dispose() for non-root
component (for example, from other, possible root component)? But here is a
question: what will happen with non root component which participates in
aborted transaction if no Dispose() call is made? What happens with its
context? Is there a danger that in multithread environment environment
KB327443 bug may occur?

Reply With Quote
Reply




Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off



Powered by vBulletin Version 3.5.4
Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.