'MSDTC not available' error on ProjectTracker

'MSDTC not available' error on ProjectTracker

Old forum URL: forums.lhotka.net/forums/t/727.aspx


MarkF posted on Wednesday, July 26, 2006

I'm using ProjectTracker20cs (unaltered) on an XP Pro pc.
If I add a new project and add some resources to it, when I try to apply I get this error:
'MSDTC on server xxx is unavailable.'
This appears to be due to the project insert using one connection and the ResourceAssignment(s) using other connections, albeit using exactly the same connection string.
Can someone confirm that System.Transactions is unable to spot that the new connection is to the same database even when the connection strings are identical?
And, if so, does this mean that a single connection needs to be passed around if a 'distributed transaction' is to be avoided?

Bonio replied on Wednesday, July 26, 2006

I had the same problem when trying to do transactional updates in separate objects.

The way I got round it was to add an SQL Connection property to the criteria class that is passed into the DataPortal_Execute and to pass the live connection that way. Although I am not sure if this is correct, it is working well for transactions where multiple updates occur in different objects.

Let me know if you need more help/code.

Bonio

Mark replied on Wednesday, July 26, 2006

Correct - System.Transactions does not inspect the connection string when determining whether or not or promote the transaction to the DTC.  If you open a second connection within a transaction, it's going to get promoted - even if the connection is to the same database as your first connection.   Passing the connection object among objects is a common way to avoid this behavior.

Also, if you're using SQL 2000, please note that it doesn't fully support the new System.Transactions namespace, so even if you only open a single connection using System.Transactions and are using SQL 2000, it will be promoted to the DTC.  There is a workaround for this, though - see http://www.lhotka.net/Article.aspx?area=4&id=39c79955-ddc9-42c7-a657-d5c2ed49975e for more details.

MarkF replied on Wednesday, July 26, 2006

Thanks for the info about SQL 2000 and confirmation that passing a single connection is the correct way to handle this. I was just hoping for a comment from RL as he says in his book that invoking DTC is expensive and yet the way he has implemented ProjectTracker causes it to be invoked when it shouldn't be necessary. It just makes me wonder whether he has a reason against passing the connection around?


ajj3085 replied on Wednesday, July 26, 2006

Well, I personally wasn't bite by this until I deployed my application, and the db server had DTS turned off, where as everywhere else it was 'on' by default (I guess).  So I could see how this would be missed.  I haven't finished the Second edition yet, but he did pass around the connection in the previous edition.

MarkF replied on Wednesday, July 26, 2006

Having read a few more pages, I see that Rocky does pass the connection around from Resource to ResourceAssignment, so that confirms to me that that approach is fine.
I do think that his comment that the connection pooling means that we don't need to worry about opening and closing connections willy-nilly does need the rider that this won't work (well) if you are using System.Transactions.

RockfordLhotka replied on Wednesday, July 26, 2006

Unfortunately it turns out that System.Transactions isn't quite as smart as would be nice, and it doesn't realize that you are reusing the same connection parameters on subsequent connections.

Toward this end, in version 2.1 I'm adding ApplicationContext.LocalContext - which works just like ClientContext and GlobalContext today, but is entirely ignored by the data portal. You can use it on the app server to store a connection or transaction object - making it available to all your code without having to pass parameters.

The only catch: you need to clean up after yourself or you'll be in trouble - resource leaks and the like.

You can actually do this today using ClientContext. ClientContext isn't returned from the server to the client, so you can use it on the app server exactly like you'll be able to use LocalContext.

Or you can be explicit (which might be better anyway) and pass the connection/transaction object as a parameter to each data method like I did in the book.

gajit replied on Wednesday, October 18, 2006

Sorry for digging up an old thread, but I am experiencing this problem (in my application - not the projecttracker) and am struggling with a resolution...

