¿Cómo puedo obtener el ID de la entidad insertada en Entity Framework? [cerrado]

611

Tengo un problema con Entity Framework en Asp.net. Quiero obtener el valor de Id cada vez que agrego un objeto a la base de datos. ¿Cómo puedo hacer esto?

ahmet
fuente
16
La respuesta de Ladislav Mrnka a continuación es la respuesta correcta y debe aceptarse como tal.
CShark
3
El OP solo publicó una vez con su cuenta, esta pregunta es más específica. Esto le otorgó casi 2k en reputación (!), La respuesta no está marcada como aceptada porque la cuenta está abandonada desde ese día.
MurariAlex
1
Si solo necesita el Id para usarlo en una relación de clave externa, puede considerar simplemente establecer la propiedad de navegación de su entidad dependiente en la entidad previamente agregada. De esta manera, no necesita preocuparse por llamar de SaveChangesinmediato solo para obtener la identificación. Lectura adicional aquí .
B12Toaster
Para Entity Framework Core 3.0, agregue el parámetro acceptAllChangesOnSuccess como verdadero: aguarde _context.SaveChangesAsync (verdadero);
Mike

Respuestas:

1006

Es bastante fácil Si está utilizando DB genera identificadores (como IDENTITYen MS SQL) sólo tiene que añadir a la entidad ObjectSety SaveChangessobre las enfermedades relacionadas ObjectContext. Idserá llenado automáticamente por usted:

using (var context = new MyContext())
{
  context.MyEntities.Add(myNewObject);
  context.SaveChanges();

  int id = myNewObject.Id; // Yes it's here
}

El marco de la entidad por defecto sigue a cada uno INSERTcon SELECT SCOPE_IDENTITY()cuando Idse utilizan s generados automáticamente .

Ladislav Mrnka
fuente
34
Entonces estás haciendo una pregunta equivocada. Si tiene un problema con la excepción, debe hacer una pregunta que muestre su excepción (y la excepción interna) y el fragmento de código que causa ese error.
Ladislav Mrnka
3
. Asegúrese de que la entidad va a añadir es una entidad válida por ejemplo, cualquier cosa con una restricción de la base de datos "no nulo" debe ser llenado, etc.
fran
44
@LadislavMrnka: ¿Qué pasa con las propiedades de navegación del objeto recién creado? No parece que se llenen automáticamente después de guardar los cambios.
Isaac Kleinman
44
Consideremos el siguiente escenario: se supone que la tabla1 genera una identidad @@ para usarla en la tabla2. Se supone que tanto la tabla1 como la tabla2 están en la misma transacción, por ejemplo, una operación SaveChange debe guardar los datos de ambas tablas. La identidad @@ no se generará a menos que se invoque un 'SaveChanges'. ¿Cómo podemos abordar esta situación?
Arash
77
@Djeroen: si usa el ID generado por la base de datos, primero debe insertar esos objetos en la base de datos. Si necesita tener identificadores antes de la inserción, debe crear su propia lógica para obtener identificadores únicos antes de insertar los datos y no utilizar la columna de identidad en la base de datos.
Ladislav Mrnka
124

Había estado usando la respuesta de Ladislav Mrnka para recuperar con éxito los identificadores cuando usaba el Entity Framework, sin embargo, estoy publicando aquí porque había estado usándolo mal (es decir, usándolo donde no era necesario) y pensé que publicaría mis hallazgos aquí en caso la gente está buscando "resolver" el problema que tuve.

Considere un objeto Order que tenga una relación de clave externa con el Cliente. Cuando agregué un nuevo cliente y un nuevo pedido al mismo tiempo, estaba haciendo algo como esto;

var customer = new Customer(); //no Id yet;
var order = new Order(); //requires Customer.Id to link it to customer;
context.Customers.Add(customer);
context.SaveChanges();//this generates the Id for customer
order.CustomerId = customer.Id;//finally I can set the Id

Sin embargo, en mi caso, esto no era necesario porque tenía una relación de clave externa entre customer.Id y order.CustomerId

Todo lo que tenía que hacer era esto;

var customer = new Customer(); //no Id yet;
var order = new Order{Customer = customer}; 
context.SaveChanges();//adds customer.Id to customer and the correct CustomerId to order

Ahora, cuando guardo los cambios, la identificación que se genera para el cliente también se agrega al pedido. No necesito los pasos adicionales.

Soy consciente de que esto no responde a la pregunta original, pero pensé que podría ayudar a los desarrolladores que son nuevos en EF a usar en exceso la respuesta más votada para algo que puede no ser necesario.

