¿Actualizar un registro sin consultar primero?


103

Digamos que consulto la base de datos y cargo una lista de elementos. Luego abro uno de los elementos en un formulario de vista detallada y, en lugar de volver a consultar el elemento fuera de la base de datos, creo una instancia del elemento desde la fuente de datos en la lista.

¿Hay alguna forma de actualizar el registro de la base de datos sin obtener el registro del artículo individual?

Aquí hay una muestra de cómo lo estoy haciendo ahora:

dataItem itemToUpdate = (from t in dataEntity.items
                                 where t.id == id
                                 select t).FirstOrDefault();

Luego, después de extraer el registro, actualizo algunos valores en el elemento y empujo el registro hacia atrás:

itemToUpdate.itemstatus = newStatus;
dataEntity.SaveChanges();

Creo que habría una mejor manera de hacer esto, ¿alguna idea?


2
No es una manera terriblemente mala de hacer las cosas. ¿Tiene acceso simultáneo a esa tabla?
Henk Holterman

Creo que este es el uso que un ORM como EF está exactamente ahí para servir. ¿Para permitir que las operaciones dentro del contexto de la aplicación se realicen en los objetos que desea crear / modificar / eliminar, sin preocuparse por la implementación de la base de datos subyacente?
Pero P.

40
Creo que para los desarrolladores con experiencia en TSQL que intentan aceptar y adoptar ORM, es un poco ineficiente buscar un registro solo para actualizarlo y nunca utilizar los datos obtenidos. Este concepto de que un desarrollador no necesita preocuparse por la implementación de la base de datos subyacente es una tontería. Cuanto más sepa un desarrollador sobre todo el sistema, mejor puede ser la solución. Las opciones nunca son malas.
barrypicker

1
El enfoque ORM está bien para objetos reales, pero si también almacena otras cosas en su base de datos (como grandes blobs binarios) puede ser muy útil poder actualizarlos sin cargar primero el contenido original.
BrainSlugs83

Respuestas:


68

Deberías usar Attach () método .

Adjuntar y separar objetos


16
¿Puede dar un ejemplo?
Bart Calixto

16
context.Products.Attach (producto); context.Entry (producto) .State = EntityState.Modified;
Gabriel

6
@Gabriel ¿No actualizará esto todas las propiedades? ¿Qué pasa si solo quiero modificar uno?
David Pfeffer

22
Sí, esto actualizará todas las propiedades. Si desea actualizar una sola propiedad, puede hacer esto: context.Entry (usuario) .Property (x => x.Property) .IsModified = true; (eche un vistazo aquí stackoverflow.com/a/5567616/57369 )
Gabriel

6
Solo me gustaría agregar ese contexto.Entry () solo está disponible en .net 4.1, si todavía está usando 4.0 (como yo), consulte esto para ver la alternativa: stackoverflow.com/questions/7113434/where-is- entrada de contexto que es esencialmente: context.ObjectStateManager.ChangeObjectState (yourObject, EntityState.Modified);
dyslexicanaboko

39

También puede utilizar SQL directo contra la base de datos utilizando el contexto del almacén de datos. Ejemplo:

dataEntity.ExecuteStoreCommand
   ("UPDATE items SET itemstatus = 'some status' WHERE id = 123 ");

Por motivos de rendimiento, es posible que desee pasar variables en lugar de una única cadena SQL codificada. Esto permitirá a SQL Server almacenar en caché la consulta y reutilizarla con parámetros. Ejemplo:

dataEntity.ExecuteStoreCommand
   ("UPDATE items SET itemstatus = 'some status' WHERE id = {0}", new object[] { 123 });

ACTUALIZACIÓN - para EF 6.0

dataEntity.Database.ExecuteSqlCommand
       ("UPDATE items SET itemstatus = 'some status' WHERE id = {0}", new object[] { 123 });

9
¿Por qué degradaría esta respuesta sin dejar un comentario? Esta sugerencia responde directamente a la pregunta de los autores originales.
barrypicker

18
ExecuteStoreCommandno es realmente una forma de EF de hacer esto, solo usa el DbConnectioncontenido dentro del DbContextpara ejecutar un comando. No es agnóstico a la base de datos, y mucho menos a la persistencia (por ejemplo, este ejemplo se bloqueará si el OP cambia a XML).
solo.otro.programador

9
@ just.another.programmer: un gran poder conlleva una gran responsabilidad.
barrypicker

13
¿Tiene que ser agnóstico a la persistencia? No es como si cambiaras tu sistema de almacenamiento cada dos días.
David

5
@ BrainSlugs83: intente usar EF en servidores de enlaces que solo admitan OpenQuery: mucha diversión. A veces es absolutamente necesario SQL sin formato para hacer el trabajo. No siempre se puede aislar el código para realizar pruebas. No es un mundo perfecto ahí fuera.
barrypicker

