Transaction already commited in COM+ Enterprise Services component

Posts   
 
    
colinvella
User
Posts: 12
Joined: 28-Feb-2006
# Posted on: 14-Mar-2006 10:49:01   

Hi,

I am having problems using generated LLBLGen code in the business layer of an application composed of COM+ serviced components. This layer essentially implements the business logic of the application, using the OR layer created by LLBLGen to interface with an underlying SQLServer 2005 database.

Everything works fine with Serviced Components marked as [TransactionAttribute(TransactionOption.Disabled)] but as soon as I try to use **Required **or RequiresNew, the application fails on the first fetch of data, specifically on the line:

dao.FetchExisting(this, base.Transaction, prefetchPathToUse, contextToUse);

within the entity base's **Fetch **method.

The exception is a Transaction exception with the message:

The transaction has already been implicitly or explicitly committed or aborted.

As far as I am aware, the business layer assembly is properly set up with a strong key and registered correctly by the application. Note that I have attempted to run my code both with and without using the TransactionComPlus object, althouh as I understand it, this object is only required if a transaction spans across method calls.

Thank you in advance for your time and your assistance, Regards, Colin Vella

colinvella
User
Posts: 12
Joined: 28-Feb-2006
# Posted on: 14-Mar-2006 10:56:35   

What follows is the relevant code sample. The public method GetAvailableOwners(...) marked as AutoComplete(True) uses a private method in the same class GetUserEntity(...) marked as AutoComplete(False) so that it does not close the transaction:

[TransactionAttribute(TransactionOption.RequiresNew)]
public class AuthorisationProcess : ServicedComponent
{
    [AutoComplete(false)]
    private UserEntity GetUserEntity(Guid gUserId)
    {
        UserEntity userEntity = new UserEntity(gUserId);
        if (userEntity.IsNew)
            throw new BusinessLayerException("User with Guid '" + gUserId + "' not found.");
        if (!userEntity.Active)
            throw new BusinessLayerException("User with Guid '" + gUserId + "' not active.");
        return userEntity;
    }

    [AutoComplete(true)]
    public List<OwnerInfo> GetAvailableOwners(Guid gUserId)
    {
        UserEntity userEntity = GetUserEntity(gUserId);

        // more code follows here...
    }
}

P.S. I've posted the code sample in a separate message as it was not showing up in the first post for some reason.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39928
Joined: 17-Aug-2003
# Posted on: 15-Mar-2006 10:40:26   

For fetches you don't need a transaction but it should work regardless.

THe stacktrace you receive with that exception, does that reveil where the error occurs? (e.g. at the point where a connection is opened, or when the actual query is executed...).

Saving data with transaction required / requires new and transactioncomplus, works? or does that fail as well?

Frans Bouma | Lead developer LLBLGen Pro
colinvella
User
Posts: 12
Joined: 28-Feb-2006
# Posted on: 15-Mar-2006 12:45:25   

If I start debug stepping into the line

dao.FetchExisting(this, base.Transaction, prefetchPathToUse, contextToUse);

the debugger steps down into

UserDAO.IDbConnection DetermineConnectionToUse(ITransaction transactionToUse)
-->DBUtils.IDbConnection DetermineConnectionToUse(ITransaction containingTransaction)
-->-->DBUtils.SqlConnection CreateConnection()
-->-->-->DBUtils.SqlConnection CreateConnection(string connectionString)

The call sequence eventually retraces back up to UserDAO's DetermineConnectionToUse(...) method and the exception is thrown on returning to the FetchExisting method.

I am ignorant of the last method's implementation but the connection appears to be created just fine.

The exception is thrown for both Required and RequiesNew and both when using and not using TransactionComPlus. That is, I got it to work only by using the Disabled transaction option.

I haven't got to the point where I can try saving data without writing separate test code and it is something that I'd wish to avoid due to time constraints.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39928
Joined: 17-Aug-2003
# Posted on: 15-Mar-2006 21:12:12   

Hmm. Very strange. I must say I'm currently without a clue what could be wrong. A fetch doesn't start a transaction behind the scenes, so it should simply open the connection, call executereader on the cmd and utilize the reader's results.

It seems as if it starts a new COM+ transaction no matter what and it already has one, which clashes, but I don't see where that com+ transaction is started as all the llblgen pro code simply doesn't require/starts a COM+ transaction, except transactioncomplus, an object you didn't use as you said.