I am running csla2.0 (don't have the resources right now to move forward), and experience this problem when trying to update child rows .

I am trying to pass the connection down the hierarchy, but I have a strange(?) behaviour.

I'll put it in 'projecttracker' terms...

I have included a "ByVal cn As SqlConnection" through the necessary class functions.

From my "projectresources" type class..

Friend Sub Update(ByVal cn As SqlConnection, ByVal cardMoves_selection As CardMoves_Selection)

Me.RaiseListChangedEvents = False

' update (thus deleting) any deleted child objects

For Each obj As CardMoves_Selection_State In DeletedList

obj.DeleteSelf(cn, cardMoves_selection)

Next

' now that they are deleted, remove them from memory too

DeletedList.Clear()

' add/update any current child objects

For Each obj As CardMoves_Selection_State In Me

If obj.IsNew Then

obj.Insert(cn, cardMoves_selection)

Else

obj.Update(cn, cardMoves_selection)

End If

Next

Me.RaiseListChangedEvents = True

End Sub

my "projectresource" type  .Update looks like this

Friend Sub Update(ByRef cn As SqlConnection, ByVal cardmoves_selection As CardMoves_Selection)

' if we're not dirty then don't update the database

If Not Me.IsDirty Then Exit Sub

Using cn 'As New SqlConnection(Database.crm2k6Connection)

If cn.State <> ConnectionState.Open Then cn.Open()

CardMoves_Selection_Assignment.UpdateStateAssignment( _

cn, cardmoves_selection.SALESREP, mSTATE, mSELECTED)

MarkOld()

End Using

End Sub

and my "assignments" type subs look like this: -

Public Sub UpdateStateAssignment(ByRef cn As SqlConnection, _

ByVal salesrep As String, _

ByVal state As String, _

ByVal selected As Boolean)

If cn.State <> ConnectionState.Open Then cn.Open()

Using cm As SqlCommand = cn.CreateCommand()

cm.CommandText = "C2K6_CardMoves_Selection_UpdateState"

DoStateAddUpdate(cm, salesrep, state, selected)

End Using

End Sub

Private Sub DoStateAddUpdate(ByVal cm As SqlCommand, _

ByVal salesrep As String, _

ByVal state As String, _

ByVal selected As Boolean)

cm.CommandType = CommandType.StoredProcedure

cm.Parameters.AddWithValue("@SALESREP", salesrep)

cm.Parameters.AddWithValue("@USERNAME", Csla.ApplicationContext.User.Identity.Name)

cm.Parameters.AddWithValue("@STATE", state)

cm.Parameters.AddWithValue("@SELECTED", selected)

cm.ExecuteNonQuery()

End Sub

 

What appears to be happening is that my connection object persists through the process, but for some reason comes back empty...

' add/update any current child objects

For Each obj As CardMoves_Selection_State In Me

If obj.IsNew Then

   obj.Insert(cn, cardMoves_selection)

Else

   obj.Update(cn, cardMoves_selection)

   'stepping through the code, the connection object persists until it returns back from .UPDATE

End If

Next

Me.RaiseListChangedEvents = True

 

Anyone have any ideas why?

Or alternatively, if someone has an exmaple of how they manage to pass the connection object about successfully, it would be much appreciated.

Thanks,

G.

 

 

 

 

 

 

 

ajj3085 replied on Wednesday, October 18, 2006

Are you using Transaction Scope and Sql Server 2000?   I think that will always result in the transaction being promoted to a distributed transaction. 

gajit replied on Wednesday, October 18, 2006

Thanks for the quick reply.

We are using SQL Server 2005
As for the transaction scope, if that is determined by

"<Transactional(TransactionalTypes.TransactionScope)> _"

Ahead of the subroutine/functions for Dataportal_Update etc in my PARENT bo, then yes, I am using transaction scope.

If that is the case, is there a programmatical resolution? Can I remove the transaction scope? and if so, what's the impact?

Thanks,
g.












Mark replied on Wednesday, October 18, 2006

First thing I see - you're wrapping the child updates in a 'using' clause.  That's bad.  :-)  The connection will be closed and disposed during each child update.  That means each child update will have to create/open a new connection, which is why you're seeing the DTC behavior.  You want to wrap the 'using' clause around the root object (and ONLY the root object).

//Root object
using (SqlConnection conn = new SqlConnection(blah, blah)
{
   //Update root
   ChildCollection1.Update(conn);
   ChildCollection2.Update(conn);
}

This will eliminate the need to check if the connection is open in the child update statements.  It will also only result in one transaction in SQL Server.

gajit replied on Wednesday, October 18, 2006

righttttt.... i see... 
i had actually given up on the single transaction methodolgy based on ajj's previous post.
However, I'm not sure of the impact of these actions.I also removed the transaction_scope prefixes on my parent object's methods - et voila, it works.... I'm hoping someone might share their wisdom..

But thanks Mark, depending on the answer I get with regards to the impact, I may very well need to go back to the single connection and this will prove very useful.

Thanks,
g.





ajj3085 replied on Thursday, October 19, 2006

gajit:
i had actually given up on the single transaction methodolgy based on ajj's previous post.


Opps, that's not the path I meant to send you down.  TransactionScope is very useful, and if you're on 2005 there's no reason not to use it.  You just have to make sure you open one and only one connection.

gajit:
However, I'm not sure of the impact of these actions.I also removed the transaction_scope prefixes on my parent object's methods - et voila, it works.... I'm hoping someone might share their wisdom..


If you just removed the attributes then the impact is your updates are no longer running in any kind of transaction.  If one of your child updates fails, you'll leave the database in a bad state.  I would put it back.. just make the root responsible for cleaning up the connection, the children should use it and not care about closing it.  That's a fine model, because the root is handling the transaction as well.

gajit:
But thanks Mark, depending on the answer I get with regards to the impact, I may very well need to go back to the single connection and this will prove very useful.


I sugest going back to the single connection route, as you probably don't want to lose transactions.  The other alternative is to handle the transaction yourself... but you'll just end up with more code in the root to handle the transaction to achieve the same result as using the transaction scope.

gajit replied on Thursday, October 19, 2006

"Oops" on my part. :) From your message, I anticipted a follow-up that says that it will ALWAYS create a second connection.... my bad!

Thanks for clarifying that for me ajj. The failure of a transaction in this scenario wouldn;t have a great impact, but I'm sure I will need to address other such update types in the future where transactions do play a crucial part - so I'll revisit the single connection strategy.

I'll keep you posted.

Thanks,
Graham





ajj3085 replied on Thursday, October 19, 2006

Sorry if I wasn't clear. You are in control of which connections are opened / closed so just keep a single connection for use during whatever operation and you'll be fine.

gajit replied on Thursday, October 19, 2006

Thanks guys!

I reinserted the transaction_scope and removed the 'using' , modified my code to pass the connection all the way down through by bo's and it works!

Thanks again. I love this framework and the great community that uses it.

Gaj.




Copyright (c) Marimer LLC