20

El código:

ExampleEntity exampleEntity = dbcontext.ExampleEntities.Attach(new ExampleEntity { Id = 1 });
exampleEntity.ExampleProperty = "abc";
dbcontext.Entry<ExampleEntity>(exampleEntity).Property(ee => ee.ExampleProperty).IsModified = true;
dbcontext.Configuration.ValidateOnSaveEnabled = false;
dbcontext.SaveChanges();

El resultado TSQL:

exec sp_executesql N'UPDATE [dbo].[ExampleEntities]
SET [ExampleProperty ] = @0
WHERE ([Id] = @1)
',N'@0 nvarchar(32),@1 bigint',@0='abc',@1=1

Nota:

La línea "IsModified = true" es necesaria porque cuando crea el nuevo objeto ExampleEntity (solo con la propiedad Id poblada) todas las demás propiedades tienen sus valores predeterminados (0, nulo, etc.). Si desea actualizar la base de datos con un "valor predeterminado", el marco de la entidad no detectará el cambio y, a continuación, la base de datos no se actualizará.

Por ejemplo:

exampleEntity.ExampleProperty = null;

no funcionará sin la línea "IsModified = true", porque la propiedad ExampleProperty, ya es nula cuando creó el objeto ExampleEntity vacío, debe decirle a EF que esta columna debe actualizarse, y este es el propósito de esta línea.


Esto es perfecto. Acabo de probar esto y es exactamente lo que quería. Quiero que los cambios pasen por la infraestructura EF (incluido el uso del proyecto EntityFramework.Triggers) pero quería poder cambiar 1 columna con solo tener la clave principal.
MikeJansen

11

Si los DataItemcampos tiene EF prevalidará (como campos que no aceptan valores NULL), tendremos que deshabilitar esa validación para este contexto:

DataItem itemToUpdate = new DataItem { Id = id, Itemstatus = newStatus };
dataEntity.Entry(itemToUpdate).Property(x => x.Itemstatus).IsModified = true;
dataEntity.Configuration.ValidateOnSaveEnabled = false;
dataEntity.SaveChanges();
//dataEntity.Configuration.ValidateOnSaveEnabled = true;

De lo contrario, podemos intentar satisfacer la validación previa y aún actualizar solo la columna única:

DataItem itemToUpdate = new DataItem
{
    Id = id,
    Itemstatus = newStatus,
    NonNullableColumn = "this value is disregarded - the db original will remain"
};
dataEntity.Entry(itemToUpdate).Property(x => x.Itemstatus).IsModified = true;
dataEntity.SaveChanges();

Asumiendo que dataEntityes unSystem.Data.Entity.DbContext

Puede verificar la consulta generada agregando esto a DbContext:

/*dataEntity.*/Database.Log = m => System.Diagnostics.Debug.Write(m);


0

Funciona algo diferente en EF Core:

Puede haber una forma más rápida de hacer esto en EF Core, pero lo siguiente asegura una ACTUALIZACIÓN sin tener que hacer una SELECCIÓN (probado con EF Core 2 y JET en .NET Framework 4.6.2):

Asegúrese de que su modelo no tenga propiedades IsRequired

Luego use la siguiente plantilla (en VB.NET):

    Using dbContext = new MyContext()
        Dim bewegung = dbContext.MyTable.Attach(New MyTable())
        bewegung.Entity.myKey = someKey
        bewegung.Entity.myOtherField = "1"

        dbContext.Entry(bewegung.Entity).State = EntityState.Modified
        dbContext.Update(bewegung.Entity)

        Dim BewegungenDescription = (From tp In dbContext.Model.GetEntityTypes() Where tp.ClrType.Name = "MyTable" Select tp).First()
        For Each p In (From prop In BewegungenDescription.GetProperties() Select prop)
            Dim pp = dbContext.Entry(bewegung.Entity).Property(p.Name)
            pp.IsModified = False
        Next
        dbContext.Entry(bewegung.Entity).Property(Function(row) row.myOtherField).IsModified = True
        dbContext.SaveChanges()
    End Using

-1

En términos generales, si usó Entity Framework para consultar todos los elementos y guardó el objeto de entidad, puede actualizar los elementos individuales en el objeto de entidad y llamar SaveChanges()cuando haya terminado. Por ejemplo:

var items = dataEntity.Include("items").items;
// For each one you want to change:
items.First(item => item.id == theIdYouWant).itemstatus = newStatus;
// After all changes:
dataEntity.SaveChanges();

La recuperación del elemento que desea no debería generar una nueva consulta.


Interesante respuesta, ¿alguien más ha confirmado esto?
Ian

5
Esto hace lo mismo que el problema de OP: recupera el registro completo y luego actualízalo. .First () deserializa el objeto.
Jerther
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.