locked
Need help figuring out how to remove deleted associated Entities RRS feed

  • Question

  • I have an Entity that I return in RIA Services called a Job with the following Metadata:

            internal sealed class JobMetadata
            {
                // Metadata classes are not meant to be instantiated.
                private JobMetadata()
                {
                }
    
                [Display(AutoGenerateField = false)]
                [Include]
                public EntityCollection<Assignment> Assignments { get; set; }
            }

    And when I load my Job, I always include "Assignments".  Problem that I have run into is when I perform an operation server-side called CopyTemplate which causes lots of changes to the Assignments, this is not reflected at all in the returned entity.  I have even tried removing the associated items with a Detach, by comparing items in the DomainContext to the returned entity's list, but it seems this list has already been merged with the local cache.

    Any ideas on how to deal with this, and not revert to a call for each entity type?  I tried working with the Composition attribute, but that really seemed problematic.


     

    Thursday, August 12, 2010 6:11 PM

Answers

  • It might be easier if you just clear the EntityContainer (DomainContext.EntityContainer.Clear) and then reload your entities. Personally, I just do the CopyTemplate work on the client instead of doing it on the server. That makes sure that everything stays in sync and, frankly, I find it easier to clone entities using RIA Services Contrib then trying to do it using the server APIs.
    Tuesday, August 17, 2010 2:33 PM

