Unit Of Work error

Posts   
 
    
Angus
User
Posts: 44
Joined: 08-Jul-2005
# Posted on: 01-Dec-2005 17:02:38   

I get the following error when I try to save a UnitOfWork Object

Type : System.ArgumentException, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Message : Item has already been added. Key in dictionary: "772f27ac-6386-48ad-815e-b9237c246ab6" Key being added: "772f27ac-6386-48ad-815e-b9237c246ab6" Source : mscorlib Help link : ParamName : TargetSite : Void Insert(System.Object, System.Object, Boolean) Stack Trace : at System.Collections.Hashtable.Insert(Object key, Object nvalue, Boolean add) at System.Collections.Hashtable.Add(Object key, Object value) at SD.LLBLGen.Pro.ORMSupportClasses.UnitOfWork2.ConstructSaveProcessQueues() at SD.LLBLGen.Pro.ORMSupportClasses.UnitOfWork2.Commit(IDataAccessAdapter adapterToUse, Boolean autoCommit) at CDT.DALController.EnumerationManager.SaveEnumeration(UnitOfWork2 unitOfWork) in d:\src\mindtree\cdt\cdt.dalcontroller\enumeration\enumerationmanager.cs:line 280 at CDT.DALController.EnumDataTypeController.SaveEnumDataType() in d:\src\mindtree\cdt\cdt.dalcontroller\enumeration\enumerationcontroller.cs:line 180 at CommsDataTool.EnumerationEditForm.ultraButtonSave_Click(Object sender, EventArgs e) in d:\src\mindtree\cdt\commsdatatool\enumerations\enumerationeditform.cs:line 691

The code that setup the UnitOfWork worked fine in version 1.0.2004.*

I have since upgraded to 1.0.2005.1 (Nov 8th) and I get an exception inside of the LLBLGen dll.

Has anyone else seen this?

Thanks for any help you have.

Chris

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 01-Dec-2005 18:12:08   

The only reason I can think of is that an entity is added individually to the UoW for saving and is also present in a collection which has been added for saving. Could you check that for me please? I'll see if I can harden the code a bit against that particular scenario, as looking at the code, it seems to me that's the only possible cause of it:


/// <summary>
/// Constructs the save process queues for insert and update actions. These queues are constructed from the entities added to this UoW 
/// for save either individually or in a collection. Call this method to determine what the sequence will be for the insert and update
/// actions executed during Commit(). Commit() uses this method as well as well as the serialization/deserialization logic, to avoid
/// sending large object graphs with few changes.
/// </summary>
public void ConstructSaveProcessQueues()
{
    // clear current queues
    _entitiesToInsert = new ArrayList(_entitiesToSave.Count);
    _entitiesToUpdate = new ArrayList(_entitiesToSave.Count);

    Hashtable inProcess = new Hashtable(512);
    Hashtable recursed = new Hashtable(512);
    Hashtable inQueue = new Hashtable(512);
    Hashtable postponed = new Hashtable(512);

    // some entities are added for recursive saves, others aren't. Use a per-entity queue build action.
    ObjectGraphUtils graphUtils = new ObjectGraphUtils();
    ArrayList elementsToProcess = new ArrayList(_entitiesToSave);
    Hashtable tmpObjectIDsToSave = new Hashtable(_objectIDsToSave);
    foreach(UnitOfWorkCollectionElement2 currentElement in _collectionsToSave)
    {
        foreach(IEntity2 currentEntity in currentElement.Collection)
        {
            if(!tmpObjectIDsToSave.ContainsKey(currentEntity.ObjectID))
            {
                UnitOfWorkElement2 newElement = new UnitOfWorkElement2(currentEntity, null, currentElement.Refetch, currentElement.Recurse);
                tmpObjectIDsToSave.Add(currentEntity.ObjectID, newElement);
                elementsToProcess.Add(newElement);
            }
        }
    }

    foreach(UnitOfWorkElement2 element in elementsToProcess)
    {
        if(element.Recurse)
        {
            graphUtils.DetermineActionQueues(element.Entity, element.Restriction, ref _entitiesToInsert, ref _entitiesToUpdate, ref inProcess, 
                    ref recursed, ref inQueue, ref postponed, element.Refetch);
        }
        else
        {
            if(element.Entity.IsNew)
            {
                _entitiesToInsert.Add(new ActionQueueElement(element.Entity, null, element.Refetch));
            }
            else
            {
                if(element.Restriction!=null)
                {
                    _entitiesToUpdate.Add(new ActionQueueElement(element.Entity, element.Restriction, element.Refetch));
                }
                else
                {
                    _entitiesToUpdate.Add(new ActionQueueElement(element.Entity, element.Entity.GetConcurrencyPredicate(ConcurrencyPredicateType.Save), element.Refetch));
                }
            }
            inQueue.Add(element.Entity.ObjectID, null);  // <<<<<< here it can go wrong
        }
    }
}

