Merging entity problem

Posts   
 
    
arschr
User
Posts: 894
Joined: 14-Dec-2003
# Posted on: 05-Sep-2008 19:36:36   

I am using v2.6 20080903 adapter. My entities are brought to the client application using WCF.

I first retrieve a number of the base entity into a EntityCollection. When the user requests more information I find the primary key value of the entity in this collection and ask my service for detailed information. That service method returns another occurrence of that entity (same primary key) but with a number of child collections filled in.

I am having a problem merging this second occurrence with the first. I thought that by adding both to the same context the child collections would be added to the first entity during the uniqueing process, when I view the variables holding the two occurrences in the debugger this doesn't seem to be happening.

When I try to add the child collections contents to the first entities child collections using addrange I get an exception saying the collection has changed can't iterate the collection.

What is the "best" way to merge the child collections from the second entity into the first entity?

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 05-Sep-2008 21:01:13   

Hi Al. Mmm disappointed Could you please post some relevant code?

David Elizondo | LLBLGen Support Team
arschr
User
Posts: 894
Joined: 14-Dec-2003
# Posted on: 05-Sep-2008 22:06:09   

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Cgm.DataLayer.EntityClasses;
using Cgm.DataLayer.FactoryClasses;
using Cgm.DataLayer.HelperClasses;
using SD.LLBLGen.Pro.ORMSupportClasses;

namespace Cgm.DataService.Client
{
    public class OperationsDataContainer
    {
        private readonly Context _context;
        public OperationsDataContainer()
        {
            _context = new Context();
            _invoiceActivityLogCollection = new EntityCollection(new InvoiceActivityLog01EntityFactory());
            _invoiceActivityLogCollection.DoNotPerformAddIfPresent = true;
            _context.Add(_invoiceActivityLogCollection);

        }


        private IEntityCollection2 _invoiceActivityLogCollection;
        public IEntityCollection2 InvoiceActivityLogCollection
        {
            get { return _invoiceActivityLogCollection; }
            set
            {
                _context.Add(value);
                    _invoiceActivityLogCollection.AddRange(value);
            }
        }
        public void MergeUpdates(IEntityCollection2 invoiceActivityLogCollection)
        {
            
            _context.Add(invoiceActivityLogCollection);
            for(int i = 0;i<invoiceActivityLogCollection.Count;i++)
            {
                InvoiceActivityLog01Entity entityRefreshed = invoiceActivityLogCollection[i] as InvoiceActivityLog01Entity;
                Debug.Assert(entityRefreshed != null);
                Debug.WriteLine(string.Format("{0}:{1}:{2}", entityRefreshed.InvoiceId, entityRefreshed.IsDirty, entityRefreshed.IsNew));
                InvoiceActivityLog01Entity entityRefreshedFromContext = _context.Get(entityRefreshed) as InvoiceActivityLog01Entity;
                Debug.Assert(entityRefreshedFromContext != null);
                Debug.WriteLine(string.Format("{0}:{1}:{2}", entityRefreshedFromContext.InvoiceId, entityRefreshedFromContext.IsDirty, entityRefreshedFromContext.IsNew));
                List<int> matches = _invoiceActivityLogCollection.FindMatches(InvoiceActivityLog01Fields.InvoiceId == entityRefreshedFromContext.InvoiceId);
                Debug.Assert(matches.Count==1);
                InvoiceActivityLog01Entity entityOriginal = _invoiceActivityLogCollection[matches[0]] as InvoiceActivityLog01Entity;
                Debug.Assert(entityOriginal != null);
                Debug.WriteLine(string.Format("{0}:{1}:{2}", entityOriginal.InvoiceId, entityOriginal.IsDirty, entityOriginal.IsNew));
                Debug.WriteLine(string.Format("{0}:{1}", entityRefreshedFromContext.GetHashCode(), entityOriginal.GetHashCode()));

                try
                {

                    entityRefreshedFromContext.InvoiceActivityLog02.AddRange(entityRefreshed.InvoiceActivityLog02);
                    entityRefreshedFromContext.InvoiceComponent.AddRange(entityRefreshed.InvoiceComponent);
                    entityRefreshedFromContext.InvoiceActivityLog.AddRange(entityRefreshed.InvoiceActivityLog);
                }
                catch (Exception except)
                {
                    Debug.WriteLine(except);
                }
            }
            
            
        }
        
    }
}

The OperationsDataContainer creates a context and a collection when it is created.

On the first retrieval, I set the collection using the property InvoiceActivityLogCollection

Then on the individual entity fetc I call MergeUpdates.

The codes pretty messy right now because I expected it to just need the context, and I've been messing with it to try to get it to work.

a IEntityView is created off of InvoiceActivityLogCollection and bond to a Devx winforms grid. I'd like the child collections data to be available though it id fetched.

arschr
User
Posts: 894
Joined: 14-Dec-2003
# Posted on: 06-Sep-2008 15:45:30   

It's my impression that the context should take care of this merging, but either it's not or I'm using it wrong. In fact I thought I'd done this before.

What I'm doing now is different than this next link, as I'm fetching a common parent with prefetches in my second fetch.

http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=12536

What I am doing is described here

http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=10910

Where you say it should work, and in fact, I think, it has worked in the past.

The difference now are 1 LlblGen versions and I am now disconnected from the adapter fetch, so I am working with the context in the client. This means the collections are added to the context after the fetch.

