Создать неглубокую копию/клон подкласса EntityObject

У нас есть таблица аудита в нашей базе данных, и при обновлении старые и новые значения сериализуются в XML и сохраняются в одной строке. В настоящее время объекты глубоко клонированы таким образом:

public EntityObject CloneEntity(EntityObject obj)
{
    DataContractSerializer dcSer = new DataContractSerializer(obj.GetType());

    MemoryStream memoryStream = new MemoryStream();

    dcSer.WriteObject(memoryStream, obj);

    memoryStream.Position = 0;

    EntityObject newObject = (EntityObject)dcSer.ReadObject(memoryStream);

    return newObject;
}

Пока это работает, он генерирует огромные объемы данных из-за связанных записей, извлеченных из глубокого клона, с сотнями тысяч чтений из БД на dcSer.WriteObject(memoryStream, obj) и конечным размером MemoryStream около 200 МБ, не говоря уже о количестве данных. записывается обратно в БД. Не идеально.

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

Итак, я сделал это:

public EntityObject CloneEntity(EntityObject obj)
{
    EntityObjectAuditable auditable = (EntityObjectAuditable)obj; // invalid cast exception

    return auditable.ShallowCopy();
}

// ....

public class EntityObjectAuditable : EntityObject
{
    public EntityObjectAuditable ShallowCopy()
    {
        return (EntityObjectAuditable)this.MemberwiseClone();
    }
}

но я получаю недопустимое исключение приведения, потому что фактический тип входящего EntityObject является подклассом, относящимся к самой таблице.

Я также пытался использовать метод расширения для доступа к MemberwiseClone(), но методы расширения не могут получить доступ к защищенным методам.

Итак, как я могу создать поверхностную копию универсального объекта EntityObject?


person sennett    schedule 14.03.2012    source источник
comment
Я вижу, что вопрос был задан некоторое время назад, однако у меня была точно такая же проблема, и я нашел решение, поэтому добавил это в качестве ответа на случай, если кто-то еще захочет знать!   -  person markmnl    schedule 11.10.2012


Ответы (3)


Моей первой рекомендацией было бы попробовать это, что похоже на то, что вы делаете сейчас, но сработало для меня с очень небольшими накладными расходами:

DataContractSerializationUtils.SerializeToXmlString(Entity, throwExceptions);

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

    /// <summary>
    /// Creates an exact duplicate of the entity provided
    /// </summary>
    /// <param name="source">The source copy of the entity</param>
    /// <returns>An exact duplicate entity</returns>
    public TEntity Clone(TEntity Source)
    {
        // Don’t serialize a null object, simply return the default for that object
        if (ReferenceEquals(Source, null))
        {
            return default(TEntity);
        }
        var dcs = new DataContractSerializer(typeof (TEntity));
        using (Stream stream = new MemoryStream())
        {
            dcs.WriteObject(stream, Source);
            stream.Seek(0, SeekOrigin.Begin);
            return (TEntity) dcs.ReadObject(stream);
        }
    }

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

person msigman    schedule 14.03.2012
comment
Спасибо за ответ. DataContractSerializationUtils.SerializeToXmlString кажется чем-то из набора инструментов West Wind Web Toolkit, который я не хочу включать в свое консольное приложение; второй метод страдает точно такими же проблемами, как и то, чем я сейчас занимаюсь; а третий? Ург. Отражение. На самом деле не должно быть так сложно и хрупко создать неглубокий клон, особенно если учесть, что MemberwiseClone существует. - person sennett; 14.03.2012
comment
Блин, я только что прочитал свой комментарий там, и я сталкиваюсь с полным придурком! Извини за это. Большое спасибо за вашу помощь. В итоге я использовал отражение, и этот ответ помог мне при создании экземпляра нового экземпляра из типа: stackoverflow.com/questions/752/ - person sennett; 14.03.2012
comment
Я согласен, что это не должно быть так сложно. Рад помочь! - person msigman; 14.03.2012

От:

http://www.codeproject.com/Tips/474296/Clone-an-Entity-in-Entity-Framework-4.

Это намного эффективнее и быстрее, чем сериализация — именно то, что вы ищете! По сути, он использует отражение для копирования необходимых свойств в новый EntityObject того же типа и может делать это в любом классе, производном от EntityObject, используя дженерики.

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);
}
person markmnl    schedule 11.10.2012

Как насчет CurrentValues.ToObject() в EF 6?

var shallowCopy = (TEntity)_dbContext.Entry(entity).CurrentValues.ToObject();
person sandimschuh    schedule 28.03.2018