Frans Bouma | Lead developer LLBLGen Pro
Angus
User
Posts: 44
Joined: 08-Jul-2005
# Posted on: 01-Dec-2005 19:48:50   

Otis wrote:

The only reason I can think of is that an entity is added individually to the UoW for saving and is also present in a collection which has been added for saving. Could you check that for me please? I'll see if I can harden the code a bit against that particular scenario, as looking at the code, it seems to me that's the only possible cause of it:

The code that is being added to the UOW is a colleciton of items in a list that can be placed in a specifc order by the user. When the user selects an item in the list, he clicks a button to move the item up or down the list. The code here shows my "MoveLiteralDown" method (The move up code is the same with the indexes being changed in reverse order). After each call I add the collection to be saved, because a single move will update at two records.
Then entire UOW consists of the entity class called "EnumDataType" and it's related "EnumLiterals" collection. The code that is adding the items to the UOW will add the "EnumDataType" as a single entity and the "EnumLiterals" as a collection, but does not add the "EnumDataType" to any collection.

Here is a sample of the code I am calling...

        public void  MoveLiteralDown( int ndx)
        { 
            if(ndx >= 0 && ndx < CurrentEnumDataType.EnumLiterals.Count -1)
            {
                CurrentEnumDataType.EnumLiterals.SupportsSorting = true;
                CurrentEnumDataType.EnumLiterals.Sort((int)EnumLiteralFieldIndex.LiteralValue,System.ComponentModel.ListSortDirection.Ascending);

                CurrentEnumDataType.EnumLiterals[ndx].SetNewFieldValue((int)EnumLiteralFieldIndex.LiteralValue,ndx+1);
                CurrentEnumDataType.EnumLiterals[ndx+1].SetNewFieldValue((int)EnumLiteralFieldIndex.LiteralValue,ndx);
                
                m_UnitOfWork.AddCollectionForSave(CurrentEnumDataType.EnumLiterals); 
                RaiseEnumLiteralDataChagnedEvent( );
            }
        }

I have also tried adding the following line of code before the call to "AddCollectionForSave"


            m_UnitOfWork.RemoveCollectionFromUoW(CurrentEnumDataType.EnumLiterals);

but I still get the error.

This code has worked perfect on the older version of LLBLGen.

Thanks for the help.

Chris

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 01-Dec-2005 21:20:23   

I think, reading what you're doing, that the issue I described is indeed what's the cause. There are more paths which can lead to a dupe in the queue so the addition clashes.

Can I mail you a test build of the latest runtime libs to see if it works if I test if the entity isn't yet in the queue?

Frans Bouma | Lead developer LLBLGen Pro
Angus
User
Posts: 44
Joined: 08-Jul-2005
# Posted on: 01-Dec-2005 22:20:06   

Otis wrote:

I think, reading what you're doing, that the issue I described is indeed what's the cause. There are more paths which can lead to a dupe in the queue so the addition clashes.

Can I mail you a test build of the latest runtime libs to see if it works if I test if the entity isn't yet in the queue?

Yes, I would be happy to test that out.

Chris

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 01-Dec-2005 22:56:36   

Angus wrote:

Otis wrote:

I think, reading what you're doing, that the issue I described is indeed what's the cause. There are more paths which can lead to a dupe in the queue so the addition clashes.

Can I mail you a test build of the latest runtime libs to see if it works if I test if the entity isn't yet in the queue?

Yes, I would be happy to test that out.

Chris

They're on their way. I hope your emailaddress can receive .zip attachments simple_smile You should see it in a minute or so.

Frans Bouma | Lead developer LLBLGen Pro