Either DotNetNuke UserController.GetUser(PortalId,UserId,false) or UserController.ValidateUser(...) inside TransactionScope is causing TransactionAbortedException and the innerException is TransactionPromotionException. The symptoms are the same as this.
Could anyone suggest me the solution to this issue?
Thanks a lot !
using (System.Transactions.TransactionScope ts = new System.Transactions.TransactionScope())
{
DotNetNuke.Entities.Users.UserInfo ui = DotNetNuke.Entities.Users.UserController.GetUser(PortalId, UserId, false);
ts.Complete();
}
By default DotNetNuke uses the ASP.NET 2.0 membership provider. As you pointed, Membership.GetUser() opens another database connection, which causes the exception inside TransactionScope.
If you want to use GetUser() inside TransactionScope, you'll either have to enable MSDTC or use SQL Server 2008. SQL 2008 allows multiple connections within a single TransactionScope, if the connections are to the same DBMS and are not open at the same time.
See Also:
TransactionScope automatically escalating to MSDTC on some machines?
Related
I use 2 different dlls to connect to 2 database in my project . each dll has it's own DAL layer .
now in a page I need to insert a set of related data into these 2 databases . I want to use transaction to make sure data are inserted correctly . but as I just have access to public methods of Insert() of each assembly , not the ADO.net codes . how can I handle this situation ? is there anything like this in c# ?
beginTransaction(){
dll1.class.Insert(data);
dll2.className.Insert(relatedData);
....
}
You are looking for the TransactionScope class:
Makes a code block transactional.
Example usage:
using (var tx = new TransactionScope())
{
dll1.class.Insert(data);
dll2.className.Insert(relatedData);
tx.Complete(); // if not executed, transaction will rollback
}
As #Anders Abel commented, the servers running the databases involved in the transaction will need the MSDTC service to be running and configured correctly on both the DB servers and the client machines. The DTCPing utility is very helpful in this regard.
As Steve B commented, there is an assumption here that all the databases and the associated drivers support distributed transaction enlistment. Check your documentation...
I'm having a helluva time wrapping a couple transactions to 2 different databases on the same SQL Server. I initially was having trouble with network DTC access and I resolved that. Now, the error that I continue to get is "Communication with the underlying transaction manager has failed."
We have some customer profiles in a database and when these profiles become outdated we want to move them to an 'archive' database for storage. The move is simply (italics for humor) adding them to the archive database and deleting them from the main/live database. I have a DataContext for each database. The code below performs the Add and then gets the error on the Delete when trying to use the second DataContext. I've only been working with LINQ for a few months and I've scoured articles for the past couple of days. I'd like to know if anything is wrong with my code or if there is still something not configured properly with the DTC or ???
We're running on VMware for my workstation and the server.
- Workstation is Windows 7 SP1
- Server is Windows and SQL Server 2008R2
Routine for the 'Move':
private int MoveProfileToArchiveDB( int iProfileId )
{
int rc = RC.UnknownError;
// get new Archive profile object
ProfileArchive.ProfileInfo piArchive = new ProfileArchive.ProfileInfo();
// 'Live' DataContext
using ( ProfileDataContext dbLive = new ProfileDataContext() )
{
// get Live profile
ProfileInfo piLive = ProfileInfo.GetProfile( dbLive, iProfileId );
// copy Live data to Archive profile object... including the id
ProfileArchive.ProfileInfo.CopyFromLive( piLive, piArchive, true );
}
bool bArchiveProfileExists = ProfileArchive.ProfileInfo.ProfileExists( piArchive.id );
// make the move a transaction...
using ( TransactionScope ts = new TransactionScope() )
{
// Add/Update to Archive db
using ( ProfileArchiveDataContext dbArchive = new ProfileArchiveDataContext() )
{
// if this profile already exists in the Archive db...
if ( bArchiveProfileExists )
{
// update the personal profile in Archive db
rc = ProfileArchive.ProfileInfo.UpdateProfile( dbArchive, piArchive );
}
else
{
// add this personal profile to the archive db
int iArchiveId = 0;
piArchive.ArchiveDate = DateTime.Now;
rc = ProfileArchive.ProfileInfo.AddProfile( dbArchive, piArchive, ref iArchiveId );
}
// if Add/Update was successful...
if ( rc == RC.Success )
{
// Delete from the Live db
using ( ProfileDataContext dbLive = new ProfileDataContext() )
{
// delete the personal profile from the Profile DB
rc = ProfileInfo.DeleteProfileExecCmd( dbLive, iProfileId ); // *** ERROR HERE ***
if ( rc == RC.Success )
{
// Transaction End (completed)
ts.Complete();
}
}
}
}
}
return rc;
}
NOTES:
I have a few different methods for the Delete and they all work outside the TransactionScope.
ProfileInfo is the main profile table and is roughly the same for both Live and Archive databases.
Any help is greatly appreciated! Thanks much...
Rather than continue criss cross comments, I decided to post this as an answer instead.
don't use error codes. That's what exceptions are for. The code flow is more difficult to read and error code returns invite to be ignored. Exceptions make the code easier to read and far less error prone.
If you use a TransactionScope, remember to always set the isolation level explicitly. See using new TransactionScope() Considered Harmful. The implicit isolation level of SERIALIZABLE is almost never called for and has tremendous negative scale impact.
Transaction escalation. Whenever multiple connections are opened inside a transaction scope they can escalate the transaction to a distributed transaction. The behavior differ from version to version, some have tried to document it, eg. TransactionScope: transaction escalation behavior:
SQL Server 2008 is much more intelligent then SQL Server 2005 and can
automatically detect if all the database connections in a certain
transaction point to the same physical database. If this is the case,
the transaction remains a local transaction and it is not escalated to
a distributed transaction. Unfortunately there are a few caveats:
If the open database connections are nested, the transaction is still
escalated to a distributed transaction.
If in the transaction, a
connection is made to another durable resource, the transaction is
immediately escalated to a distributed transaction.
Since your connection (from the two data contextes used) point to different databases, even on SQL Server 2008 your TransactionScope will escalate to a distributed transaction.
Enlisting your application into DTC is harmful in at least two ways:
throughput will sink through the floor. A database can support few thousand local transactions per second, but only tens (maybe low hundreds) of distributed transactions per second. Primarily this is because of the complexity of two phase commit.
DTC requires a coordinator: MSDTC. The [security enhancements made to MSDTC] make configuration more challenging and it certainly is unexpected for devs to discover that MSDTC is required in their app. The steps described in the article linked are probably what you're missing right now. For Windows Vista/Windows 7/Windows Server 2008/Windows Server 2008R2 the steps are described in MSDTC in Windows Vista and Windows Server 2008, in How to configure DTC on Windows 2008 and other similar articles.
Now if you fix MSDTC communication following the articles mentioned above, your code should be working, but I still believe this archiving should not occur in the client code running EF. There are far better tools, SSIS being a prime example. A nightly scheduled job running SSIS would transfer those unused profiles far more efficiently.
I have a method which I use to execute all my SQL queries (inserts, updates, delete) and I want to put a check in to make sure the change is within a transaction before the code is executed but I can't find a property against the SqlTransaction to check for.
How should I proceed?
Note: I am using FDBConnection.BeginTransaction("MY_TRANS"); to begin the transaction and standard rollback and commnit methods
Using .NET 4.0 with VS 2010 Web Dev Express.
if your db is SQL server, ##TRANCOUNT shows # of active transactions in the connection in context
Select ##TRANCOUNT
if other db , please specify
HTH
I need to use application rule security on sql server. And i want to use Enity Framework Code First.
After a successful login my connection is set to application role. I then create my DbContext using this connection.
But: EF expects a closed connection object. And closing a connection drops the application role.
How can i solve this dilemma?
I managed to get this work with two steps:
Switch connection pooling off, which is mentioned all the time for connections using application roles. As i have a desktop application, this is no problem for me.
Add a handler to DbConnection.StateChanged and activate the application role on every opening of the connection. Without connection pooling, it is not necessary to sp_unsetapprole on closing. So this works for me:
context.Database.Connection.StateChanged += (sender, args) =>
if (args.CurrentState == ConnectionState.Open) {
activateAppRole((DbConnection)sender, ...);
}
}
I guess, if Pooling is vital for someone, she may call sp_unsetapprole on closing the connection in this same handler.
Since this question is high on the search result list, I just wanted to throw in a word of caution. I have an app that needed to use an application role and okrumnow's solution seemed at first to work.
However, in unit testing I discovered that sometimes handling the StateChanged event will cause the event to be raised twice and you'll get the error:
"Impersonate Session Security Context" cannot be called in this batch because a simultaneous batch has called it.
It seems to help to change the conditional to:
args.CurrentState == ConnectionState.Open &&
args.CurrentState == ConnectionState.Closed
But it still doesn't eliminate the error. I confirmed this in EF4.3 and EF5. Ladislav is correct that the ideal way is creating a connection for the DbContext and telling the context that it doesn't own it.
Also, connection pooling is never possible with this setup since there is no ConnectionState.Closing event where you can call sp_unsetapprole before your connection is closed.
Since I had the flexibility, my solution was to eliminate the usage of an app role and using a dedicated SQL login instead. Either way you're hard-coding a password...
EF doesn't have any native support for this. I guess the workaround can be:
Creating your own connection and passing it (closed) to EF context / EntityConnection. This should enforce that you will have connection lifetime under your control and EF will not close it (but I already saw complains that it doesn't work with DbContext).
Once you have instance of the context created set application role. Context itself should not generate any queries to the database (except DbContext with code first checking version of the database) so setting the role after context creation should not cause any problems.
I get following error randomly when executing code from debug mode.
Cannot access a disposed object.
Object name: 'SqlDelegatedTransaction'.
Error is being thrown after few commands have been executed instantly, not an timeout issue
I have just one transaction, opened with
using(var scope = new TransactionScope(TransactionOption.Required))
Multiple connections are opened with same statement above in nested code.
i am using sqlserver 2008
What could be wrong?
When you use TransactionOption.Required the transaction joins the ambient transaction.
One possible theory is:
If you go through the transaction scope and do not call scope.Complete(), it will dispose the abmient transaction. The next code that tries to run against the database will then fail.
Another would be problems with respect to active result sets:
Are you using SQL Server 2000, which does not support Multiple Active Result Sets(MARS)
Does your connect string specify MultipleActiveResultSets= true