update tabla query migraciones framework ejemplo editar delete datos con actualizar c# entity-framework

c# - tabla - migraciones entity framework



Ignorar inserciĆ³n de clave duplicada con Entity Framework (4)

Dado que esta es su clave principal, sus opciones son limitadas. Si esta no fuera su clave principal, y solo un índice único, asumiendo que se trata de SQL Server, podría configurar su clave única para ignorar los duplicados.

Lo que podría sugerir es simplemente envolver un try / catch alrededor de Agregar y comer la excepción si la excepción es un error de clave duplicada.

También puede ver si su objeto admite el método AddOrUpdate() . Sé que esto está soportado en las implementaciones de Code First. Creo que en este caso se agregará una nueva o se actualizará si existe la fila. Sin embargo, esto podría implicar un viaje a la base de datos para ver si el usuario ya existe para saber si hacer una adición o actualización. Y, en algunos casos, es posible que no desee realizar una actualización.

Creo que si fuera yo, iría a la ruta Try / Catch.

Estoy usando ASP.NET MVC4 con Entity Framework Code First. Tengo una tabla llamada "usuarios", con clave principal "UserId". Esta tabla puede tener más de 200,000 entradas.

Necesito insertar otros 50 usuarios. Yo podría hacer esto como

foreach(User user in NewUsers){ context.Add(user); } dbcontext.SaveChanges();

El problema es que uno o más de esos nuevos usuarios ya podrían existir en la base de datos. Si los agrego y luego trato de guardar, arroja un error y no se agrega ninguno de los válidos. Podría modificar el código para hacer esto:

foreach(User user in NewUsers){ if(dbcontext.Users.FirstOrDefault(u => u.UserId) == null) { dbcontext.Users.Add(user); } } dbcontext.SaveChanges();

que funcionaria El problema es que, entonces, tiene que ejecutar una consulta 50 veces en una tabla de más de 200,000. Entonces, mi pregunta es, ¿cuál es el método más eficiente en el rendimiento para insertar a estos usuarios, ignorando cualquier duplicado?


El siguiente método de extensión le permitirá insertar registros de cualquier tipo mientras ignora los duplicados:

public static void AddRangeIgnore(this DbSet dbSet, IEnumerable<object> entities) { var entitiesList = entities.ToList(); var firstEntity = entitiesList.FirstOrDefault(); if (firstEntity == null || !firstEntity.HasKey() || firstEntity.HasIdentityKey()) { dbSet.AddRange(entitiesList); return; } var uniqueEntities = new List<object>(); using (var dbContext = _dataService.CreateDbContext()) { var uniqueDbSet = dbContext.Set(entitiesList.First().GetType()); foreach (object entity in entitiesList) { var keyValues = entity.GetKeyValues(); var existingEntity = uniqueDbSet.Find(keyValues); if (existingEntity == null) { uniqueEntities.Add(entity); uniqueDbSet.Attach(entity); } } } dbSet.AddRange(uniqueEntities); } public static object[] GetKeyValues(this object entity) { using (var dbContext = _dataService.CreateDbContext()) { var entityType = entity.GetType(); dbContext.Set(entityType).Attach(entity); var objectStateEntry = ((IObjectContextAdapter)dbContext).ObjectContext.ObjectStateManager.GetObjectStateEntry(entity); var value = objectStateEntry.EntityKey .EntityKeyValues .Select(kv => kv.Value) .ToArray(); return value; } } public static bool HasKey(this object entity) { using (var dbContext = _dataService.CreateDbContext()) { var entityType = entity.GetType(); dbContext.Set(entityType).Attach(entity); var objectStateEntry = ((IObjectContextAdapter)dbContext).ObjectContext.ObjectStateManager.GetObjectStateEntry(entity); return objectStateEntry.EntityKey != null; } } public static bool HasIdentityKey(this object entity) { using (var dbContext = _dataService.CreateDbContext()) { var entityType = entity.GetType(); dbContext.Set(entityType).Attach(entity); var objectStateEntry = ((IObjectContextAdapter)dbContext).ObjectContext.ObjectStateManager.GetObjectStateEntry(entity); var keyPropertyName = objectStateEntry.EntityKey .EntityKeyValues .Select(kv => kv.Key) .FirstOrDefault(); if (keyPropertyName == null) { return false; } var keyProperty = entityType.GetProperty(keyPropertyName); var attribute = (DatabaseGeneratedAttribute)Attribute.GetCustomAttribute(keyProperty, typeof(DatabaseGeneratedAttribute)); return attribute != null && attribute.DatabaseGeneratedOption == DatabaseGeneratedOption.Identity; } }


Puede filtrar los usuarios existentes con una consulta

foreach(User user in NewUsers.Where(us => !dbcontext.Users.Any(u => u.userId == us.userId))) { dbcontext.Users.Add(user); } dbcontext.SaveChanges();

EDITAR:

Como se señaló en los comentarios, la propuesta anterior dará como resultado una convocatoria de SQL para cada elemento de la colección NewUsers. Podría confirmar eso con SQL Server Profiler.

Un resultado interesante del perfil es el sql algo extraño generado por EF para cada elemento (los nombres de los modelos son diferentes que en el OP, pero la consulta es la misma):

exec sp_executesql N''SELECT CASE WHEN ( EXISTS (SELECT 1 AS [C1] FROM [dbo].[EventGroup] AS [Extent1] WHERE [Extent1].[EventGroupID] = @p__linq__0 )) THEN cast(1 as bit) WHEN ( NOT EXISTS (SELECT 1 AS [C1] FROM [dbo].[EventGroup] AS [Extent2] WHERE [Extent2].[EventGroupID] = @p__linq__0 )) THEN cast(0 as bit) END AS [C1] FROM ( SELECT 1 AS X ) AS [SingleRowTable1]'',N''@p__linq__0 int'',@p__linq__0=10

Bastante buen código para hacer el trabajo de un sencillo de una sola línea.

Mi punto de vista es que escribir un código declarativo agradable y legible y dejar que el compilador y el optimizador hagan el trabajo sucio es una gran actitud. Este es uno de los casos en que el resultado de este estilo es sorprendente y hay que ensuciarse.


Puedes hacerlo:

var newUserIDs = NewUsers.Select(u => u.UserId).Distinct().ToArray(); var usersInDb = dbcontext.Users.Where(u => newUserIDs.Contains(u.UserId)) .Select(u => u.UserId).ToArray(); var usersNotInDb = NewUsers.Where(u => !usersInDb.Contains(u.UserId)); foreach(User user in usersNotInDb){ context.Add(user); } dbcontext.SaveChanges();

Esto ejecutará una única consulta en su base de datos para encontrar usuarios que ya existen, y luego los filtrará de su conjunto de NewUsers .