GraphDiff icon indicating copy to clipboard operation
GraphDiff copied to clipboard

Merge on Unique Key (not Primary Key)

Open guillaume-fr opened this issue 11 years ago • 8 comments
trafficstars

For synchronisation purpose sometimes we add a GUID column with an unique constraint but keep an autoinc int PKEY for regular work (avoiding fragmentation, unreadable keys,...). If we could specify the column(s) on which the equality should be done during merge that would be great.

Context.UpdateGraph(document, map => map.OwnedCollection(d => d.Lines, withKey : line => line.UniqueId));

Without it, GraphDiff removes and re-adds the lines instead of updating them.

guillaume-fr avatar Jun 26 '14 09:06 guillaume-fr

Is the Int set as the sole primary key in fluent api or data annotations? Can you show an example of the mappings & model?

refactorthis avatar Jun 26 '14 09:06 refactorthis

Here is my context

public class MyContext : DbContext
{
  public DbSet<Owner> Owners { get; set; }
  public DbSet<Line> Lines { get; set; }
}

public class Owner
{
  public Owner()
  {
    Lines = new HashSet<Line>();
  }

  [Key]
  public int Id { get; set; }

  public virtual ICollection<Line> Lines { get; private set; }
}

public class Line
{
  [Key]
  public int Id { get; set; }

  public int OwnerId { get; set; }

  public string Caption { get; set; }

  [Index(IsUnique=true)]
  public Guid UniqueId { get; set; }
}

And my failing test method

[TestMethod]
public void TestMethod1()
{
  Owner existingOwner = new Owner();
  existingOwner.Lines.Add(new Line() { Caption = "EXISTING LINE", UniqueId = new Guid("D605A6BE-9CF7-4387-8B52-E40AA526AF26")});
  using (var context = new MyContext())
  {
    context.Database.Delete();
    context.Database.Create();
    context.Owners.Add(existingOwner);
    context.SaveChanges();
  }

  Owner externalOwner = new Owner() { Id = existingOwner.Id };
  externalOwner.Lines.Add(new Line() { Caption = "UPDATED LINE", UniqueId = new Guid("D605A6BE-9CF7-4387-8B52-E40AA526AF26") });

  Owner updatedOwner;
  using (var context = new MyContext())
  {
    updatedOwner = context.UpdateGraph(externalOwner, map => map.OwnedCollection(o => o.Lines));
    context.SaveChanges();
  }

  Assert.AreEqual(existingOwner.Lines.Single().Id, updatedOwner.Lines.Single().Id);
}

guillaume-fr avatar Jun 26 '14 10:06 guillaume-fr

The line you are updating must have the primary key specified. It doesn't make sense not to. If the entity was detached from the database it would have a primary key and this is not being set in your test for the updating line.

refactorthis avatar Jun 26 '14 13:06 refactorthis

I know but I have to synchronize my context with data from another application that cannot use the same pkey : if I add objects on both sides then synchronize, pkey will collide so we use another column for synchronization purposes (we don't have the PKEY in our DTO). I know that I can add a step to retrieve Ids from Guids but that's one more step and a lot of code. I think it could be part of GraphDiff.

That's my request : I would like to be able to merge using a specified column (and not the PKEY) to match row identity because I don't have the PKEY. That's a common pattern in synchronization and that would be great to have GraphDiff merge doing it natively.

guillaume-fr avatar Jun 26 '14 13:06 guillaume-fr

Ah right I understand now, sounds like it could be useful and not very hard to implement. If you are willing to give it a go that would be great otherwise I may get a chance next week to do some more work

refactorthis avatar Jun 26 '14 13:06 refactorthis

@refactorthis what do you think about my implementation of this feature ?

guillaume-fr avatar Aug 28 '14 08:08 guillaume-fr

Was this ever implemented in the main branch? Any intentions to include, or even allow for a custom expression for equality? I.e. with a lambda expression in case of multiple keys. Thanks

amadeogallardo avatar Nov 17 '15 17:11 amadeogallardo

Hey, I'm willing to implement this feature but I think I need a little hint or suggestion (@refactorthis ). I think the right place to implement this feature is within "GraphDiffer.FindEntityMatching" there we can use an custom IComparer<T>implementation to find the "correct" entity.

Thanks

AlexMountainBN avatar Mar 19 '19 15:03 AlexMountainBN