Could you try to setup a simple testcase app, which has just very little code and reproduces the problem? I'm sorry that you need to do this, but these things are very hard to track down, and it would be much easier for us to even get the right setup to test, if we have a setup which fails on your end so we can try to reproduce it here.

Frans Bouma | Lead developer LLBLGen Pro
colinvella
User
Posts: 12
Joined: 28-Feb-2006
# Posted on: 16-Mar-2006 18:10:01   

Ok, I have created a new project called **TestAssembly **with a strong key and set up a single component called **TestProcess **with a single method DoStuff(). The assembly has references to System.EnterpriseServices, the OR layer assembly generated by LLBLGen and the LLBLGen support assembly for .NET 2.0.

Assembly Code:

using System;
using System.Collections.Generic;
using System.Text;
using System.EnterpriseServices;

using SD.LLBLGen.Pro.ORMSupportClasses;

using Crimsonwing.LRHI.DataLayer.OR.EntityClasses;
using Crimsonwing.LRHI.DataLayer.OR.CollectionClasses;

namespace TestAssembly
{
    [TransactionAttribute(TransactionOption.Required)]
    public class TestProcess : ServicedComponent
    {
        public TestProcess()
        {
        }

        [AutoComplete(true)]
        public void DoStuff()
        {
            UserCollection userCollection = new UserCollection();
            userCollection.GetMulti(null);
        }
    }
}

I have also set up a simple console app that uses TestAssembly. It has the same references as the assembly plus one to the assembly itself.

Program Code:

using System;
using System.Collections.Generic;
using System.Text;

using TestAssembly;

namespace TestApp
{
    class Program
    {
        static void Main(string[] args)
        {
            TestProcess testProcess = new TestProcess();
            testProcess.DoStuff();
        }
    }
}

I still get the same results.. even for something as simple as getting all the records of a table. Note that I am using the same OR layer as for my full application but I assume that is not an issue.

colinvella
User
Posts: 12
Joined: 28-Feb-2006
# Posted on: 17-Mar-2006 15:34:00   

Hi Otis, sorry for bumping the thread but I wanted to check on the status of this issue.

If it is of any help I could try preparing a completely standalone solution with the OR layer, enterprise service layer and simple console up and send it over together with an SQL script so you can try the setup at your end.

Speaking of DB structure, would it be enough if I send you the SQL just for the table I'm trying to access in the sample (the OR layer has code for the whole DB structure)?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39928
Joined: 17-Aug-2003
# Posted on: 17-Mar-2006 19:01:40   

colinvella wrote:

Hi Otis, sorry for bumping the thread but I wanted to check on the status of this issue.

If it is of any help I could try preparing a completely standalone solution with the OR layer, enterprise service layer and simple console up and send it over together with an SQL script so you can try the setup at your end.

Speaking of DB structure, would it be enough if I send you the SQL just for the table I'm trying to access in the sample (the OR layer has code for the whole DB structure)?

Sorry for not getting back to you earlier. Your issue was planned for testing this morning, the problem was that I had to spend the whole day working on one issue in the object graph sort routine for recursive saves, which turned out to result in a rewrite of that routine (which is alittle complex) and that took all day unfortunately.

If you could setup a simple testcase, that would be great. I indeed just need the DDL sql for the table that's accessed. you can mail it to support AT llblgen.com, and I'll work on it tomorrow morning (Saturday). Your situation should be easy to reproduce so I have good hope I have a solution tomorrow. Sorry again for not looking into your error today.

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39928
Joined: 17-Aug-2003
# Posted on: 20-Mar-2006 10:17:54   

