Копировать объект Entity Framework - PullRequest
13 голосов
/ 24 августа 2011

У меня есть класс X EF4.1, и я хочу сделать копию этого плюс все его дочерние записи. 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 есть несколько свойств.

Ответы [ 5 ]

27 голосов
/ 08 августа 2013

В это невероятно легко с 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();
9 голосов
/ 24 августа 2011

Вам необходимо сделать правильную глубокую копию всего графа сущностей - лучший способ - сериализовать исходный граф сущностей в поток памяти и десериализовать его в новый экземпляр.Ваша сущность должна быть сериализуемой. часто используется с DataContractSerializer , но вы также можете использовать двоичную сериализацию.

3 голосов
/ 24 августа 2011

C - это не копия, это запись, ошибка, которую вы получаете, потому что вы пытаетесь обновить его первичный ключ, даже если это не так, он все равно не будет работать.Вам необходимо создать новый объект X, а затем скопировать значения из свойств извлеченного объекта и затем вставить новый объект.

1 голос
/ 06 апреля 2015

Я использую Newtonsoft.Json, и эта потрясающая функция.

    private static T CloneJson<T>(T source)
    {
        return ReferenceEquals(source, null) ? default(T) : JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source));
    }
1 голос
/ 16 октября 2012

Не уверен, работает ли он в 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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...