web-dev-qa-db-fra.com

Entity Framework 5 copie/copie complète d'une entité

J'utilise Entity Framework 5 (DBContext) et j'essaie de trouver le meilleur moyen de copier en profondeur une entité (c'est-à-dire de copier l'entité et tous les objets associés), puis d'enregistrer les nouvelles entités dans la base de données. Comment puis-je faire ceci? J'ai envisagé d'utiliser des méthodes d'extension telles que CloneHelper mais je ne suis pas sûr que cela s'applique à DBContext.

67
kypk

Un moyen simple et peu coûteux de cloner une entité est de procéder de la manière suivante:

var originalEntity = Context.MySet.AsNoTracking()
                             .FirstOrDefault(e => e.Id == 1);
Context.MySet.Add(originalEntity);
Context.SaveChanges();

le truc ici est/ AsNoTracking () - lorsque vous chargez une entité comme celle-ci, votre contexte ne le sait pas et lorsque vous appelez SaveChanges, il la traitera comme une nouvelle entité.

Si MySet a une référence à MyProperty et que vous souhaitez en obtenir une copie également, utilisez simplement un Include:

var originalEntity = Context.MySet.Include("MyProperty")
                            .AsNoTracking()
                            .FirstOrDefault(e => e.Id == 1);
120
Leo

Voici une autre option. 

Je le préfère dans certains cas car cela ne vous oblige pas à exécuter une requête spécifiquement pour obtenir le clonage de données. Vous pouvez utiliser cette méthode pour créer des clones d'entités déjà obtenues de la base de données.

//Get entity to be cloned
var source = Context.ExampleRows.FirstOrDefault();

//Create and add clone object to context before setting its values
var clone = new ExampleRow();
Context.ExampleRows.Add(clone);

//Copy values from source to clone
var sourceValues = Context.Entry(source).CurrentValues;
Context.Entry(clone).CurrentValues.SetValues(sourceValues);

//Change values of the copied entity
clone.ExampleProperty = "New Value";

//Insert clone with changes into database
Context.SaveChanges();

Cette méthode copie les valeurs actuelles de la source dans une nouvelle ligne ajoutée.

21
Jas Laferriere

C'est une méthode d'extension générique qui permet le clonage générique.

Vous devez extraire System.Linq.Dynamic de nuget.

    public TEntity Clone<TEntity>(this DbContext context, TEntity entity) where TEntity : class
    {

        var keyName = GetKeyName<TEntity>();
        var keyValue = context.Entry(entity).Property(keyName).CurrentValue;
        var keyType = typeof(TEntity).GetProperty(keyName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance).PropertyType;

        var dbSet = context.Set<TEntity>();
        var newEntity =  dbSet
            .Where(keyName + " = @0", keyValue)
            .AsNoTracking()
            .Single();

        context.Entry(newEntity).Property(keyName).CurrentValue = keyType.GetDefault();

        context.Add(newEntity);

        return newEntity;
    }

La seule chose que vous devez implémenter vous-même est la méthode GetKeyName. Cela peut aller de return typeof(TEntity).Name + "Id" à return the first guid property ou renvoyer la première propriété marquée par DatabaseGenerated(DatabaseGeneratedOption.Identity)].

Dans mon cas, j'ai déjà marqué mes cours avec [DataServiceKeyAttribute("EntityId")]

    private string GetKeyName<TEntity>() where TEntity : class
    {
        return ((DataServiceKeyAttribute)typeof(TEntity)
           .GetCustomAttributes(typeof(DataServiceKeyAttribute), true).First())
           .KeyNames.Single();
    }
1
Jürgen Steinblock

J'ai eu le même problème dans Entity Framework Core où le clonage profond implique plusieurs étapes lorsque les entités enfants sont chargées paresseuses. Une façon de cloner la structure entière est la suivante:

   var clonedItem = Context.Parent.AsNoTracking()
        .Include(u => u.Child1)
        .Include(u => u.Child2)
        // deep includes might go here (see ThenInclude)
        .FirstOrDefault(u => u.ParentId == parentId);

    // remove old id from parent
    clonedItem.ParentId = 0;

    // remove old ids from children
    clonedItem.Parent1.ForEach(x =>
    {
        x.Child1Id = 0;
        x.ParentId= 0;
    });
    clonedItem.Parent2.ForEach(x =>
    {
        x.Child2Id = 0;
        x.ParentId= 0;
    });

    // customize entities before inserting it

    // mark everything for insert
    Context.Parent.Add(clonedItem);

    // save everything in one single transaction
    Context.SaveChanges();

Bien sûr, il y a moyen de créer des fonctions génériques pour tout charger et/ou réinitialiser les valeurs de toutes les clés, mais cela devrait rendre toutes les étapes plus claires et personnalisables Comprendre).

0
Alexei