Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Hay cuatro API principales para acceder a las entidades de las que realiza un seguimiento :DbContext
- DbContext.Entry devuelve una instancia EntityEntry<TEntity> para una instancia de entidad determinada.
- ChangeTracker.Entries devuelve EntityEntry<TEntity> instancias de todas las entidades con seguimiento o para todas las entidades con seguimiento de un tipo determinado.
- DbContext.Find, DbContext.FindAsync, DbSet<TEntity>.Findy DbSet<TEntity>.FindAsync buscan una sola entidad por clave principal, primero buscan en entidades con seguimiento y, a continuación, consultan la base de datos si es necesario.
- DbSet<TEntity>.Local devuelve entidades reales (no instancias de EntityEntry) para las entidades del tipo de entidad representado por dbSet.
Cada uno de estos se describe con más detalle en las secciones siguientes.
Sugerencia
En este documento se da por supuesto que se comprenden los estados de entidad y los conceptos básicos del seguimiento de cambios de EF Core. Consulte Change Tracking en EF Core para obtener más información sobre estos temas.
Sugerencia
Puede ejecutar y depurar todo el código de este documento descargando el código de ejemplo de GitHub.
Uso de instancias dbContext.Entry y EntityEntry
Para cada entidad con seguimiento, Entity Framework Core (EF Core) realiza un seguimiento de:
- Estado general de la entidad. Se trata de uno de
Unchanged
,Modified
,Added
oDeleted
; consulte Change Tracking en EF Core para obtener más información. - Relaciones entre entidades con seguimiento. Por ejemplo, el blog al que pertenece una entrada.
- Los "valores actuales" de las propiedades.
- Los "valores originales" de las propiedades, cuando esta información está disponible. Los valores originales son los valores de propiedad que existían cuando se consultaba la entidad desde la base de datos.
- Qué valores de propiedad se han modificado desde que se consultaron.
- Otra información sobre los valores de propiedad, como si el valor es temporal o no.
Pasar una instancia de entidad a DbContext.Entry da como resultado un EntityEntry<TEntity> que proporciona acceso a esta información para la entidad especificada. Por ejemplo:
using var context = new BlogsContext();
var blog = await context.Blogs.SingleAsync(e => e.Id == 1);
var entityEntry = context.Entry(blog);
En las secciones siguientes se muestra cómo usar entityEntry para acceder y manipular el estado de entidad, así como el estado de las propiedades y las navegaciones de la entidad.
Trabajar con la entidad
El uso más común de EntityEntry<TEntity> es acceder al actual EntityState de una entidad. Por ejemplo:
var currentState = context.Entry(blog).State;
if (currentState == EntityState.Unchanged)
{
context.Entry(blog).State = EntityState.Modified;
}
El método Entry también se puede usar en entidades que aún no están siendo rastreadas. Esto no inicia el seguimiento de la entidad; el estado de la entidad sigue siendo Detached
. Sin embargo, el EntityEntry devuelto se puede usar para cambiar el estado de la entidad, momento en el que la entidad será rastreada en el estado especificado. Por ejemplo, el código siguiente iniciará el seguimiento de una instancia de Blog como Added
:
var newBlog = new Blog();
Debug.Assert(context.Entry(newBlog).State == EntityState.Detached);
context.Entry(newBlog).State = EntityState.Added;
Debug.Assert(context.Entry(newBlog).State == EntityState.Added);
Sugerencia
A diferencia de EN EF6, establecer el estado de una entidad individual no hará que se realice el seguimiento de todas las entidades conectadas. Esto hace que establecer el estado de esta manera sea una operación de nivel inferior en comparación con llamar a Add
, Attach
, o Update
, que funcionan sobre todo el gráfico de entidades.
En la tabla siguiente se resumen las formas de usar EntityEntry para trabajar con una entidad completa:
Miembro EntityEntry | Descripción |
---|---|
EntityEntry.State | Obtiene y establece el EntityState de la entidad. |
EntityEntry.Entity | Obtiene la instancia de la entidad. |
EntityEntry.Context | El DbContext que realiza el seguimiento de esta entidad. |
EntityEntry.Metadata | IEntityType metadatos para el tipo de entidad. |
EntityEntry.IsKeySet | Si la entidad tiene establecido su valor de clave o no. |
EntityEntry.Reload() | Sobrescribe los valores de las propiedades con valores leídos de la base de datos. |
EntityEntry.DetectChanges() | Forza la detección de cambios solo para esta entidad; consulte Detección de cambios y notificaciones. |
Trabajar con una sola propiedad
Varias sobrecargas de EntityEntry<TEntity>.Property permiten acceder a información sobre una propiedad individual de una entidad. Por ejemplo, utilizando una API fuertemente tipada y con un estilo fluido.
PropertyEntry<Blog, string> propertyEntry = context.Entry(blog).Property(e => e.Name);
En su lugar, el nombre de la propiedad se puede pasar como una cadena. Por ejemplo:
PropertyEntry<Blog, string> propertyEntry = context.Entry(blog).Property<string>("Name");
A continuación, se puede usar el valor devuelto PropertyEntry<TEntity,TProperty> para acceder a la información de la propiedad. Por ejemplo, se puede usar para obtener y establecer el valor actual de la propiedad en esta entidad:
string currentValue = context.Entry(blog).Property(e => e.Name).CurrentValue;
context.Entry(blog).Property(e => e.Name).CurrentValue = "1unicorn2";
Ambos métodos Property usados anteriormente devuelven una instancia genérica PropertyEntry<TEntity,TProperty> fuertemente tipificada. Se prefiere usar este tipo genérico porque permite el acceso a valores de propiedad sin tipos de valor boxing. Sin embargo, si el tipo de entidad o propiedad no se conoce en tiempo de compilación, se puede obtener un no genérico PropertyEntry en su lugar:
PropertyEntry propertyEntry = context.Entry(blog).Property("Name");
Esto permite acceder a la información de cualquier inmueble, independientemente de su tipo, a costa de encasillar tipos de valores. Por ejemplo:
object blog = await context.Blogs.SingleAsync(e => e.Id == 1);
object currentValue = context.Entry(blog).Property("Name").CurrentValue;
context.Entry(blog).Property("Name").CurrentValue = "1unicorn2";
En la tabla siguiente se resume la información de propiedad expuesta por PropertyEntry:
Miembro PropertyEntry | Descripción |
---|---|
PropertyEntry<TEntity,TProperty>.CurrentValue | Obtiene y establece el valor actual de la propiedad . |
PropertyEntry<TEntity,TProperty>.OriginalValue | Obtiene y establece el valor original de la propiedad, si está disponible. |
PropertyEntry<TEntity,TProperty>.EntityEntry | Referencia inversa para EntityEntry<TEntity> la entidad. |
PropertyEntry.Metadata | IProperty metadatos de la propiedad . |
PropertyEntry.IsModified | Indica si esta propiedad está marcada como modificada y permite cambiar este estado. |
PropertyEntry.IsTemporary | Indica si esta propiedad está marcada como temporal y permite cambiar este estado. |
Notas:
- El valor original de una propiedad es el valor que tenía la propiedad cuando se consultó la entidad desde la base de datos. Sin embargo, los valores originales no están disponibles si la entidad se desconectó y, a continuación, se adjuntó explícitamente a otro DbContext, por ejemplo con
Attach
oUpdate
. En este caso, el valor original devuelto será el mismo que el valor actual. - SaveChanges solo actualizará las propiedades marcadas como modificadas. Establézcalo IsModified en true para forzar a EF Core a actualizar un valor de propiedad determinado o establézcalo en false para evitar que EF Core actualice el valor de la propiedad.
- Los generadores de valores de EF Core suelen generar valores temporales. Al establecer el valor actual de una propiedad, se reemplazará el valor temporal por el valor especificado y se marcará la propiedad como no temporal. Establézcalo IsTemporary en true para forzar que un valor sea temporal incluso después de que se haya establecido explícitamente.
Trabajar con una navegación única
Varias sobrecargas de EntityEntry<TEntity>.Reference, EntityEntry<TEntity>.Collection y EntityEntry.Navigation permiten el acceso a información sobre una navegación específica.
A las navegaciones de referencia a una sola entidad relacionada se accede a través de los Reference métodos. Las navegaciones de referencia apuntan a los lados "uno" de las relaciones uno a varios y a ambos lados de las relaciones uno a uno. Por ejemplo:
ReferenceEntry<Post, Blog> referenceEntry1 = context.Entry(post).Reference(e => e.Blog);
ReferenceEntry<Post, Blog> referenceEntry2 = context.Entry(post).Reference<Blog>("Blog");
ReferenceEntry referenceEntry3 = context.Entry(post).Reference("Blog");
Las navegaciones también pueden ser colecciones de entidades relacionadas cuando se usan para los lados "varios" de relaciones de uno a varios y varios a varios. Los Collection métodos se usan para acceder a las navegaciones de colecciones. Por ejemplo:
CollectionEntry<Blog, Post> collectionEntry1 = context.Entry(blog).Collection(e => e.Posts);
CollectionEntry<Blog, Post> collectionEntry2 = context.Entry(blog).Collection<Post>("Posts");
CollectionEntry collectionEntry3 = context.Entry(blog).Collection("Posts");
Algunas operaciones son comunes para todas las navegaciones. Se puede acceder a estos para navegaciones de referencia y de recopilación usando el método EntityEntry.Navigation. Tenga en cuenta que solo hay acceso no genérico disponible al acceder a todas las navegaciones juntas. Por ejemplo:
NavigationEntry navigationEntry = context.Entry(blog).Navigation("Posts");
En la tabla siguiente se resumen las formas de usar ReferenceEntry<TEntity,TProperty>, CollectionEntry<TEntity,TRelatedEntity>y NavigationEntry:
Miembro NavigationEntry | Descripción |
---|---|
MemberEntry.CurrentValue | Obtiene y establece el valor actual de la navegación. Esta es toda la colección para las navegaciones de recopilación. |
NavigationEntry.Metadata | INavigationBase metadatos para la navegación. |
NavigationEntry.IsLoaded | Obtiene o establece un valor que indica si la entidad o colección relacionada se ha cargado completamente desde la base de datos. |
NavigationEntry.Load() | Carga la entidad o colección relacionada de la base de datos; consulte Carga explícita de datos relacionados. |
NavigationEntry.Query() | La consulta que EF Core usaría para cargar esta navegación de modo que IQueryable pueda seguir componiéndose; vea Carga explícita de datos relacionados. |
Trabajar con todas las características de una entidad
EntityEntry.Properties devuelve un IEnumerable<T> de PropertyEntry para cada propiedad de la entidad. Esto se puede usar para realizar una acción para cada propiedad de la entidad. Por ejemplo, para establecer cualquier propiedad DateTime en DateTime.Now
:
foreach (var propertyEntry in context.Entry(blog).Properties)
{
if (propertyEntry.Metadata.ClrType == typeof(DateTime))
{
propertyEntry.CurrentValue = DateTime.Now;
}
}
Además, EntityEntry contiene varios métodos para obtener y establecer todos los valores de propiedad al mismo tiempo. Estos métodos usan la PropertyValues clase , que representa una colección de propiedades y sus valores. PropertyValues se puede obtener para los valores actuales o originales, o para los valores almacenados actualmente en la base de datos. Por ejemplo:
var currentValues = context.Entry(blog).CurrentValues;
var originalValues = context.Entry(blog).OriginalValues;
var databaseValues = await context.Entry(blog).GetDatabaseValuesAsync();
Estos objetos PropertyValues no son muy útiles por sí mismos. Sin embargo, se pueden combinar para realizar operaciones comunes necesarias al manipular entidades. Esto resulta útil cuando se trabaja con objetos de transferencia de datos y al resolver conflictos de simultaneidad optimista. En las secciones siguientes se muestran algunos ejemplos.
Establecimiento de valores actuales o originales de una entidad o DTO
Los valores actuales o originales de una entidad se pueden actualizar copiando valores de otro objeto. Por ejemplo, considere un BlogDto
objeto de transferencia de datos (DTO) con las mismas propiedades que el tipo de entidad:
public class BlogDto
{
public int Id { get; set; }
public string Name { get; set; }
}
Esto se puede usar para establecer los valores actuales de una entidad con seguimiento mediante PropertyValues.SetValues:
var blogDto = new BlogDto { Id = 1, Name = "1unicorn2" };
context.Entry(blog).CurrentValues.SetValues(blogDto);
Esta técnica se usa a veces al actualizar una entidad con valores obtenidos de una llamada de servicio o un cliente en una aplicación de n niveles. Tenga en cuenta que el objeto usado no tiene que ser del mismo tipo que la entidad, siempre y cuando tenga propiedades cuyos nombres coincidan con los de la entidad. En el ejemplo anterior, se usa una instancia de DTO BlogDto
para establecer los valores actuales de una entidad seguida Blog
.
Tenga en cuenta que las propiedades solo se marcarán como modificadas si el conjunto de valores difiere del valor actual.
Establecimiento de valores actuales o originales de un diccionario
En el ejemplo anterior se asignan valores de una entidad o una instancia de DTO. El mismo comportamiento está disponible cuando los valores de propiedad se almacenan como pares nombre-valor en un diccionario. Por ejemplo:
var blogDictionary = new Dictionary<string, object> { ["Id"] = 1, ["Name"] = "1unicorn2" };
context.Entry(blog).CurrentValues.SetValues(blogDictionary);
Establecimiento de valores actuales o originales de la base de datos
Los valores actuales o originales de una entidad se pueden actualizar con los valores más recientes de la base de datos llamando a GetDatabaseValues() o GetDatabaseValuesAsync usando el objeto devuelto para establecer valores actuales o originales, o ambos. Por ejemplo:
var databaseValues = await context.Entry(blog).GetDatabaseValuesAsync();
context.Entry(blog).CurrentValues.SetValues(databaseValues);
context.Entry(blog).OriginalValues.SetValues(databaseValues);
Creación de un objeto clonado que contenga valores actuales, originales o de base de datos
El objeto PropertyValues devuelto por CurrentValues, OriginalValues o GetDatabaseValues se puede usar para crear un clon de la entidad mediante PropertyValues.ToObject(). Por ejemplo:
var clonedBlog = (await context.Entry(blog).GetDatabaseValuesAsync()).ToObject();
Tenga en cuenta que ToObject
devuelve una nueva instancia que no realiza el seguimiento de DbContext. El objeto devuelto tampoco tiene relaciones establecidas con otras entidades.
El objeto clonado puede ser útil para resolver problemas relacionados con las actualizaciones simultáneas de la base de datos, especialmente cuando se enlazan datos a objetos de un tipo determinado. Consulte simultaneidad optimista para obtener más información.
Trabajar con todas las navegaciones de una entidad
EntityEntry.Navigations devuelve un IEnumerable<T> de NavigationEntry para cada vez que se navega por la entidad. EntityEntry.References y EntityEntry.Collections hacen lo mismo, pero restringidos a las navegaciones de referencia o colección, respectivamente. Esto se puede usar para realizar una acción en cada una de las navegaciones de la entidad. Por ejemplo, para forzar la carga de todas las entidades relacionadas:
foreach (var navigationEntry in context.Entry(blog).Navigations)
{
navigationEntry.Load();
}
Trabajar con todos los miembros de una entidad
Las propiedades normales y las propiedades de navegación tienen un estado y un comportamiento diferentes. Por lo tanto, es habitual procesar las navegaciones y las no navegaciones por separado, como se muestra en las secciones anteriores. Sin embargo, a veces puede ser útil hacer algo con cualquier miembro de la entidad, independientemente de si es una propiedad o navegación normales. EntityEntry.Member y EntityEntry.Members se proporcionan para este fin. Por ejemplo:
foreach (var memberEntry in context.Entry(blog).Members)
{
Console.WriteLine(
$"Member {memberEntry.Metadata.Name} is of type {memberEntry.Metadata.ClrType.ShortDisplayName()} and has value {memberEntry.CurrentValue}");
}
La ejecución de este código en un blog del ejemplo genera la siguiente salida:
Member Id is of type int and has value 1
Member Name is of type string and has value .NET Blog
Member Posts is of type IList<Post> and has value System.Collections.Generic.List`1[Post]
Sugerencia
La vista de depuración del rastreador de cambios muestra información como esta. La vista de depuración del rastreador de cambios completo se genera a partir de los componentes EntityEntry.DebugView de cada entidad rastreada.
Buscar y BuscarAsync
DbContext.Find, DbContext.FindAsync, DbSet<TEntity>.Findy DbSet<TEntity>.FindAsync están diseñados para una búsqueda eficaz de una sola entidad cuando se conoce su clave principal. Busque primero comprueba si la entidad ya se ha seguido y, si es así, devuelve la entidad inmediatamente. Una consulta de base de datos solo se realiza si la entidad no se rastrea localmente. Por ejemplo, considere este código que llama a Find dos veces para la misma entidad:
using var context = new BlogsContext();
Console.WriteLine("First call to Find...");
var blog1 = await context.Blogs.FindAsync(1);
Console.WriteLine($"...found blog {blog1.Name}");
Console.WriteLine();
Console.WriteLine("Second call to Find...");
var blog2 = await context.Blogs.FindAsync(1);
Debug.Assert(blog1 == blog2);
Console.WriteLine("...returned the same instance without executing a query.");
La salida de este código (incluido el registro de EF Core) al usar SQLite es:
First call to Find...
info: 12/29/2020 07:45:53.682 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
Executed DbCommand (1ms) [Parameters=[@__p_0='1' (DbType = String)], CommandType='Text', CommandTimeout='30']
SELECT "b"."Id", "b"."Name"
FROM "Blogs" AS "b"
WHERE "b"."Id" = @__p_0
LIMIT 1
...found blog .NET Blog
Second call to Find...
...returned the same instance without executing a query.
Observe que la primera llamada no encuentra la entidad localmente y, por tanto, ejecuta una consulta de base de datos. Por el contrario, la segunda llamada devuelve la misma instancia sin consultar la base de datos porque ya se está realizando el seguimiento.
Find devuelve null si no se realiza un seguimiento local de una entidad con la clave especificada y no existe en la base de datos.
Claves compuestas
La función Find también se puede usar con claves compuestas. Por ejemplo, considere una OrderLine
entidad con una clave compuesta formada por el identificador de pedido y el identificador de producto:
public class OrderLine
{
public int OrderId { get; set; }
public int ProductId { get; set; }
//...
}
La clave compuesta debe configurarse en DbContext.OnModelCreating para definir las partes clave y su orden. Por ejemplo:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<OrderLine>()
.HasKey(e => new { e.OrderId, e.ProductId });
}
Observe que OrderId
es la primera parte de la clave y ProductId
es la segunda parte de la clave. Este orden se debe usar al pasar valores de clave a Find. Por ejemplo:
var orderline = await context.OrderLines.FindAsync(orderId, productId);
Uso de ChangeTracker.Entries para acceder a todas las entidades con seguimiento
Hasta ahora solo hemos accedido a una sola EntityEntry a la vez. ChangeTracker.Entries() devuelve un EntityEntry para cada entidad actualmente rastreada por el DbContext. Por ejemplo:
using var context = new BlogsContext();
var blogs = await context.Blogs.Include(e => e.Posts).ToListAsync();
foreach (var entityEntry in context.ChangeTracker.Entries())
{
Console.WriteLine($"Found {entityEntry.Metadata.Name} entity with ID {entityEntry.Property("Id").CurrentValue}");
}
Este código genera el siguiente resultado:
Found Blog entity with ID 1
Found Post entity with ID 1
Found Post entity with ID 2
Tenga en cuenta que se devuelven entradas para blogs y publicaciones. En su lugar, los resultados se pueden filtrar a un tipo de entidad específico mediante la ChangeTracker.Entries<TEntity>() sobrecarga genérica:
foreach (var entityEntry in context.ChangeTracker.Entries<Post>())
{
Console.WriteLine(
$"Found {entityEntry.Metadata.Name} entity with ID {entityEntry.Property(e => e.Id).CurrentValue}");
}
La salida de este código muestra que solo se devuelven entradas:
Found Post entity with ID 1
Found Post entity with ID 2
Además, el uso de la sobrecarga genérica devuelve instancias genéricas EntityEntry<TEntity> . Esto es lo que permite el acceso fluido a la propiedad Id
en este ejemplo.
El tipo genérico usado para el filtrado no tiene que ser un tipo de entidad asignado; En su lugar, se puede usar un tipo base o una interfaz no asignados. Por ejemplo, si todos los tipos de entidad del modelo implementan una interfaz que define su propiedad de clave:
public interface IEntityWithKey
{
int Id { get; set; }
}
A continuación, esta interfaz se puede usar para trabajar con la clave de cualquier entidad con seguimiento de forma fuertemente tipada. Por ejemplo:
foreach (var entityEntry in context.ChangeTracker.Entries<IEntityWithKey>())
{
Console.WriteLine(
$"Found {entityEntry.Metadata.Name} entity with ID {entityEntry.Property(e => e.Id).CurrentValue}");
}
Uso de DbSet.Local para consultar entidades con seguimiento
Las consultas de EF Core siempre se ejecutan en la base de datos y solo devuelven entidades que se han guardado en la base de datos. DbSet<TEntity>.Local proporciona un mecanismo para consultar el DbContext en busca de entidades locales y con seguimiento.
Puesto que DbSet.Local
se utiliza para consultar entidades con seguimiento, es habitual cargar las entidades en el DbContext y, a continuación, trabajar con las entidades cargadas. Esto es especialmente cierto para el enlace de datos, pero también puede ser útil en otras situaciones. Por ejemplo, en el código siguiente, la base de datos se consulta primero para todos los blogs y entradas. El método de extensión Load se utiliza para ejecutar esta consulta, con los resultados siendo monitorizados por el contexto, sin ser devueltos directamente a la aplicación. (El uso ToList
o similar tiene el mismo efecto, pero con la sobrecarga de crear la lista devuelta, que no es necesaria aquí). A continuación, en el ejemplo se usa DbSet.Local
para acceder a las entidades de seguimiento local:
using var context = new BlogsContext();
await context.Blogs.Include(e => e.Posts).LoadAsync();
foreach (var blog in context.Blogs.Local)
{
Console.WriteLine($"Blog: {blog.Name}");
}
foreach (var post in context.Posts.Local)
{
Console.WriteLine($"Post: {post.Title}");
}
Tenga en cuenta que, a diferencia de ChangeTracker.Entries(), DbSet.Local
devuelve instancias de entidad directamente. Se puede obtener un EntityEntry para la entidad devuelta siempre llamando a DbContext.Entry.
Vista local
DbSet<TEntity>.Local devuelve una vista de las entidades que se siguen localmente y que refleja el estado actual de dichas entidades. En concreto, esto significa que:
- Se incluyen las entidades
Added
. Tenga en cuenta que esto no es el caso de las consultas normales de EF Core, yaAdded
que las entidades aún no existen en la base de datos y, por tanto, nunca las devuelve una consulta de base de datos. -
Deleted
están excluidas las entidades. Tenga en cuenta que, una vez más, este no es el caso de las consultas normales de EF Core, ya que las entidades siguen existiendo en la base de datos y, por tanto, son devueltas por las consultas de la base de datos.
Todo esto significa que DbSet.Local
es una vista sobre los datos que reflejan el estado conceptual actual del gráfico de entidades, con Added
entidades incluidas y Deleted
entidades excluidas. Esto coincide con el estado de la base de datos que se espera que sea después de llamar a SaveChanges.
Normalmente es la vista ideal para el enlace de datos, ya que presenta al usuario los datos a medida que lo entienden en función de los cambios realizados por la aplicación.
El código siguiente muestra esto marcando una publicación como Deleted
y, a continuación, agregando una nueva publicación, marcándola como Added
:
using var context = new BlogsContext();
var posts = await context.Posts.Include(e => e.Blog).ToListAsync();
Console.WriteLine("Local view after loading posts:");
foreach (var post in context.Posts.Local)
{
Console.WriteLine($" Post: {post.Title}");
}
context.Remove(posts[1]);
context.Add(
new Post
{
Title = "What’s next for System.Text.Json?",
Content = ".NET 5.0 was released recently and has come with many...",
Blog = posts[0].Blog
});
Console.WriteLine("Local view after adding and deleting posts:");
foreach (var post in context.Posts.Local)
{
Console.WriteLine($" Post: {post.Title}");
}
La salida de este código es:
Local view after loading posts:
Post: Announcing the Release of EF Core 5.0
Post: Announcing F# 5
Post: Announcing .NET 5.0
Local view after adding and deleting posts:
Post: What’s next for System.Text.Json?
Post: Announcing the Release of EF Core 5.0
Post: Announcing .NET 5.0
Observe que la publicación eliminada se quita de la vista local y se incluye la publicación agregada.
Uso de Local para agregar y quitar entidades
DbSet<TEntity>.Local devuelve una instancia de LocalView<TEntity>. Se trata de una implementación de ICollection<T> que genera y responde a las notificaciones cuando se agregan y quitan entidades de la colección. (Este es el mismo concepto que ObservableCollection<T>, pero se implementa como una proyección sobre las entradas existentes de seguimiento de cambios de EF Core, en lugar de como una colección independiente).
Las notificaciones de la vista local se enlazan al seguimiento de cambios de DbContext para que la vista local permanezca sincronizada con DbContext. Concretamente:
- Agregar una nueva entidad a
DbSet.Local
hace que dbContext realice el seguimiento de ella, normalmente en elAdded
estado . (Si la entidad ya tiene un valor de clave generado, se realiza el seguimiento comoUnchanged
en su lugar). - Quitar una entidad de
DbSet.Local
hace que se marque comoDeleted
. - Una entidad que es rastreada por DbContext aparecerá automáticamente en la colección
DbSet.Local
. Por ejemplo, ejecutar una consulta para incorporar más entidades automáticamente hace que se actualice la vista local. - Una entidad marcada como
Deleted
se quitará de la colección local automáticamente.
Esto significa que la vista local se puede usar para manipular entidades de seguimiento simplemente agregando y quitando de la colección. Por ejemplo, vamos a modificar el código de ejemplo anterior para agregar y quitar publicaciones de la colección local:
using var context = new BlogsContext();
var posts = await context.Posts.Include(e => e.Blog).ToListAsync();
Console.WriteLine("Local view after loading posts:");
foreach (var post in context.Posts.Local)
{
Console.WriteLine($" Post: {post.Title}");
}
context.Posts.Local.Remove(posts[1]);
context.Posts.Local.Add(
new Post
{
Title = "What’s next for System.Text.Json?",
Content = ".NET 5.0 was released recently and has come with many...",
Blog = posts[0].Blog
});
Console.WriteLine("Local view after adding and deleting posts:");
foreach (var post in context.Posts.Local)
{
Console.WriteLine($" Post: {post.Title}");
}
La salida permanece sin cambios en el ejemplo anterior porque los cambios realizados en la vista local se sincronizan con DbContext.
Uso de la vista local para el enlace de datos de Windows Forms o WPF
DbSet<TEntity>.Local constituye la base para el enlace de datos a entidades de EF Core. Sin embargo, tanto Windows Forms como WPF funcionan mejor cuando se usan con el tipo específico de colección de notificación que esperan. La vista local admite la creación de estos tipos de colección específicos:
- LocalView<TEntity>.ToObservableCollection() devuelve un ObservableCollection<T> para el enlace de datos de WPF.
- LocalView<TEntity>.ToBindingList() devuelve un BindingList<T> para el enlace de datos de Windows Forms.
Por ejemplo:
ObservableCollection<Post> observableCollection = context.Posts.Local.ToObservableCollection();
BindingList<Post> bindingList = context.Posts.Local.ToBindingList();
Consulte Introducción a WPF para obtener más información sobre el enlace de datos de WPF con EF Core y Introducción a Windows Forms para obtener más información sobre el enlace de datos de Windows Forms con EF Core.
Sugerencia
La vista local de una instancia específica de DbSet se crea de manera diferida cuando se accede por primera vez y luego se almacena en caché. La creación de LocalView es rápida y no usa memoria significativa. Sin embargo, llama a DetectChanges, que puede resultar lento con un gran número de entidades. Las colecciones creadas por ToObservableCollection
y ToBindingList
también se crean perezosamente y luego se almacenan en caché. Ambos métodos crean nuevas colecciones, que pueden ser lentas y usar una gran cantidad de memoria cuando participan miles de entidades.