Anyway, I have A and A prime same primary key. A prime has filled child collections. how do I get them into A?

arschr
User
Posts: 894
Joined: 14-Dec-2003
# Posted on: 06-Sep-2008 15:57:25   

Also at the end of this thread I think Frans is saying this should work.

http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=6753

arschr
User
Posts: 894
Joined: 14-Dec-2003
# Posted on: 06-Sep-2008 16:10:28   

My A prime occurrence has a different object id but the same primary key than A (as expected) before it is added to the context. It is still different after the add to the context.

So what is the proper way to get the child entities out of A prime and into A?

arschr
User
Posts: 894
Joined: 14-Dec-2003
# Posted on: 06-Sep-2008 16:30:06   

I ended up writeing a routine for each collection move like:

private void MoveInvoiceActivityLog(InvoiceActivityLog01Entity entityRefreshed, InvoiceActivityLog01Entity entityOriginal)
        {
            int count = entityRefreshed.InvoiceActivityLog.Count;
            for(int j = count-1;j>= 0;j--)
            {
                InvoiceActivityLogEntity entity = entityRefreshed.InvoiceActivityLog[j];
                entityRefreshed.InvoiceActivityLog.RemoveAt(j);
                entityOriginal.InvoiceActivityLog.Add(entity);
            }
        }

Is there a more generic way to do this? This line:

entityOriginal.InvoiceActivityLog.Add(entity);

won't accept an IEntity2, so maybe the answer to my question is to make it a generic method?

arschr
User
Posts: 894
Joined: 14-Dec-2003
# Posted on: 06-Sep-2008 16:46:01   

I'm using


private void MoveCollection<TEntity>(EntityCollection<TEntity> source, EntityCollection<TEntity> destination) where TEntity : EntityBase2,IEntity2
        {
            int count = source.Count;
            for (int j = count - 1; j >= 0; j--)
            {
                TEntity entity = source[j];
                source.RemoveAt(j);
                destination.Add(entity);
            }
        }

perhaps this or a better version could be called inside the context?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39865
Joined: 17-Aug-2003
# Posted on: 07-Sep-2008 12:18:17   

arschr wrote:

My A prime occurrence has a different object id but the same primary key than A (as expected) before it is added to the context. It is still different after the add to the context.

So what is the proper way to get the child entities out of A prime and into A?

(A' == A prime)

I think I understand what the confusion is: when you do A = context.Get(A'), you will get back the data in A' into the A entity, but NOT child entities. So A still has no child entities and A' does.

The simplest solution I see is that you replace A with A' in the collection where A is located in. The thing is that although you can for example replace the collection with the child entities in A with the one in A', the child entities will refer to A', not A. The main problem is: if X references Y and you want to replace Y with a different object, you can only do that by altering the references to Y, i.e. change it in X.

The question now is of course... is this possible in your application?

Frans Bouma | Lead developer LLBLGen Pro
arschr
User
Posts: 894
Joined: 14-Dec-2003
# Posted on: 07-Sep-2008 12:46:59   

(A' == A prime)

I think I understand what the confusion is: when you do A = context.Get(A'), you will get back the data in A' into the A entity, but NOT child entities. So A still has no child entities and A' does.

The simplest solution I see is that you replace A with A' in the collection where A is located in. The thing is that although you can for example replace the collection with the child entities in A with the one in A', the child entities will refer to A', not A. The main problem is: if X references Y and you want to replace Y with a different object, you can only do that by altering the references to Y, i.e. change it in X.

The question now is of course... is this possible in your application?

I'll have to try it, but I think it will mess up the binding in the ui.

you replace A with A' in the collection where A is located in

If you are saying remove A from the collection and add A' to the collection, I don't expect it will play nice with binding.

At least in my application, where the child collections are 1 to 20 elements long, my solution, (removing entities from the A' collection set and adding them to the A collection set, seems to work, (at least in my controlled dev environment).

I would think this would be pretty often needed, so framework support would be good.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39865
Joined: 17-Aug-2003
# Posted on: 08-Sep-2008 11:01:54   

arschr wrote:

(A' == A prime)

I think I understand what the confusion is: when you do A = context.Get(A'), you will get back the data in A' into the A entity, but NOT child entities. So A still has no child entities and A' does.

The simplest solution I see is that you replace A with A' in the collection where A is located in. The thing is that although you can for example replace the collection with the child entities in A with the one in A', the child entities will refer to A', not A. The main problem is: if X references Y and you want to replace Y with a different object, you can only do that by altering the references to Y, i.e. change it in X.

The question now is of course... is this possible in your application?

I'll have to try it, but I think it will mess up the binding in the ui.

you replace A with A' in the collection where A is located in

If you are saying remove A from the collection and add A' to the collection, I don't expect it will play nice with binding.

At least in my application, where the child collections are 1 to 20 elements long, my solution, (removing entities from the A' collection set and adding them to the A collection set, seems to work, (at least in my controlled dev environment).

I would think this would be pretty often needed, so framework support would be good.

Removing and adding is indeed what could solve this, as you pull 1 object from the service. I'll add to the todo list a way to make this more easier. Not sure when / if this will be added though, as such a feature obviously should support more scenario's than this one.

Frans Bouma | Lead developer LLBLGen Pro