Объект дублирования объекта Entity Framework и все дочерние свойства

Пример структуры

public class Page
{
    public int PageId { get; set; }
    public string Prop1 { get; set; }
    public string Prop2 { get; set; }
    public virtual List<Section> Sections { get; set; }
}

public class Section
{
    public int SectionId { get; set; }
    public int PageId { get; set; }
    public virtual Page Page { get; set; }
    public virtual List<Heading> Headings { get; set; }
}

public class Heading
{
    public int HeadingId { get; set; }
    public int SectionId { get; set; }
    public virtual Section Section { get; set; }
}

Стоит отметить, что моя фактическая структура имеет больше уровней, чем это, но этого должно быть достаточно, чтобы объяснить, чего я пытаюсь достичь.

Итак, я загружаю свой объект Page, а затем клонирую этот объект и делаю небольшие изменения в свойствах Page i.e. Prop1, Prop2

Page pageFromDb = getPageMethod();
Page clonedPage = pageFromDb.Clone();
clonedPage.Prop1 = clonedPage.Prop1 + " Cloned";
addPageMethod(clonedPage); //Adds the page to db

В приведенном выше примере clonedPage структура прекрасна, и в базу данных добавляется новый Page. Однако я верю, потому что Идентификатор дочерних объектов задан, и отношение детей всегда одно для многих. Исходный объект pageFromDb потеряет все дочерние элементы в качестве рамки сущности вместо создания новых объектов Section для клонированного Page обновит Section.PageId на вновь вставленной странице.

Я считаю, что исправление для этого было бы foreach, foreach и т.д. и установить все идентификаторы на 0 перед вставкой, тогда инфраструктура сущности создаст новые записи для каждого объекта. Есть ли более простой/лучший способ клонирования объекта в среде фреймворка сущности.?

Ответ 1

Для того, чтобы Entity Framework обрабатывала клон как целый новый граф объектов при сохранении графика, все сущности в графе должны быть отключены из контекста, в котором был восстановлен корневой объект.

Это можно сделать с помощью метода AsNoTracking в контексте.

Например, это приведет к отображению графика страницы и связанных разделов из базы данных и отключению отслеживания. Эффективно это клон, как если бы вы добавили его в свой DbSet страницы и сохранили его, создаст совершенно новый граф объектов в базе данных. То есть новый объект страницы и новые подразделения Секции. Обратите внимание: вам не нужно вызывать ваш метод Clone.

var clone = context.Pages
    .AsNoTracking()
    .Including(pages => pages.Sections)
    .Single(...);
context.Pages.Add(clone);
context.SaveChanges(); // creates an entirely new object graph in the database

Ответ 2

Попробуйте это!

public Page CopyPage(int pageID)
{
    using(Context context = new Context())
    {
        context.Configuration.LazyLoadingEnabled = false;
        Page dbPage = context.Pages.Where(p => p.PageId == pageID).Include(s => s.Sections.Select(s => s.Section)).First();
        Page page = dbPage.Clone();
        page.PageId = 0;

        for (int i = 0; i < dbPage .Sections.Count; i++)
            page.Sections[i] = new Section
            {
                SectionId = 0,
                PageId = 0,
                Page = null,
                Headings = dbPage[i].Headings
            };
        return page;
    }
}

public Page Clone()
    {
        Object page = this.GetType().InvokeMember("", BindingFlags.CreateInstance, null, this, null);

        foreach(PropertyInfo propertyInfo in this.GetType().GetProperties())
        {
            if(propertyInfo.CanWrite)
            {
                propertyInfo.SetValue(page, propertyInfo.GetValue(this, null), null);
            }
        }

        return page;   
    }