Создать мелкую копию / клон подкласса EntityObject - PullRequest
3 голосов
/ 14 марта 2012

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

Ответы [ 3 ]

3 голосов
/ 14 марта 2012

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

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

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

2 голосов
/ 11 октября 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);
}
0 голосов
/ 28 марта 2018

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

var shallowCopy = (TEntity)_dbContext.Entry(entity).CurrentValues.ToObject();
...