Esto también significa que las actualizaciones se completan en una sola transacción, evitando potencialmente los datos de orphin (todas las actualizaciones se completan o ninguna lo hace).

mark_h
fuente
8
Gracias ! IMPORTANTE Sugerencia de mejores prácticas: var order = new Order {Customer = customer}; Esto muestra el poder de Entity Framework para asignar objetos relacionados y el Id se actualizará automáticamente.
LA Guy 88
Gracias por esta información adicional a la respuesta. Fue muy útil para guardar un viaje de ida y vuelta de DB al usar EF.
Prasad Korhale
Muchas gracias por esto. Esto es exactamente lo que estaba buscando. Solo creí que podría haber una manera mejor que llamar a "SaveChanges" dos veces
Josh
1
Mencione también en su respuesta (preferiblemente en negrita) que esto resuelve un comportamiento de transacción. Si no se puede agregar uno de los objetos, parte de la transacción no tendrá éxito. Ej. Agregar persona y estudiante. La persona tiene éxito y el estudiante falla. Ahora la persona es redundante. ¡Gracias por esta gran solución!
Imran Faruqi
32

Debe volver a cargar la entidad después de guardar los cambios. Porque ha sido alterado por un disparador de base de datos que EF no puede rastrear. Entonces, necesitamos volver a cargar la entidad desde la base de datos,

db.Entry(MyNewObject).GetDatabaseValues();

Entonces

int id = myNewObject.Id;

Mira la respuesta de @jayantha en la siguiente pregunta:

¿Cómo puedo obtener el Id. De la entidad insertada en Entity Framework cuando uso defaultValue?

Mirar la respuesta @christian en la siguiente pregunta también puede ayudar:

Entity Framework Refresh context?

QMaster
fuente
2
Hola, cuando me gusta esto, aparece el error de compilación que dice: no puedo resolver las propiedades, por ejemplo, Id o Nombre, ¿me pueden ayudar por favor?
Morteza Azizi
1
@MortezaAzizi Parece ser un error de un problema de propiedad no manejado o nulo, pero ¿puede explicar o escribir un fragmento de código?
QMaster
3
No debería tener que hacerlo .GetDatabaseValues();si acaba de guardar su modelo en la base de datos. La ID debe repoblarse al modelo automáticamente.
vapcguy
3
@QMaster Excepto que EF también es capaz (y lo hace) de ejecutar esa misma instrucción SQL y rellena la ID de su objeto. De lo contrario, ¿cómo puede guardar varios objetos dependientes al mismo tiempo? ¿Cómo podría hacerlo GetDatabaseValues()si no conoce la ID del objeto a buscar en la base de datos?
krillgar
2
@vapcguy ya veo. Gracias por tu atención. Comprobaré eso en ambas versiones de EF ASAP.
QMaster
16

Por favor, consulte este enlace.

http://www.ladislavmrnka.com/2011/03/the-bug-in-storegeneratedpattern-fixed-in-vs-2010-sp1/

Debe establecer la propiedad de StoreGeneratedPattern en identidad y luego probar su propio código.

O también puedes usar esto.

using (var context = new MyContext())
{
  context.MyEntities.AddObject(myNewObject);
  context.SaveChanges();

  int id = myNewObject.Id; // Your Identity column ID
}
Azhar Mansuri
fuente
77
Igual que la respuesta más votada.
Lewis86
2
StoreGeneratedPatternfue la pieza que faltaba para mí.
BurnsBA
1
Ese es el camino a seguir cuando se trabaja con ORACLE y ON-Insert Trigger,
Karl
el enlace está roto - dominio estacionado
t.durden
Hola, con Oracle, tuve que configurar StoreGeneratedPattern en Entity: vaya al visor de modelos, busque su tabla y PK, seleccione la propiedad StoreGeneratedPattern y configúrelo en Identity. Funciona como indica la respuesta anterior. Esto es para el caso donde tiene un disparador que llena su PK de una secuencia en la inserción.
Rob
15

El objeto que está guardando debe tener una correcta Iddespués de propagar los cambios en la base de datos.

Snowbear
fuente
27
¿Has visto la excepción interior? ;-)
Snowbear
6

Puede obtener ID solo después de guardar, en su lugar, puede crear un nuevo Guid y asignarlo antes de guardar.

Vaduganathan Pillappan
fuente
4

Me encuentro con una situación en la que necesito insertar los datos en la base de datos y, al mismo tiempo, necesito la identificación principal utilizando el marco de la entidad. Solución:

