Nested Transactions and "MSDTC on server is unavailable"

Nested Transactions and "MSDTC on server is unavailable"

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


Cslah posted on Wednesday, January 09, 2008

Sometimes within the dataportal of one object I need to create a new business object and save it. When I do so I get the dreaded "MSDTC on server xxxx is unavailable" error and am reminded that I have to do some work to pass the connection around. I'm always perplexed when I get this that it's not discussed further. I would think it's a very common use case to modify business objects while you're inserting or updating.

There are two issues that I would love to have an elegant solution for.

First, one (which is at least solvable) is to pass around the connection. This is done by saving it in localcontext. I understand this, but don't like it. IMO, it's a pain to have to check the local context every time for a connection and use it, otherwise create a new. It's so redundant that I would think it could be embedded into the framework somewhere. I tried creating the suggested ConnectionManager that was in a thread, and it works for a few of my cases (see second problem below), but again, should be within the framework.

But passing the connection around doesn't solve the bigger second problem, which is creating an object within the dataportal, when this newly created object has a DataPortal_Create that fetches things. The dataportal_create tries to open a datareader, which is is actually not possible because a connection can only have a single open datareader and it's already being used. I have read about workarounds with this as well, but it again adds further complexity.

I can turn off my transactional support and all of this will work fine, but this means that if anything in my dataportal fails, nothing will be rolled back.

So popping up to the global view of this all, my internal debate is a tradeoff that seems to be simple to maintain code without transactions vs. complex code that use transactions properly.

I realize also that I may be missing something slap-yourself-in-the-forehead obvious, which is why I'm posting this. I've found that sometimes when there isn't much traffic on an issue it's that I'm doing something wrong.

Has anyone else totally opted for turned off transactions to get this sort of behavior to work? Or anything else?

vdhant replied on Wednesday, January 09, 2008

Hey
I have been running into a similar problem for the last little while. Not specifically with CSLA (as you said that might already be a solution within CSLA but i couldn't find it) but in general where you have the business and data tiers split.

Basically the reason why I imaging that it only works in some of you cases (putting the connection into the localcontext) is that the local context only exists per call to the server. Meaning that if you make one call through the data portal then set up the connection into the local context, anything you do while on that thread is fine. But if you go through the dataportal then come back to the client and then want to go through the data portal again you have lost the connection because it is on a different thread. This is the problem i faced, because I consider that the data methods should not contain business logic around what else needs to happen when that item is being saved, that is the job of the business layer. But if you say this then you run into the problem that I imagine you and I are facing.

So what needs to occur is that the connection needs to be persisted on the server until it is finished with, so that you can make as many calls across the network as you need to and still use the same connection. NOW, i say this with caution because the whole idea is that you make is limited amount of calls across the network as possible and having connections persisted on the server, if not handled correctly can be a dangerous thing.

Hence to get around all this i started working on a generic solution which is not necessarily tied into CSLA but could be used in any client server environment as long as it has access to the data portal in this case or whatever mechanism sends the messages across the network. And the way that i have gone about it is that in the business logic you simple have a using statement which works in the same logical way as transactionscope does. In that you can have nested transactions all of which can be defined in the business logic or the data logic and they just work and don't need to know about each other etc.

In saying all this CSLA might have a solution but I needed something that i could use elsewhere as well, so if CSLA has something built in and someone knows about it please mention it, but on the other hand if you are interested in my implementation of this have a look at this thread. I know the first half of it addresses some specific stuff about transaction promotion but towards the end we get into the bigger picture of what I am trying to do.
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2600936&SiteID=1


Hope that helps
Anthony

ajj3085 replied on Wednesday, January 09, 2008

Cslah:
But passing the connection around doesn't solve the bigger second problem, which is creating an object within the dataportal, when this newly created object has a DataPortal_Create that fetches things. The dataportal_create tries to open a datareader, which is is actually not possible because a connection can only have a single open datareader and it's already being used. I have read about workarounds with this as well, but it again adds further complexity.


Well, you'll have to make sure you close the data reader before trying to use another one, or look at using MARS.  I don't think Csla provides support because this is any issue anytime you try to use TransactionScope.. whether or not you use Csla.    Alternately, you can handle transactions manually.

JoeFallon1 replied on Wednesday, January 09, 2008

I handle the transactions manually for a few reasons - one is to avoid the use of MSDTC.

I add it to the LocalContext and pass it around. The code is redundant which is why I moved it into my Base class which inherits from CSLA. I believe I have posted samples of the entire code before so feel free to search for it.

Joe

 

Cslah replied on Wednesday, January 09, 2008

OK, I figured out my problem. It actually came back to my slap-self-in-forehead answer, like I suspected.

I have an object that has references to other objects. The user is actually allowed to modify the other objects at the same time as the main object. it's not a parent/child relationship though, they're just related to each other buy an ID. What I was trying to do is fetch the other object by ID and save all of them separately, within the dataportal of the main object. Duh!

So, what I did was override the Save method, modify the other objects, then call base.Save() and and in the DataPortal of the main object detect if the other ones are dirty and update them if so.

Slap! :-)

Brian

Copyright (c) Marimer LLC