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

StackOverflow https://stackoverflow.com//questions/9695420

Вопрос

У нас есть таблица аудита в нашей базе данных, и при обновлении старые и новые значения сериализуются в 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), и возможный размер потока памяти около 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?

Это было полезно?

Решение

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

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);
        }
    }

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

Другие советы

От:

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);
}

Что насчет CurrentValues.ToObject() в EF 6?

var shallowCopy = (TEntity)_dbContext.Entry(entity).CurrentValues.ToObject();
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top