Ответ 1
Используя прямую сериализацию, вы можете сделать это:
Используя Reflection, но с большим количеством кода вы можете сделать это: http://msmvps.com/blogs/matthieu/archive/2008/05/31/entity-cloner.aspx
Я создаю программное обеспечение, где пользователь может создавать новый продукт на основе более старого продукта.
Теперь мне нужно сделать операции копирования/клонирования с 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 для копирования свойств.
Используя прямую сериализацию, вы можете сделать это:
Используя Reflection, но с большим количеством кода вы можете сделать это: http://msmvps.com/blogs/matthieu/archive/2008/05/31/entity-cloner.aspx
Чтобы клонировать 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();
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;
}
Это простой метод, который я написал. Он должен работать для большинства людей.
Чтобы добавить новую строку, содержимое которой основано на существующей строке, выполните следующие действия:
Вот пример:
var rabbit = db.Rabbits.First(r => r.Name == "Hopper");
db.Entry(rabbit).State = EntityState.Added;
rabbit.IsFlop = false;
db.SaveChanges();
Если вы хотите создать копию объекта для сравнения позже в вашем исполнении кода, вы можете выбрать объект в новом контексте 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
будет содержать исходное значение. Полезно, если у вас есть функция для регистрации изменений, где вы можете пройти в обновленном и оригинальном объекте.
Очень короткий способ дублирования объектов с использованием дженериков (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