kdeez
kdeez

Reputation: 711

Entity Framework C# - Cloning object with children with many-to-many relationships

Using EF and C#, I'm trying to make a clone of an entity which has a lot of related records. I want to clone the related records as well. The top level object is a Bid.

    class Bid 
    {
          Collection<ItemGroup> ItemGroups {get;set;}
          Collection<Evaluator> Evaluators {get;set;}
    }

Children being ItemGroups and Evaluators. They are many-to-many.

class ItemGroup
{
     Collection<Evaluator> Evaluators {get;set;}
}

Class Evaluator
{
     Collection<ItemGroup> ItemGroups {get;set;}
}

I clone the Bid, and all of it's children, by querying the database

Bid bid = dbContext.Bids.AsNoTracking().FirstOrDefault()
                   .Include(b => b.ItemGroups)
                   .Include(b => b.ItemGroups.Select(e => e.Evaluators))

and adding the Bid back in with

database.Bids.Add(bid);
database.SaveChanges();

The problem is the many-to-many relationship between Evaluators and ItemGroups. Because their collections reference eachother, when you add the Bid back in, it DUPLICATES the records.

So, before clone, I have a Bid:

Original Bid -
Number of ItemGroups = 3
Number of Evaluators = 2

and, after clone, I have a new Bid, with:

New Bid -
Number of ItemGroups = 3
Number of Evaluators = 6

Which is obviously incorrect. How do I clone this relationship without having EF add duplicates?

Is the problem my original query? I've tried all sorts of options, using .Include()s that trace from Bid -> Evaluators -> Item Groups, or simply just go Bid -> Evaluator and Bid -> ItemGroup, but nothing seems to get me quite what I want. Any and all help is greatly appreciated, and please just let me know if I can offer some clarifications. Thanks in advance.

Upvotes: 1

Views: 498

Answers (1)

kdeez
kdeez

Reputation: 711

Well, here's my super janky solution until someone can tell me a better way to do it. Basically, I leave out the many-to-many relationships in my query Include()s, querying for bid.ItemGroups, and bid.Evaluators only.

I add those as normal, which gives me a Bid with the appropriate number of item groups, and 0 evaluators, as the ItemGroup -> Evaluator relationships weren't established because i did not query for them.

Then, after calling database.Bids.Add(bid), I re-query for the original bid and it's Evaluator/ItemGroup relationships, and manually hook up all of the Evaluator/ItemGroup references using names/id's/whatever identifiers are available.

List<Evaluator> originalEvals = db.Bids.Where(b => b.Id == entityId)
                .Select(b => b.Evaluators)
                .FirstOrDefault()
                .ToList();
            foreach(Evaluator origEval in originalEvals)
            {
                db.Entry(origEval).Reference(e => e.ClientUser).Load();
                db.Entry(origEval).Collection(e => e.ItemGroups).Load();
            }

            foreach (Evaluator newEval in bid.Evaluators)
            {
                Evaluator originalEval = originalEvals.Where(e => e.ClientUserId == newEval.ClientUserId).FirstOrDefault();

                foreach (ItemGroup ig in originalEval.ItemGroups)
                {
                    newEval.ItemGroups.Add(bid.ItemGroups.Where(g => g.Name == ig.Name).FirstOrDefault());
                }
            }

Shitty, but it works.

Upvotes: 1

Related Questions