Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to deep clone/copy in EF Core

What I would like to do is duplicate/copy my School object and all of its children/associations in EF Core

I have something like the following:

var item = await _db.School
.AsNoTracking()
.Include(x => x.Students)
.Include(x => x.Teachers)
.Include(x => x.StudentClasses)
.ThenInclude(x => x.Class)
.FirstOrDefaultAsync(x => x.Id == schoolId);

I have been reading up on deep cloning and it seems that I should be able to do just add the entity...so pretty much the next line.

await _db.AddAsync(item);

Then EF should be smart enough to add that entity as a NEW entity. However, right off the bat I get a conflict that says "the id {schoolId} already exists" and will not insert. Even if I reset the Id of the new item I am trying to add, I still get conflicts with the Ids of the associations/children of the school iteam.

Is anyone familiar with this and what I might be doing wrong?

like image 706
tania_s Avatar asked Feb 19 '18 20:02

tania_s


1 Answers

I had the same problem too, but in my case EF core was throwing exception "the id already exists". Following the answer of @Irikos so I have created method which clones my objects.

Here's example

public class Parent
{
    public int Id { get; set; }
    public string SomeProperty { get; set; }
    public virtual List<Child> Templates { get; set; }

    public Parent Clone()
    {
        var output = new Parent() { SomeProperty = SomeProperty };

        CloneTemplates(output);

        return output;
    }

    private void CloneTemplates(Parent parentTo, Child oldTemplate = null, Child newTemplate = null)
    {
        //find old related Child elements
        var templates = Templates.Where(c => c.Template == oldTemplate);

        foreach (var template in templates)
        {
            var newEntity = new Child()
            {
                SomeChildProperty = template.SomeChildProperty,
                Template = newTemplate,
                Parent = parentTo
            };

            //find recursivly all related Child elements
            CloneTemplates(parentTo, template, newEntity);

            parentTo.Templates.Add(newEntity);
        }
    }
}

public class Child
{
    public int Id { get; set; }
    public int ParentId { get; set; }
    public virtual Parent Parent { get; set; }
    public int? TemplateId { get; set; }
    public virtual Child Template { get; set; }
    public string SomeChildProperty { get; set; }
}

Then I just call DbContext.Parents.Add(newEntity) and DbContext.SaveChanges()

That worked for me. Maybe this will be useful for someone.

like image 59
Timothy Avatar answered Nov 19 '22 00:11

Timothy