Partial Save of Entities with Child Entities using Entity Graphs

Oct 19, 2011 at 11:44 AM

I am having a requirement of saving a single entity out of a Domain Context. I am using Entity graphs to achieve it.

1. Whenever an entity with New Parent  is to be saved, i just add it to a temporary domain context and submit changes.

2. When I have a Parent entity with Modified state is to be saved, I use the Entity Graph to clone it and attach it to the temporary domain context. Then I will apply the state of the real entities in my graph to the entities in temporary domain context and then submit the changes.

How do i deal with the scenario when I am having a Parent entity which is in Modified State but the Child Entities of that parent is having New state. The ExtractState method of RIA Contrib is unable to extract the New state of an entity. So its returns in an Unmodified state which doesn't submit the changes to DB. 

Any help is appreciated.

Thanks

Syam

Developer
Oct 29, 2011 at 7:04 PM

Hi,

Thanks for using EntityGraph!

Could you provide the code of a minimal example that demonstrates the issue.

That would help me finding the cause of the issue and perhaps fixing it.

Thanks.

 

Kind regards,

Merijn

Oct 31, 2011 at 12:40 PM

 

Hi Merijin,

Thanks for your reply. 

I will explain my scenario. I am having a master table 'Task' and a child table 'TimeEntry'. I created an entity Graph for the same:

EntityGraphShape shape = new EntityGraphShape()

                            .Edge<Task, TimeEntry>(t => t.TimeEntries);

                                                   

Then on the event of saving a single entity, i do the following:

 private void DoPartialSave(Task task, EntityGraph graph)