long id;
IGenericQueryRepository<myentityclass, Entityname> InfoBase = null;
try
 {
    InfoBase = new GenericQueryRepository<myentityclass, Entityname>();
    InfoBase.Add(generalinfo);
    InfoBase.Context.SaveChanges();
    id = entityclassobj.ID;
    return id;
 }
Señor Kunal
fuente
4

Todas las respuestas son muy adecuadas para sus propios escenarios, lo que hice diferente es que asigné la PK int directamente desde el objeto (TEntity) que Add () devolvió a una variable int como esta;

using (Entities entities = new Entities())
{
      int employeeId = entities.Employee.Add(new Employee
                        {
                            EmployeeName = employeeComplexModel.EmployeeName,
                            EmployeeCreatedDate = DateTime.Now,
                            EmployeeUpdatedDate = DateTime.Now,
                            EmployeeStatus = true
                        }).EmployeeId;

      //...use id for other work
}

así que en lugar de crear un objeto completamente nuevo, simplemente toma lo que quieras :)

EDITAR Para el Sr. @GertArnold:

ingrese la descripción de la imagen aquí

IteratioN7T
fuente
3
No es realmente útil agregar un método no revelado para asignar un Id. Esto ni siquiera está relacionado con Entity Framework.
Gert Arnold
1
@GertArnold ... Tuve la misma pregunta que el OP. Encontré las respuestas y las hice en una sola línea, y nunca he oído hablar del término cuidado de método no revelado .
IteratioN7T
3
Eso solo significa que no muestra (revela) todo lo que hace. Tal vez simplemente no se describe claramente, no lo sé. Lo que sí sé es que Add(si es así DbSet.Add) no asigna mágicamente un valor a EmployeeId.
Gert Arnold
3
No sin SaveChanges. Imposible. A menos que tenga algún otro proceso que asigne EmployeeId, por ejemplo, en Employeeel constructor. O estás usando EF core ForSqlServerUseSequenceHiLo. De todos modos, no estás dando la imagen completa.
Gert Arnold
3
Mi último comentario será: este no es el comportamiento estándar de EF. Debería dar más detalles de su entorno. Versión EF, marca y versión de la base de datos, clase de empleado, sus detalles de mapeo.
Gert Arnold
3
Repository.addorupdate(entity, entity.id);
Repository.savechanges();
Var id = entity.id;

Esto funcionará

Saket Choubey
fuente
44
El repositorio no es responsable de guardar los cambios en la base de datos. Es el trabajo de UnitOfWork.
tchelidze
55
Además, no está absolutamente claro qué Repositoryes. ¡Y "esto funcionará" es una explicación!
Gert Arnold
2

Hay dos estrategias:

  1. Usar generado por la base de datos ID( into GUID)

    Contras:

    Debe realizar SaveChanges()para obtener las IDentidades recién guardadas.

    Pros:

    Puede usar intidentidad.

  2. Usar cliente generado ID- solo GUID.

    Pros: Minificación de SaveChangesoperaciones. Capaz de insertar un gran gráfico de nuevos objetos por una operación.

    Contras:

    Permitido solo para GUID

Vladislav Furdak
fuente
1

Cuando usa el código EF 6.x primero

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }

e inicializar una tabla de base de datos, pondrá un

(newsequentialid())

dentro de las propiedades de la tabla debajo del encabezado Valor predeterminado o Enlace, lo que permite que la ID se complete a medida que se inserta.

El problema es si crea una tabla y agrega el

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]

parte posterior, las futuras bases de datos de actualización no volverán a agregar el (newsequentialid ())

Para corregir la forma correcta es borrar la migración, eliminar la base de datos y volver a migrar ... o simplemente puede agregar (newsequentialid ()) en el diseñador de la tabla.

Jeff Li
fuente
¿Puedes explicar dónde va newsequentialid ()? Ya tengo modelos de bases de datos creados a mano, que no usan el código EF primero, y no llenan la identificación porque no es mi clave principal. Me encantaría encontrar una solución para eso.
Josh
1
@josh Suponiendo que el código primero es tipo Guid, haga clic con el botón derecho en su tabla en Sql Server Management Studio, haga clic en la columna Id, y debajo de la pestaña Propiedades de columna, dentro de (General), verá Valor predeterminado o Enlace. Si está creando tablas de bases de datos a mano, consulte NewSequentialId o NewId . El caso de uso general es algo comowhen -column- is NULL, then NEWID()
Jeff Li