I can't reproduce it. This is what I've done: Generated a project from northwind, called NWComPlus (.NET 2.0, selfservicing 2class scenario, C#). I gave it a strong name. created a class library TestLib with this code:


using System;
using System.Collections.Generic;
using System.Text;
using System.EnterpriseServices;

using SD.LLBLGen.Pro.ORMSupportClasses;
using NWComPlus.CollectionClasses;

namespace TestLib
{
    [TransactionAttribute( TransactionOption.Required )]
    public class TestProcess : ServicedComponent
    {
        public TestProcess()
        {
        }

        [AutoComplete( true )]
        public void DoStuff()
        {
            CustomerCollection customers = new CustomerCollection();
            customers.GetMulti( null );
            Console.WriteLine( "# of customers fetched: {0}", customers.Count );
        }
    }
}

Also strong named.

Then I created a console app Tester with this code:


using System;
using System.Collections.Generic;
using System.Text;

using TestLib;

namespace Tester
{
    class Program
    {
        static void Main( string[] args )
        {
            TestProcess tp = new TestProcess();
            tp.DoStuff();
        }
    }
}

Compiled it, ran it and got the expected results.

Ran on Windows XP SP2 while connecting to a SQLServer db on a win2k3 box.

Could you remove the dll from COM+ (component services snap in, component removal) and try again, perhaps on another machine? I find it really strange you get that particular error, because it makes no sense at all.

Frans Bouma | Lead developer LLBLGen Pro
colinvella
User
Posts: 12
Joined: 28-Feb-2006
# Posted on: 22-Mar-2006 17:51:02   

Unfortunately I am quite tied up with a milestone this week so I cannot spend as much time on this as I'd like to. However, I will eventually create a completely new solution from scratch and a very small generated OR layer for only one table to try to reproduce the testcase as separately as possible from the main project codebase.

In the meantime here is some info on my workstation specs: Windows 2000 SP4 5.0.2195. The SQL Server 2005 database is on another server running Windows 2003.

Just an afterthought.. I am also registering lower layer assemblies (beneath my COM+ business layer) with a strong key, including the OR layer itself. Could this be the reason for the problem I am experiencing? Only classes in the Business Layer assembly inherit from ServicedComponent and use the appropriate transaction attributes.. the lower layers simply have a strong name and are registered with regsvcs in a post-build step.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39928
Joined: 17-Aug-2003
# Posted on: 23-Mar-2006 11:06:09   

colinvella wrote:

Unfortunately I am quite tied up with a milestone this week so I cannot spend as much time on this as I'd like to. However, I will eventually create a completely new solution from scratch and a very small generated OR layer for only one table to try to reproduce the testcase as separately as possible from the main project codebase.

Ok simple_smile Just post back into this thread simple_smile

In the meantime here is some info on my workstation specs: Windows 2000 SP4 5.0.2195. The SQL Server 2005 database is on another server running Windows 2003.

I don't think it's a problem with win2k, it should work the same as with windows xp, although I have to admit, the COM+ version in XP is higher, it has more features than in windows2000

Just an afterthought.. I am also registering lower layer assemblies (beneath my COM+ business layer) with a strong key, including the OR layer itself.

I signed the generated code, and as the ormsupportclasses are already signed, it should work OK. I didnt register anything, the necessary dlls are registered automatically when you start the app.

Could this be the reason for the problem I am experiencing? Only classes in the Business Layer assembly inherit from ServicedComponent and use the appropriate transaction attributes.. the lower layers simply have a strong name and are registered with regsvcs in a post-build step.

You unregister them also? Could you try by first removing them completely, then NOT registering anything and simply run the app? It should register the dlls and start the app.

Frans Bouma | Lead developer LLBLGen Pro
colinvella
User
Posts: 12
Joined: 28-Feb-2006
# Posted on: 06-Apr-2006 11:19:26   

I finally found some time to set up a completely standal-one test case. It is VS 2005 solution containing a TestApp console application, a strong named TestAssembly assembly and a small generated OR layer. The OR layer contains only one entity (User table) and is also strong named (the TestAssembly project cannot be compiled unless the referenced OR layer project is also strongly signed).

Note that there is no explicit registration via a build step in the project - the assembly is registered automatically using my admin rights on my workstation.

Unfortunately I still experience the same problem with this setup, but at least I can now send you the full solution with LLBLGen project file and all - the only extra thing required at your end is to update the app.config file to have the OR layer point to your database environment.

I will be shortly packaging the test case and sending it to support_at_llblgen_dot_com.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39928
Joined: 17-Aug-2003
# Posted on: 06-Apr-2006 12:50:48   

You forgot to add the attachment, could you please email it again? thanks simple_smile

Frans Bouma | Lead developer LLBLGen Pro
colinvella
User
Posts: 12
Joined: 28-Feb-2006
# Posted on: 04-May-2006 16:32:04   

I eventually 'solved' the problem by ditching Com+ (it was a bit of an overkill for this project) and resorted to using the LLGLGen Transaction object.

Everything seems ok although I do occasionally experience timeouts.. but this could be due to situations like re-accessing a row that was saved during a transaction before committing etc.