Utilizzo degli stati delle entità

Questo argomento illustra come aggiungere e associare entità a un contesto e come Entity Framework elabora tali entità durante SaveChanges. Entity Framework si occupa del rilevamento dello stato delle entità mentre sono connessi a un contesto, ma in scenari disconnessi o a più livelli è possibile comunicare a EF lo stato in cui devono trovarsi le entità. Le tecniche illustrate in questo argomento si applicano in modo analogo ai modelli creati con Code First ed EF Designer.

Stati dell'entità e SaveChanges

Un'entità può essere in uno dei cinque stati definiti dall'enumerazione EntityState. Questi stati sono:

  • Aggiunta: l'entità viene rilevata dal contesto, ma non esiste ancora nel database
  • Non modificato: l'entità viene rilevata dal contesto ed è presente nel database e i relativi valori delle proprietà non sono stati modificati dai valori nel database
  • Modifica: l'entità viene rilevata dal contesto ed esiste nel database e alcuni o tutti i relativi valori di proprietà sono stati modificati
  • Eliminato: l'entità viene rilevata dal contesto ed è presente nel database, ma è stata contrassegnata per l'eliminazione dal database alla successiva chiamata a SaveChanges
  • Scollegato: l'entità non viene rilevata dal contesto

SaveChanges esegue operazioni diverse per le entità in stati diversi:

  • Le entità non modificate non vengono toccate da SaveChanges. Aggiornamenti non vengono inviati al database per le entità nello stato Non modificato.
  • Le entità aggiunte vengono inserite nel database e quindi diventano invariate quando SaveChanges restituisce.
  • Le entità modificate vengono aggiornate nel database e quindi diventano invariate quando SaveChanges restituisce.
  • Le entità eliminate vengono eliminate dal database e quindi scollegate dal contesto.

Gli esempi seguenti illustrano i modi in cui è possibile modificare lo stato di un'entità o di un entità graph.

Aggiunta di una nuova entità al contesto

È possibile aggiungere una nuova entità al contesto chiamando il metodo Add in DbSet. In questo modo l'entità viene inserita nello stato Aggiunto, ovvero verrà inserita nel database la volta successiva che viene chiamato SaveChanges. Ad esempio:

using (var context = new BloggingContext())
{
    var blog = new Blog { Name = "ADO.NET Blog" };
    context.Blogs.Add(blog);
    context.SaveChanges();
}

Un altro modo per aggiungere una nuova entità al contesto consiste nel modificarne lo stato in Aggiunta. Ad esempio:

using (var context = new BloggingContext())
{
    var blog = new Blog { Name = "ADO.NET Blog" };
    context.Entry(blog).State = EntityState.Added;
    context.SaveChanges();
}

Infine, è possibile aggiungere una nuova entità al contesto associandola a un'altra entità già rilevata. Potrebbe trattarsi di aggiungere la nuova entità alla proprietà di navigazione raccolta di un'altra entità o impostando una proprietà di navigazione di riferimento di un'altra entità in modo che punti alla nuova entità. Ad esempio:

using (var context = new BloggingContext())
{
    // Add a new User by setting a reference from a tracked Blog
    var blog = context.Blogs.Find(1);
    blog.Owner = new User { UserName = "johndoe1987" };

    // Add a new Post by adding to the collection of a tracked Blog
    blog.Posts.Add(new Post { Name = "How to Add Entities" });

    context.SaveChanges();
}

Si noti che per tutti questi esempi se l'entità da aggiungere include riferimenti ad altre entità non ancora rilevate, queste nuove entità verranno aggiunte anche al contesto e verranno inserite nel database alla successiva chiamata a SaveChanges.

Collegamento di un'entità esistente al contesto

Se si dispone di un'entità già esistente nel database, ma che non è attualmente monitorata dal contesto, è possibile indicare al contesto di tenere traccia dell'entità usando il metodo Attach in DbSet. L'entità sarà nello stato Non modificato nel contesto. Ad esempio:

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Blogs.Attach(existingBlog);

    // Do some more work...  

    context.SaveChanges();
}

Si noti che non verranno apportate modifiche al database se SaveChanges viene chiamato senza eseguire altre modifiche dell'entità associata. Ciò è dovuto al fatto che l'entità è nello stato Non modificato.

Un altro modo per collegare un'entità esistente al contesto consiste nel modificarne lo stato in Unchanged. Ad esempio:

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Entry(existingBlog).State = EntityState.Unchanged;

    // Do some more work...  

    context.SaveChanges();
}

Si noti che per entrambi questi esempi se l'entità associata ha riferimenti ad altre entità non ancora rilevate, queste nuove entità verranno associate anche al contesto nello stato Non modificato.

Collegamento di un'entità esistente ma modificata al contesto

Se si dispone di un'entità già esistente nel database, ma a cui potrebbero essere state apportate modifiche, è possibile indicare al contesto di collegare l'entità e impostarne lo stato su Modificato. Ad esempio:

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Entry(existingBlog).State = EntityState.Modified;

    // Do some more work...  

    context.SaveChanges();
}

Quando si modifica lo stato su Modificato tutte le proprietà dell'entità verranno contrassegnate come modificate e tutti i valori delle proprietà verranno inviati al database quando viene chiamato SaveChanges.

Si noti che se l'entità associata ha riferimenti ad altre entità non ancora rilevate, queste nuove entità verranno collegate al contesto nello stato Non modificato, non verranno automaticamente modificate. Se sono presenti più entità che devono essere contrassegnate come Modificate, è necessario impostare lo stato per ognuna di queste entità singolarmente.

Modifica dello stato di un'entità rilevata

È possibile modificare lo stato di un'entità già rilevata impostando la proprietà State sulla relativa voce. Ad esempio:

var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" };

using (var context = new BloggingContext())
{
    context.Blogs.Attach(existingBlog);
    context.Entry(existingBlog).State = EntityState.Unchanged;

    // Do some more work...  

    context.SaveChanges();
}

Si noti che la chiamata a Add o Attach per un'entità già rilevata può essere usata anche per modificare lo stato dell'entità. Ad esempio, la chiamata a Attach per un'entità attualmente nello stato Aggiunto cambierà lo stato in Unchanged.

Modello di inserimento o aggiornamento

Un modello comune per alcune applicazioni consiste nell'aggiungere un'entità come nuova (con conseguente inserimento di un database) o collegare un'entità come esistente e contrassegnarla come modificata (con conseguente aggiornamento del database) a seconda del valore della chiave primaria. Ad esempio, quando si usano chiavi primarie integer generate dal database, è comune considerare un'entità con una chiave zero come nuova e un'entità con una chiave diversa da zero come esistente. Questo modello può essere ottenuto impostando lo stato dell'entità in base a un controllo del valore della chiave primaria. Ad esempio:

public void InsertOrUpdate(Blog blog)
{
    using (var context = new BloggingContext())
    {
        context.Entry(blog).State = blog.BlogId == 0 ?
                                   EntityState.Added :
                                   EntityState.Modified;

        context.SaveChanges();
    }
}

Si noti che quando si modifica lo stato su Modificato tutte le proprietà dell'entità verranno contrassegnate come modificate e tutti i valori delle proprietà verranno inviati al database quando viene chiamato SaveChanges.