Данные клонирования для платформы Entity Framework

Я создаю программное обеспечение, где пользователь может создавать новый продукт на основе более старого продукта.

Теперь мне нужно сделать операции копирования/клонирования с Entity Framework. Сначала я начал писать так:

foreach(sourcedata1 in table1)
{
   ... create new table
   ... copy data
   ... create Guid
   ... add
   foreach(sourcedata2 in table2)
   {
       ... create new table
       ... copy data
       ... create Guid
       ... add       

       ... and so on
   }
}

Проблема в том, что это не очень хороший способ сделать это. Есть ли какая-либо информация о клонированном виде (кроме Guid, которая должна быть сгенерирована для новых строк) или мне нужно вручную копировать все?

Другое решение

Вы также можете использовать EmitMapper или AutoMapper для копирования свойств.

Ответ 2

Чтобы клонировать Entity в Entity Framework, вы могли бы просто отсоединить объект от DataContext, а затем снова добавить его в EntityCollection.

context.Detach(entity);
entityCollection.Add(entity);

Обновить для EF6 + (из комментариев)

context.Entry(entity).State = EntityState.Detached;
entity.id = 0;
entity.property = value;
context.Entry(entity).State = EntityState.Added;
context.SaveChanges();

Ответ 3

public static EntityObject Clone(this EntityObject Entity)
{
    var Type = Entity.GetType();
    var Clone = Activator.CreateInstance(Type);

    foreach (var Property in Type.GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.SetProperty))
    {
        if (Property.PropertyType.IsGenericType && Property.PropertyType.GetGenericTypeDefinition() == typeof(EntityReference<>)) continue;
        if (Property.PropertyType.IsGenericType && Property.PropertyType.GetGenericTypeDefinition() == typeof(EntityCollection<>)) continue;
        if (Property.PropertyType.IsSubclassOf(typeof(EntityObject)))  continue;

        if (Property.CanWrite)
        {
            Property.SetValue(Clone, Property.GetValue(Entity, null), null);
        }
    }

    return (EntityObject)Clone;
}

Это простой метод, который я написал. Он должен работать для большинства людей.

Ответ 4

Чтобы добавить новую строку, содержимое которой основано на существующей строке, выполните следующие действия:

  • Получить объект на основе начальной строки.
  • Задайте состояние записи для добавления объекта.
  • Изменить сущность.
  • Сохранить изменения.

Вот пример:

var rabbit = db.Rabbits.First(r => r.Name == "Hopper");
db.Entry(rabbit).State = EntityState.Added;
rabbit.IsFlop = false;
db.SaveChanges();

Ответ 5

Если вы хотите создать копию объекта для сравнения позже в вашем исполнении кода, вы можете выбрать объект в новом контексте db.

Если, например, вы обновляете объект, то позже в коде вы хотите сравнить обновленный и оригинальный объект:

var db = new dbEntityContext();
var dbOrig = new dbEntityContext();

var myEntity = db.tblData.FirstOrDefault(t => t.Id == 123);
var myEntityOrig = dbOrig.tblData.FirstOrDefault(t => t.Id == 123);

//Update the entity with changes
myEntity.FirstName = "Gary";

//Save Changes
db.SaveChnages();

В этот момент myEntity.FirstName будет содержать "Gary", тогда как myEntityOrig.FirstName будет содержать исходное значение. Полезно, если у вас есть функция для регистрации изменений, где вы можете пройти в обновленном и оригинальном объекте.

Ответ 6

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

<Extension> _
Public Function DuplicateEntity(Of T As {New, Class})(ctx As myContext, ent As T) As T
    Dim other As New T 'T is a proxy type, but New T creates a non proxy instance
    ctx.Entry(other).State = EntityState.Added 'attaches it to ctx
    ctx.Entry(other).CurrentValues.SetValues(ent) 'copies primitive properties
    Return other
End Function

Например:

newDad = ctx.DuplicateEntity(oDad)
newDad.RIDGrandpa ' int value copied
newDad.Grandpa    ' object for RIDGrandpa above, equals Nothing(null)
newDad.Children   ' nothing, empty

Я точно не знаю, как перезагрузить Grandpa в этом случае.
Это не работает:

ctx.SaveChanges()
ctx.Entry(newDad).Reload()

но на самом деле, никаких проблем. Я бы предпочел назначить Grandpa вручную, если мне это нужно.

newDad.Grandpa = oDad.Grandpa

EDIT: Как MattW предлагает в своем комментарии, отделяя и обнаруживая новый объект, вы загружаете его дочерние (а не коллекции).

ctx.Entry(newDad).State = EntityState.Detached
ctx.Find(newDad.RowId) 'you have to know the key name