Question

Nous avons une table d'audit dans notre base de données et lors de la mise à jour, les anciennes et les nouvelles valeurs sont sérialisées au format XML et stockées dans la même ligne.Les objets sont actuellement clonés en profondeur ainsi :

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

Bien que cela fonctionne, cela génère de grandes quantités de données en raison des enregistrements associés extraits du clone profond, avec des centaines de milliers de lectures à partir de la base de données. dcSer.WriteObject(memoryStream, obj), et une taille éventuelle de MemoryStream d'environ 200 Mo, sans parler de la quantité de données réécrites dans la base de données.Pas idéal.

Je voudrais donc faire un clone par membre à la place, car je crois comprendre qu'un clone par membre laisserait de côté les références d'objet et éviterait de copier tous les modèles Entity Framework associés.

J'ai donc fait ceci :

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

mais j'obtiens une exception de conversion non valide car le type réel du message entrant EntityObject est une sous-classe relative à la table elle-même.

J'ai également essayé d'utiliser une méthode d'extension pour accéder MemberwiseClone(), mais les méthodes d'extension ne peuvent pas accéder aux méthodes protégées.

Alors, comment puis-je créer une copie superficielle d’un EntityObject générique ?

Était-ce utile?

La solution

Ma première recommandation serait d'essayer ceci, qui est similaire à ce que vous faites actuellement, mais qui a fonctionné pour moi avec très peu de frais généraux :

DataContractSerializationUtils.SerializeToXmlString(Entity, throwExceptions);

De plus, j'ai déjà utilisé cette méthode avec succès et je ne trouve pas le résultat trop verbeux.Cela semble être presque identique à ce que vous faites actuellement.

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

Si aucune de ces options ne semble attrayante, ma recommandation est d'écrire une courte fonction qui utilise la réflexion pour copier toutes les propriétés de l'entité source.

Autres conseils

Depuis:

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

C'est beaucoup plus efficace et plus rapide que la sérialisation - exactement ce que vous recherchez !Fondamentalement, il utilise la réflexion pour copier les propriétés nécessaires dans un nouvel EntityObject du même type et est capable de le faire sur n'importe quelle classe dérivée d'un EntityObject en utilisant des génériques.

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

Par exemple, disons que vous aviez une entité :Client, qui possédait la propriété Navigation :Ordres.Vous pouvez ensuite copier le client et ses commandes en utilisant la méthode ci-dessus comme suit :

Customer newCustomer = CopyEntity(myObjectContext, myCustomer, false);

foreach(Order order in myCustomer.Orders)
{
    Order newOrder = CopyEntity(myObjectContext, order, true);
    newCustomer.Orders.Add(newOrder);
}

Qu'en est-il de CurrentValues.ToObject() dans EF 6 ?

var shallowCopy = (TEntity)_dbContext.Entry(entity).CurrentValues.ToObject();
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top