Скопировать объект структуры объекта
У меня есть класс EF4.1 X, и я хочу сделать копию этого плюс все его дочерние записи.
X.Y и X.Y.Z
Теперь, если я делаю следующее, он возвращает ошибку.
Свойство "X.ID" является частью информации ключа объекта и не может быть изменено.
public void CopyX(long ID)
{
var c = db.Xs.Include("Y").Include("W").Include("Y.Z").SingleOrDefault(x => x.ID == ID);
if (c != null)
{
c.ID = 0;
c.Title = "Copy Of " + c.Title;
for (var m = 0; m < c.Ys.Count; m++)
{
c.Ys[m].ID = 0;
c.Ys[m].XID=0-m;
for (var p = 0; p < c.Ys[m].Zs.Count; p++)
{
c.Ys[m].Zs[p].XID = 0 - m;
c.Ys[m].Zs[p].ID = 0 - p;
}
}
for (var i = 0; i < c.Ws.Count; i++)
{
c.Ws[i].ID = 0 - i;
c.Ws[i].XID = 0;
}
db.Entry<Content>(c).State = System.Data.EntityState.Added;
db.SaveChanges();
}
}
Или есть другой способ сделать копию объектов сущности.
ПРИМЕЧАНИЕ: в каждом W, X, Y, Z есть несколько свойств.
Ответы
Ответ 1
Вам нужно сделать правильную глубокую копию всего графа объектов - лучший способ - сериализовать исходный граф объекта в потоке памяти и десериализовать его на новый экземпляр. Ваша организация должна быть сериализована. часто используется с DataContractSerializer, но вы также можете использовать двоичную сериализацию.
Ответ 2
В entity-framework-5, это безумно легко с DbExtensions.AsNotracking().
Возвращает новый запрос, в котором возвращенные объекты не будут кэшироваться в DbContext или ObjectContext.
Это похоже на все объекты в графе объектов.
Вам просто нужно понять ваш график и то, что вы делаете, и не хотите вставлять/дублировать его в БД.
Предположим, что у нас есть такие объекты, как:
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
}
public class Address
{
public int ID { get; set; }
public AddressLine { get; set; }
public int StateID { get; set; }
public ICollection<State> { get; set; }
}
Итак, чтобы Дублировать человека, мне нужно дублировать адреса, но я не хочу дублировать Штаты.
var person = this._context.Persons
.Include(i => i.Addresses)
.AsNoTracking()
.First();
// if this is a Guid, just do Guid.NewGuid();
// setting IDs to zero(0) assume the database is using an Identity Column
person.ID = 0;
foreach (var address in person.Addresses)
{
address.ID = 0;
}
this._context.Persons.Add(person);
this._context.SaveChanges();
Если вы затем захотите повторно использовать те же самые объекты снова, чтобы вставить третий дубликат, вы снова запустите запрос (с помощью AsNoTracking()) или отсоедините объекты (пример):
dbContext.Entry(person).State = EntityState.Detached;
person.ID = 0;
foreach (var address in person.Addresses)
{
dbContext.Entry(address).State = EntityState.Detached;
address.ID = 0;
}
this._context.Persons.Add(person);
this._context.SaveChanges();
Ответ 3
C не является копией, это запись, ошибка, которую вы получаете, заключается в том, что вы пытаетесь обновить ее первичный ключ, даже если вы не все равно не поработали. Вам нужно создать новый объект X, а затем скопировать значения из свойств получаемого объекта и затем вставить новый объект.
Ответ 4
Не уверен, работает ли он в 4.1, http://www.codeproject.com/Tips/474296/Clone-an-Entity-in-Entity-Framework-4:
public static T CopyEntity<T>(MyContext ctx, T entity, bool copyKeys = false) where T : EntityObject
{
T clone = ctx.CreateObject<T>();
PropertyInfo[] pis = entity.GetType().GetProperties();
foreach (PropertyInfo pi in pis)
{
EdmScalarPropertyAttribute[] attrs = (EdmScalarPropertyAttribute[])pi.GetCustomAttributes(typeof(EdmScalarPropertyAttribute), false);
foreach (EdmScalarPropertyAttribute attr in attrs)
{
if (!copyKeys && attr.EntityKeyProperty)
continue;
pi.SetValue(clone, pi.GetValue(entity, null), null);
}
}
return clone;
}
Теперь вы можете скопировать связанные объекты на свой клонированный объект; скажем, у вас есть лицо: Клиент, у которого есть свойство навигации: Заказы. Затем вы можете скопировать Клиента и их Заказы с использованием вышеуказанного метода:
Customer newCustomer = CopyEntity(myObjectContext, myCustomer, false);
foreach(Order order in myCustomer.Orders)
{
Order newOrder = CopyEntity(myObjectContext, order, true);
newCustomer.Orders.Add(newOrder);
}
Ответ 5
Я использую Newtonsoft.Json и эту потрясающую функцию.
private static T CloneJson<T>(T source)
{
return ReferenceEquals(source, null) ? default(T) : JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source));
}