All replies

  • Hi Rposener,

    Would you please compare your code segment with this sample?

    Best regards,

    Jonathan 

    Monday, August 16, 2010 4:29 AM
  • this does not address my issue, unless you are suggesting that I change my entity to a composition instead of just using an include attribute?  Please let me know if that is what you are suggesting.  I really do not want to do this, because my child entities can take on a life of their own, so they don't really match the requirements for a Composition from my understanding. 

    Monday, August 16, 2010 6:57 PM
  • Hi,

    I'm afraid that I have misunderstood your question a little.

     

    I always include "Assignments".  Problem that I have run into is when I perform an operation server-side called CopyTemplate which causes lots of changes to the Assignments, this is not reflected at all in the returned entity.. 
     

    Would you please elaborate on it a bit more?   If possible, please share more of your code segment.  Thanks for your understanding.

    Best regards,

    Jonathan

    Tuesday, August 17, 2010 3:15 AM
  • Thank you for your help.   I guess I was not completely clear in my original post.

    Let me setup the situation:

    I have a set of related entities (FK relations) using the [Include] attribute to return them.  They don't really warrant a [Composition] based upon the use and life of the entities.  The example Entities I am using here are the Job and the Assignment entities (which have a 1..many relationship).

    I have a query "GetCompleteJob(Guid jobID)" which returns the Job and includes all associated entities.

    This all works great.

    Then I added a function called CopyTemplate(Guid sourceJobID, Guid destJobID), which as yould imagine copies all the data (and associated entities from one job to another like using a template).

    Problem is the following:

    Source Job - has 5 related Assignments, Destination Job - has 2 related Assignments.  In My Silverlight RIA application, user is currently viewing the destination job, opens window, searches, and selectes source job. 

    Then I copytemplate from the source to destination which does the following:

    Deletes the 2 currently related assignments, Adds 5 new related assignments, and sets all the properties on the destination job (other than the Key) to match the source.

    It appears all works fine, but when entity is returned, it has 7 related entites (this is because my domaincontext is never aware that server-side performed a delete on the orginal 2 related assignments).  Now my domain context is not current to the domainservice, and when user tries to manually delete the 2 'old' assignments, they cause a WCF/RIA error, since the entities are trying to be reattached for a delete, but do not exist on the server side.

    My goal is to be able to call the GetCompleteJob() function and inspect the associated entities.  If they were not returned from the server, then detach them from my client's domaincontext.

    For the Job entity, I can compare the DomainContext's EntitySet to the LoadOperation's returned Entities or AllEntities and identify the items that I need to remove.  For an entity returned with an Include, it seems that this is impossible.  It seems that Associations are already completely attached, and not inspectable from the loadoperation.

    Here is some code to see if this helps?

    Here is the GetCompleteJob function:

            [Query(IsComposable=false)]
            public Job GetCompleteJob(Guid jobID)
            {
                var query = this.ObjectContext.Jobs
                            .Include("Assignments")
                            .Include("JobDetail")
                            .Include("Workplans")
                            .Include("WorkplanItems")
                            .Include("Resources")
                            .Where(j => j.JobID == jobID);
                Job result = query.SingleOrDefault();
    
                return result;
            }


     

    Here is the Copy Template function (abbreviated):

            public void CopyJob(Guid sourceID, Guid destinationID, bool keepHours, bool keepResources)
            {
                Job source = this.ObjectContext.Jobs.Where(j => j.JobID == sourceID).SingleOrDefault();
                Job destination = this.ObjectContext.Jobs.Where(j => j.JobID == destinationID).SingleOrDefault();
    
                if (source == null)
                    throw new DomainException("Not a valid Source");
                if (destination == null)
                    throw new DomainException("Not a valid Destination");
                
                // Delete Assignments
                var oldA = destination.Assignments.ToList();
                foreach (var a in oldA)
                    this.ObjectContext.Assignments.DeleteObject(a);
    
                // Copy Assignments
                foreach (var a in source.Assignments)
                {
                    mapping.Add(a.AssignmentID, Guid.NewGuid());
                    Assignment newAssignment = new Assignment()
                    {
                        AssignmentID = mapping[a.AssignmentID],
                        Job = destination,
                        Workplan = destination.Workplans.Where(w => w.WorkplanID == mapping[a.WorkplanID]).Single(),
                        WorkplanItem = destination.WorkplanItems.Where(w => w.ItemID == mapping[a.WorkplanItemID]).Single(),
                        Resource = destination.Resources.Where(r => r.ResourceID == mapping[a.ResourceID]).Single(),
                        IsComplete = false,
                        HoursBudgeted = (keepHours) ? a.HoursBudgeted : 0,
                        HoursRemaining = (keepHours) ? a.HoursBudgeted : 0,
                    };
                    newAssignment.CostBudgeted = newAssignment.HoursBudgeted * newAssignment.Resource.Rate;
                    newAssignment.CostRemaining = newAssignment.HoursRemaining * newAssignment.Resource.Rate;
                    this.ObjectContext.Assignments.AddObject(newAssignment);
                }
    
                // Commit the Copy Process
                this.ObjectContext.SaveChanges();
            }


    And here is the client-side function, which calls GetCompleteJob after the copy templates where I cannot seem to eliminate the extra junk:

            public void LoadJob(Guid jobID, Action<Guid> action)
            {
                var query = workContext.GetCompleteJobQuery(jobID);
                workContext.Load(query, LoadBehavior.RefreshCurrent, lo =>
                {
                    if (lo.HasError)
                        throw lo.Error;
                    else
                    {
                        Job currentJob = workContext.Jobs.SingleOrDefault(j => j.JobID == jobID);
                        if (currentJob != null)
                        {
                            if (lo.Entities.Count() > 0)
                            {
                                Job newJob = lo.Entities.First();
                                var oldAssignments = from e in workContext.Assignments.Where(e => e.JobID == newJob.JobID)
                                                     where newJob.Assignments.Contains(e) == false
                                                     select e;
                                foreach (var item in oldAssignments)
                                {
                                    System.Diagnostics.Debug.WriteLine("Removing {0}", item.AssignmentID);
                                    workContext.Assignments.Detach(item);
                                }
                            }
                        }
                        action((Guid)lo.UserState);
                    }
                }, jobID);
            }


     

    Tuesday, August 17, 2010 9:43 AM
  • It might be easier if you just clear the EntityContainer (DomainContext.EntityContainer.Clear) and then reload your entities. Personally, I just do the CopyTemplate work on the client instead of doing it on the server. That makes sure that everything stays in sync and, frankly, I find it easier to clone entities using RIA Services Contrib then trying to do it using the server APIs.
    Tuesday, August 17, 2010 2:33 PM
  • Thanks - that is what I was arriving at - just didn't want to admit it just yet. 

    Tuesday, August 17, 2010 2:45 PM