{

  TasksDomainContext tempContext = new TasksDomainContext();

//If Parent entity is New or Detached, Add the parent to the temporary context 

            if (task.EntityState == EntityState.New || task.EntityState == EntityState.Detached)

            {

                addingNewTask = true;

                tempContext.Tasks.Add(task);

 

                //Add the childs in the parent to the temporary context

                foreach (TimeEntry entry in timeEntries)

                {

                    if (entry == null)

                    {

                         //Add the dummy child with relationship

                        TimeEntry newEntry = new TimeEntry();

                        newEntry.Task = task;

                        tempContext.TimeEntries.Add(newEntry);

                    }

 

                    else

                    {

                        tempContext.TimeEntries.Add(entry);

                    }

                }

              

            }

             else

            {

               //Clone the Entity graph and add attach it to graph by applying the real state. Child with New state will come out as Unmodified.

            tempContext.CloneEntityGraphAndAttach(graph);

            }

 

          //Submit temporary context to database

            tempContext.SubmitChanges(submitOperation =>

                {

                    if (submitOperation.HasError)

                    {

                        //Handle error

                    }

 

  //If a new parent is being added, add the clone of the graph to original context

                   if (addingNewTask)

                   {

                     context.Tasks.Add(graph.Clone());

                   }

                    else

                   {

                        context.ApplyEntityGraphState(tempContext.Tasks.First().EntityGraph(shape));

                    }

}

 

The definitions for CloneEntityGraphAndAttach and ApplyEntityGraphState:

 

public static void CloneEntityGraphAndAttach(this DomainContext contextToAttach,           EntityGraph graph)

        {

            Entity clone = graph.Clone();

            // Attach a clone of the entity graph to the temporary domain context

            contextToAttach.EntityContainer.GetEntitySet(clone.GetType()).Attach(clone);

 

            // Apply the state of each entity to the cloned entity

            foreach (Entity realEntity in graph)

            {

                    ApplyStateToMatchingEntity(contextToAttach, realEntity);

            }

        }

 

public static void ApplyEntityGraphState(this DomainContext contextToApplyState, EntityGraph graph)

        {

            foreach (Entity entity in graph)

            {

                // Apply the state of the cloned entity to the real one....

                ApplyStateToMatchingEntity(contextToApplyState, entity);

            }

         }

 public static void ApplyStateToMatchingEntity(this DomainContext domainContextToSearch, Entity realEntity)

        {

             FindMatchingEntity(domainContextToSearch, realEntity)

                 .ApplyState(realEntity.ExtractState (ExtractType.OriginalState), realEntity.ExtractState(ExtractType.ModifiedState));

        }

 public static Entity FindMatchingEntity  ( this DomainContext domainContextToSearch, Entity realEntity )

        {

            // Find the entity set holding the clone

            EntitySet entitySet = domainContextToSearch.EntityContainer

                .GetEntitySet(realEntity.GetType());

 

            // Find the clone within this entity set...

            IEnumerator enumerator = entitySet.GetEnumerator();

             // Find the Key property for the Entity

            PropertyInfo keyProperty = null;

            PropertyInfo[] properties = entitySet.EntityType.GetProperties();

            foreach (PropertyInfo propertyInfo in properties)

            {

                foreach (Attribute attribute in entitySet.EntityType

                    .GetProperty(propertyInfo.Name).GetCustomAttributes(false))

                {

                    if (attribute.GetType() == typeof(KeyAttribute))

                    {

                        keyProperty = propertyInfo;

                        break;

                    }

                }

            }

             // Use the key property to find the clone with the same Id

            while (enumerator.MoveNext())

            {

                Entity candidate = enumerator.Current as Entity;

                 if ((int)keyProperty.GetValue(candidate, null) ==

                    (int)keyProperty.GetValue(realEntity, null))

                {

                    return candidate;

                }

            }

             return null;

        }

 I hope this is enough. Please revert to me if you have any doubts on this code.

Developer
Nov 12, 2011 at 9:04 PM

I have improved the Clone method in two ways:

  • It now uses the ApplyState/ExtractState methods from entity tools. These will clone the original state from cloned entities whenever possible.
  • There is an additional clone method that takes a domain context as argument. This method will add/attach cloned entities according to the state of the original entities. The result is that cloned entities will end up the given domain context and have the same EntityState as their source. The provided domain context cannot be the same as the original (it will throw an exception then). This method is useful to duplicate an entity graph in another domain context.

I guess that you want to use this new Clone method. It's new so it may not fully work, but you should be able to do something like thisL

 

private void DoPartialSave(Task task, EntityGraphShape shape)
{
    TasksDomainContext tempContext = new TasksDomainContext();
    task.Clone(tempContext, shape);
    tempContext.SubmitChanges();
}

I've created a new NuGet package containing the changes to EntityGraph, so you can directly make use of it. Of course, the changes are also available through subversion.

Please le me know if this is working for you.

 

Nov 13, 2011 at 6:47 AM

Hi Merijn

Thanks a lot for this.  I tried this out but I am facing an issue. 

In my case, I have the Task Entity as parent and TimeEntry entity as child. I am taking a Task entity (With TaskId=1) which has more than one child TimeEntry entity. Now I am adding one more TimeEntry entity to it. Now when I am cloning the Task Entity to the tempContext, it throws an exception : "An entity with the same identity already exists in this EntitySet."

This also happens when I modify the parent and don't add any child TimeEntry to the Task entity. 

 

 IEnumerable<Task> taskList = context.Tasks.Where(t => t.TaskId == 1);
                     Task task = taskList.First();
                     task.TaskName = "Modified Task 1";
                     task.Description = "Modified Task 1";

  TimeEntry newEntry1 = new TimeEntry
                     {
                         StartTime = DateTime.Now,
                         EndTime = DateTime.Now + TimeSpan.FromHours(5),
                         Task = task,
                         Description = "Partial Save 44"
                     };
 context.Tasks.Add(newTask);
 NewPartialSave(shape, task);

private void NewPartialSave(EntityGraphShape shape, Task task)
        {

            TasksDomainContext tempContext = new TasksDomainContext();
            task.Clone(tempContext, shape);

            tempContext.SubmitChanges(submitOperation =>
                {
                    if (submitOperation.HasError)
                    {
                    }

                }, null);
        }

 

I debugged your source code and found that this exception is thrown in the CloneDataMembersIntoDomainContext method.

 

 private TClone CloneDataMembersIntoDomainContext<TClone>(DomainContext context, TClone source) where TClone : Entity
        {
            // Create new object of type T (or subtype) using reflection and inspecting the concrete
            // type of the entity to copy.
            TClone clone = (TClone)Activator.CreateInstance(source.GetType());
            var entitySet = context.EntityContainer.GetEntitySet(source.GetType());
            if(source.EntityState == EntityState.Unmodified || source.EntityState == EntityState.Modified)
            {
                entitySet.Attach(clone); --->  When the second unmodified Child entity is attached, it throws an exception
            }
            var originalState = source.ExtractState(ExtractType.OriginalState);
            var modifiedState = source.ExtractState(ExtractType.ModifiedState);

            clone.ApplyState(originalState, modifiedState);

            if(source.EntityState == EntityState.New)
            {
                entitySet.Add(clone);
            }
            return clone;
        }

I hope I made myself clear.
Thanks
Syam

 

 

Developer
Nov 13, 2011 at 1:37 PM

Hi,

I've difficulties in reproducing the error you're describing. So let's try to get to a minimal example that fails for you. From your description I understand that the following will raise the exception:

TasksDomainContext context = new TasksDomainContext();
var task = new Task{ Id = 1 };
context.Tasks.Attach(task);
context.TimeEntries.Attach(new TimeEntry{Id = 1, TaskId = 1});
context.TimeEntries.Attach(new TimeEntry{Id = 2, TaskId = 1});

task.TaskName = "Modified Task 1";

TasksDomainContext tempContext = new TasksDomainContext();

var shape = new EntityGraphShape().Edge<Task, TimeEntry>(t => t.TimeEntries);
task.Clone(tempContext, shape); // Does this throw an exception?

This example attaches a task with a given Id and two TimeEntries which both are linked (by means of 'TaskId = 1') to the task object.
This yields an object context with 3 entities which are all in the 'EntityState.Unmodified' state.

Next I change the state object so it entered the 'EntityState.Modified' state.

Finally, I instantiate an entity graph and make a clone of it.

Does this fragment leads to the exception you mentioned? If not, could you adapt this fragment such that it will throw the exception?

Thanks!

Nov 13, 2011 at 3:25 PM

Hi Merijn,

Not exactly. I will replicate my exact scenario. Before doing any operation, my database is having a Task entity with TaskId =1 and 3 TimeEntry entities (TimeEntry Id =1,2,3) belonging to TaskId=1.

Now in my code, on click of PartialSave button, i fetch the Task which has TaskId=1 and modify it. I add a new TimeEntry entity as a child to that Task. In this scenario I get the aforementioned exception. The exception comes when the EntitySet tries to attach more than one TimeEntry clone in your CloneDataMembersIntoDomainContext method. In your code, EntitySet attaches the clone entity when source = TimeEntry: 1, but when it tries to attach clone for  source = TimeEntry: 2, it throws the exception.

Even if i remove the adding a new TimeEntry entity i.e. just modifying Task with TaskId=1 and doing Partial Save also throws this exception. In this case also, The TimeEntry entities (TimeEntry Id =1,2,3) is part of the shape/graph as these belong to TaskId=1.

EntityGraphShape shape = new EntityGraphShape()
                                                    .Edge<Task, TimeEntry>(t => t.TimeEntries)
                                                    ;
private void OnPartialSaveCommand()
        {
 LoadOperation<Task> lop =  context.Load(context.GetTasksQuery());
             lop.Completed += (s,o) =>
                 {
                     IEnumerable<Task> taskList = context.Tasks.Where(t => t.TaskId == 1);
                     Task task = taskList.First();
                     task.TaskName = "Modified Task 1";
                     task.Description = "Modified Task 1";

TimeEntry newEntry1 = new TimeEntry
                     {

                         StartTime = DateTime.Now,
                         EndTime = DateTime.Now + TimeSpan.FromHours(5),
                         Task = task,
                         Description = "Partial Save 1"
                     };
PartialSave(shape, task);
               };
        }


        private void NewPartialSave(EntityGraphShape shape, Task task)
        {

            TasksDomainContext tempContext = new TasksDomainContext();
            task.Clone(tempContext, shape);

            tempContext.SubmitChanges(submitOperation =>
                {
                    if (submitOperation.HasError)
                    {
                    }

                }, null);
        }

I hope you understood the scenario. 
Thanks
Syam

Developer
Nov 13, 2011 at 4:39 PM

I think that scenario is exactly what I tried to capture in my code example. The only thing I left out was adding the new TimeEntry because you indicated that it was not necessary for getting the exception.

I created two TimeEntries (not three as in your case, but that shouldn't make a difference) and connected them to the single Task entity (with Id 1). The domain context that I created now consists of three entities, each in an unmodified state. The TimeEntries collection of the single task entity contains the two attached TimeEntry entities. That should be identical to your query (except, as I explained, that I create only two TimeEntries). Next, I make a change to the single Task entities. Again, similar to your example. I skip the addition of a new TimeEntry (it is not needed acoording to your information). Finally, I create an entitygraph using the exact same shape as yours, and I clone the graph.

So my question is still, could you please check if my code fragment works for you or that it also generates an exception. Or do I miss something. Please let me know.

Nov 14, 2011 at 3:40 AM

Hi Merijn,

I tried the exact fragment of code you mentioned. It's throwing the same exception for me. 

For more information, I have decorated the TimeEntries attribute in Task Metadata class with 'Include' attribute and while fetching tasks i have included "TimeEntries". Also Lazy Loading is disabled for my entity model.

 [Include]
            public EntityCollection<TimeEntry> TimeEntries { get; set; }


public IQueryable<Task> GetTasks()
        {
            return this.ObjectContext.Tasks.Include("TimeEntries");
        }

Thanks
Syam

Nov 14, 2011 at 6:06 PM

Merijn,

I am having the exact same difficulty as syam.  I don't know if I can describe it any better. 

access Parent Entity in BaseContext.

Make some changes to parent or child.

Clone the Parent to ParentCopy

Create tempcontext

   now - if I ADD ParentCopy to tempcontext and SubmitChanges I get a 'new' Parent in the database complete with children

     ( this works fine in a SAVE-AS situation)

  if I  use Parent.Clone(tempcontext, graphshape)  i get the same "An entity with the same identity already exists in this EntitySet." error

  This also happens when I use the old clone method and then ATTACH to tempcontext and save.

What I need to happen is the modified Parent Entity to be "updated" in the database.  I am also trying to work out how to reflect the update in the base context.

Thanks for your help.

Darrell

 

 

 

Developer
Nov 14, 2011 at 6:44 PM

I was finally able to reproduce the issue. I already suspected my implementation and already had a solution in mind, but I simply couldn't reproduce the issue.

It turned out that my test entities were using client-side auto-generated keys. Hence, they were, by definition, always different and therefore the problem that you guys reported didn't occur at my site.

After realizing this, I changed my entities and I could then reproduce the issue.

I've committed the solution for the issue at riaservices.codeplex.com. You can read about the solution in the commit message. I've also pushed a new NuGet package.

Could you please let me know if it indeed solves your problem.

Thanks.

Nov 16, 2011 at 1:44 AM
Edited Nov 16, 2011 at 3:58 AM

Merjin, Thank you, thank you. I was able to mke partial saves with the method below

 1) I have two questions remaining, when I edit "only" the child object the "parent" entitystate is not changing to "modified".. 

 2) I am unsure of how to update the Entitystate in the original context.

I really appreciate your help.

 

public void DoPartialSave()
        {
            var shape = new EntityGraphShape().Edge<Spring, FinishNote>(spring => spring.FinishNotes);
            var context = new baseDomainContext();
            CurrentSpring.Clone(context, shape);

            context.SubmitChanges(submitOperation =>
            {
                if (submitOperation.HasError) { FlashMsg("Save Error"); }
                else
                {
                    FlashMsg("Saved " + CurrentSpring.PartNumber);
                    // Please can you suggest method to set the state in the original context to Unmodified
                }
            }, null);
        }

 

Nov 16, 2011 at 11:27 AM

Hi Merijn,

Thanks for the quick update. It works fine now. I hope the same clone method can be used to apply the state of the Temporary context to the Main context.

Thanks

Syam

Developer
Nov 17, 2011 at 11:53 AM

Entities implement the IChangeTracker interface. By calling 'AcceptChanges' on an entity, its entity state will be changed to 'Unchanged'. So after a partial save you can call 'AcceptChanges' on every entity in the entity graph to update their states. EntityGraph now also implements this interface, which means you can directly call 'AccepChanges' on a graph to accept the changes on the entities in the graph.

I've created a Blog about implementing partial save (http://riaservicescontrib.codeplex.com/wikipage?title=PartialSaveWithEntityGraph). Please read the disclaimer on that page because a partial save does not always work correctly.

@LightGreen: A parent's entity state is never changed when editing a child. However, if you have an EntityGraph that includes both entities, you can use the EntityGraph's HasChanges property. It will inspect the entity state of all entities in the graph.

Nov 17, 2011 at 1:34 PM

Hi Mdej,

Thanks a lot. I took the latest EntityGraph (EntityGraph.1.0.66283.0) from NuGet and tried using the EntityGraph.Clone method which takes Entity and DomainContext as argument as you have mentioned in the above blog. But I am not able to find the overload which takes these two arguments. Am i doing something wrong??

Also I am not able to get the AcceptChanges method of EntityGraph. I checked the source code of the latest drop (RiaServicesContrib-66282). I am able to find EntityGraph implementing IChangeTracking interface only for EntityGraph.Net and not for EntityGraph.Silverlight

Thanks

Syam

Developer
Nov 17, 2011 at 6:56 PM

@syammohan,

Thanks for reporting. The Blog incorrectly called the Clone() method with two arguments: var clone = graph.Clone(car, partialSaveCOntext);

This should be:

var clone = graph.Clone(partialSaveContext);
I've fixed it in the Blog.

The AccepChanges() method should be there. Are you perhaps missing a using statement ()?

The IChangeTracking interface is indeed implemented in EntityGraph.Net. Because it is located in the file IChangeTracking.shared.cs, its implementation gets copied to EntityGraph.Silverlight by Ria services.

Please let me know if this doesn't help.

Nov 19, 2011 at 11:44 PM

Merijn,

Thanks,  PartialSave is working very well for me and I am begining to understand what is happening with the implementation.  I'm now trying to come up with a "save-as" method.  Idealy I could then save the current entity as a new entity (new partnumber and ID).  Would it work like the following?

Clone current Entity into "New" Entity. so ID's can be set?

Change ID for Parent and Children (? ID's are read only)

modify partnumber (save-as name)

Save new Entity to temp contex

Attach? to original context 

Have you got a method for "save-as" also?

Dec 2, 2011 at 2:23 AM

Hi Merijn,

Thanks a lot for the modifications you did in a short span of time. Really appreciate it. I would like to ask one more thing. Is there a way to generate the shape of  EntityGraph from a database or entity framework model. As i am having 40 odd tables linked with a single parent entity, it's really a pain to define a proper shape for the EntityGraph.

 

Thanks

Syam

Developer
Dec 2, 2011 at 7:58 AM

@LightGreen: There is no save-as method because that requires some changes to EntityTools. However, copying is much easier than cloning because during a copy new entities (with new Id's) are created. So a save-as method for an entity "e" can be done like this:

  • Create an EntityGrapg for "e": var clone = new EntityGraph(e, your_shape);
  • Create a copy of the graph: var copy = clone.Copy();
  • Add the source element of the copy to your a temporary domain context: tmp_context.<entityset_of_e>.Add(copy.Source);
  • Call submit changes on this context: tmp_context.SubmitChanges();
  • Detach the copy from the tmp_context: copy.DetachEntityGraph(tmp_context.<entityset_of_e>);
  • Attach the source element of the copy to your actual domain context: context.<entityset_of_e>.Attach(copy.Source);

I think this will do the trick.

Developer
Dec 2, 2011 at 8:07 AM

@syammohan: Unfortunately there is currently no tool available for generating an entitygraph shape from a database or an entity framework model. What may or may not help you is the generic entity graph shape 'FullEntityGraphShape()'. This shape defines a complete graph. This means that it includes each and every association in your model. I'm not sure if it helps in your case because using it may result in an entity graph that connects all entities in your context, which will prevent a partial save.

In general, I find defining entity graph shapes useful because they contain domain knowledge that is not available elsewhere (i.e., not in your database or in your entity model) and they can be used at other places as well. So you can define several shapes and re-use them all over the place. You can even define them in a '.shared.cs' file to make them available at the server and the client side. Despite the initial work of defining them I experience graph shapes as useful artifacts in my programs.

Dec 15, 2011 at 2:46 PM

Hi Mdej,

Thanks for your suggestion. In my requirement, I have one Master Entity which has associations with 40 odd detail entities. My application can have multiple instances of Master Entity. I use Partial Save to save one of those Master Entity. In this scenario, will the FullEntityGraphShape help me. Could you please brief me when the FullEntityGraphShape can be used.

Thanks

Syam

Developer
Dec 15, 2011 at 7:52 PM

Hi Syam,

The FullEntityGraphShape defines an entity graph shape of all properties that have the AssociationAttribute defined. Effectively, this means that it will cover all navigation properties of your entities. So, if you use it to make, for example, a copy of your master entity, all detail entities will be copied as well. If any of these has additional navigation properties, the corresponding entities are also copied. EntityGraph operations are robust for cyclic paths.

 

Does this answer your question?

Kind regards,

Merijn

Dec 21, 2011 at 12:33 PM

Hi Merijn,

I tried using FullEntityGraphShape. I used it in the following way :

 FullEntityGraphShape newShape = new FullEntityGraphShape();

 EntityGraph newGraph = task.EntityGraph(newShape);

But by doing this, my graph is having entire Entities which are present in my Domain Context including multiple 'Task' entities and its child entities. Is there any way to have all the associated entities of Task in my graph.

Thanks

Syam

Developer
Dec 22, 2011 at 7:52 AM

Hi Syammohan,

As you can read in my previous post, the FullEntityGraphShape will include each and every association defined in your model. And, yes, this can result in an entity graph that contains all entities that are present in your domain context. So FullEntityGraphShape must be used with care.

Apparently, in your case it is not the right way to go. This means that you have to define an entity graph shape your self with edges for all associations that you want to include in the entity graph. Instructions on how to do this can be found here.

Succes!

Merijn

Mar 20, 2012 at 12:58 PM

Hi Merijn,

I came across a problem while using the Partial Save approach you suggested.

In my scneario, the entities in the clone gets truncated after i submit changes of the temporary context used in Partial Save. This leads to an error in synchronise method: "Source graph entity graph must be a copy or a clone of present entity graph."

 

My graph shape is : 

EntityGraphShape shape = new EntityGraphShape()
                                                    .Edge<JobRequest, DirectMailRequest>(j => j.DirectMailRequests)
                                                    .Edge<DirectMailRequest, DirectMailComponent>(d => d.DirectMailComponents)
                                                    .Edge<JobRequest, JobRequestDigitalResource>(j => j.JobRequestDigitalResources)
                                                    .Edge<JobRequestDigitalResource, DigitalResource>(jdr => jdr.DigitalResource)
                                                    .Edge<DigitalResource, DigitalAssetFile>(d => d.DigitalAssetFile);

private void DoPartialSave(EntityGraphShape shape, JobRequest job)
        {
            JobTrackerContext tempContext = new JobTrackerContext();


            EntityGraph graph = new EntityGraph(job, shape);

            var clone = graph.Clone(tempContext);


            tempContext.SubmitChanges(submitOperation =>
            {
                if (submitOperation.HasError)
                {
                }
                else
                {
                    graph.Synchronize(clone);  //Here it throws errors as the clone here is not same as graph
                   
                }

            }, null);
        }

Thanks
Syam

 

 

Developer
Mar 20, 2012 at 9:25 PM

Hi Syam,

Your code looks correct. Did you find out which entities disappear in the clone after you called submitchanges?

Kind regards,

Merijn

Mar 21, 2012 at 3:24 AM
Edited Mar 22, 2012 at 10:32 AM

Hi Merijn,

Thanks for your quick response. The entities getting disappeared are DigitalResource and DigitalAssetFile.

In my database structure, JobRequestDigitalResource is many to many relationship mapping table for the entities JobRequest and DigitalResource. So this relationship cant be considered as a typical parent-child relationship. Will this have anything to do with the disappearing of entities. Here as you may see, JobRequest is the top-level parent table and all other tables are related to it in some way or the other. 

Let me know if you need any further information.

Thanks

Syam

Developer
Mar 25, 2012 at 7:58 PM

Hi Syam,

is it possible to send me your data model classes that are involved in the entity graph. that would help me in setting up a test to reproduce your issue.

Thanks  in advance,

Merijn

Mar 26, 2012 at 6:02 AM

Hi Merijn,

Can you mail me your e-mail address to wickedsyam@gmail.com so that i can send you a sample demonstrating the issue?

Thanks

Syam

Developer
Mar 26, 2012 at 2:40 PM
Edited Mar 26, 2012 at 8:09 PM

Hi Syam,

Thanks for sharing your code.

As a workaround create yet another entitygraph in your callback method and use that one to synchronize your initial entitygraph, like this:

 

var clone2 = new EntityGraph(clone.Source, shape);
graph.Synchronize(clone2);

 

Add:

The thing is that with newly created link entities (such  as JobRequestDigitalResource), updating the foreign keys during a call to SubmitChanges is not an atomic action. As a result, there is a short time span in which one of the associations is null and will be removed from the corresponding entitycollection at the other end of the association. EntityGraph monitors collection changed events and will remove an entity from the graph if it is removed from an entity collection. Later, RIA services will update the association in the link entity, but then it is no longer tracked by EntityGraph and therefore it is no longer part of the entity graph.

The proposed workaround simply creates a new instance of the entity graph after SubmitChanges completed. It will do a fresh scan of the entities based on the entity graph shape. As a result, all entities are now correctly included in this entity graph instance.

At the moment I've not yet an idea for a solution to this problem. Therefore, I propose to use the workaround.

Good luck,

Merijn

Mar 27, 2012 at 8:03 AM

Hi Merijn,

Thanks for your reply. The workaround seems to have fixed the problem. Thanks a lot for this quick fix. Will let you know if i face any issues.

Thanks

Syam

Apr 4, 2012 at 12:07 PM

Hi Merijn,

Your work around for my issue works like charm.

It may be a bit over the top to ask this. Your Partial Save by EntityGraph disclaimer clearly states that Partial Save of entities will not work if any of the entities is removed from the graph due to change in foreign key of the entity. Do you have any work around or suggestions on how to approach this. May be just an idea, on which i can work up.

Thanks

Syam

Developer
Apr 14, 2012 at 7:58 PM

Hi Syam,

Sorry for my late reply, I've been pretty busy lately...

EntityGraph simply doesn't keep track of "old" members. That is, it keeps track of changes of entities that are in the graph and not of entities that are not in the graph. I cannot easily foresee the consequences of keeping track of such entities (for example w.r.t. garbage collection). I welcome any suggestions for this issue, so feel free to come up with ideas or an implementation.

Kind regards,

Merijn

May 18, 2012 at 2:21 AM
Edited May 18, 2012 at 2:35 AM

Hi Merijin

I've been working on tracking old members. See "Implementing Partial Save of deletions (plus Undo)" http://riaservicescontrib.codeplex.com/discussions/356233

Regards,
Peter