Как я могу получить идентификатор вставленной сущности в Entity Framework? [закрыто]

612

У меня проблема с Entity Framework в Asp.net. Я хочу получить значение Id всякий раз, когда я добавляю объект в базу данных. Как я могу это сделать?

Ahmet
источник
16
Ответ Ладислава Мринки, приведенный ниже, является правильным ответом, и его следует принять как таковой.
CShark
3
ОП опубликовал только один раз со своей учетной записью, этот вопрос будет более конкретным. Это дало ему / ей почти 2к репутации (!), Ответ не помечается как принятый, поскольку аккаунт был оставлен с того дня.
MurariAlex
1
Если вам просто нужен идентификатор, чтобы использовать его в отношениях внешнего ключа, вы можете вместо этого просто установить свойство навигации зависимой сущности для ранее добавленной сущности. Таким образом, вам не нужно беспокоиться о том, чтобы позвонить SaveChangesпрямо сейчас, чтобы получить удостоверение личности. Дальнейшее чтение здесь .
B12Toaster
Для Entity Framework Core 3.0 добавьте параметр acceptAllChangesOnSuccess как true: await _context.SaveChangesAsync (true);
Майк

Ответы:

1007

Это довольно легко. Если вы используете DB генерируется Идентификаторы (например , IDENTITYв MS SQL) вам просто нужно добавить объект к ObjectSetи SaveChangesна связанные ObjectContext. Idбудет автоматически заполнено для вас:

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

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

Рамки Entity по умолчанию следует каждый INSERTс , SELECT SCOPE_IDENTITY()когда автоматически генерируемые Idиспользуются s.

Ладислав Мрнка
источник
34
Итак, вы задаете неправильный вопрос. Если у вас есть проблема с исключением, вы должны задать вопрос, показывающий ваше исключение (и внутреннее исключение) и фрагмент кода, вызывающий эту ошибку.
Ладислав Мрнка
3
. Убедитесь в том , что Entity добавлении является действительным Entity , например, что - нибудь с ограничением базы данных «не нуль» должны быть заполнены и т.д.
фран
4
@LadislavMrnka: А как насчет навигационных свойств вновь созданного объекта? Похоже, они не заполняются автоматически после сохранения изменений.
Исаак Кляйнман
4
Давайте рассмотрим следующий сценарий: table1 должен генерировать идентификатор @@, чтобы использовать его в table2. Предполагается, что и table1, и table2 находятся в одной и той же транзакции, например, одна операция SaveChange должна сохранять данные обеих таблиц. @@ identity не будет сгенерировано, пока не будет вызвано «SaveChanges». как мы можем решить эту ситуацию?
Араш
7
@Djeroen: если вы используете Id, сгенерированный базой данных, вам необходимо сначала вставить эти объекты в базу данных. Если вам нужно иметь идентификаторы перед вставкой, вы должны создать собственную логику для получения уникальных идентификаторов до вставки данных и не использовать столбец идентификаторов в базе данных.
Ладислав Мрнка
125

Я использовал ответ Ладислава Мрнки для успешного получения идентификаторов при использовании Entity Framework, однако я публикую здесь, потому что я его не использовал (т. Е. Использовал там, где это не требовалось) и думал, что я опубликую свои выводы здесь, Если люди хотят «решить» проблему, которая у меня была.

Рассмотрим объект Заказа, который имеет отношение внешнего ключа с Клиентом. Когда я добавил нового клиента и новый заказ одновременно, я делал что-то вроде этого;

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

Однако в моем случае это не требовалось, потому что у меня были отношения внешнего ключа между customer.Id и order.CustomerId

Все, что мне нужно было сделать, это;

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

Теперь, когда я сохраняю изменения, идентификатор, который генерируется для клиента, также добавляется в заказ. Мне не нужны дополнительные шаги

Я знаю, что это не отвечает на первоначальный вопрос, но я подумал, что это может помочь разработчикам, которые плохо знакомы с EF, чрезмерно использовать ответ с верхним голосом для чего-то, что может не потребоваться.

Это также означает, что обновления завершаются за одну транзакцию, что может привести к потере данных о потерях (либо все обновления завершены, либо нет).

mark_h
источник
8
Спасибо ! ВАЖНО Совет по передовой практике: var order = new Order {Customer = customer}; Это показывает способность Entity Framework назначать связанный объект, и идентификатор будет автоматически обновляться.
LA Guy 88
Спасибо за эту дополнительную информацию к ответу. Очень помог в сохранении БД туда-обратно при использовании EF.
Прасад Корхале
Спасибо тебе большое за это. Это именно то, что я искал. Я просто полагал, что может быть способ лучше, чем дважды вызывать «SaveChanges»
Джош
1
Также укажите в своем ответе (желательно жирным шрифтом), что это разрешает поведение транзакции. Если один из объектов не удалось добавить, часть транзакции не будет успешной. Например, добавление человека и ученика. Человек преуспевает, а ученик терпит неудачу. Теперь человек избыточен. Спасибо за это отличное решение, хотя!
Имран Фаруки
32

Вам необходимо перезагрузить объект после сохранения изменений. Потому что он был изменен триггером базы данных, который не может быть отслежен EF. Так что нам нужно перезагрузить сущность снова из БД,

db.Entry(MyNewObject).GetDatabaseValues();

затем

int id = myNewObject.Id;

Посмотрите на ответ @jayantha в следующем вопросе:

Как я могу получить идентификатор вставленной сущности в Entity Framework при использовании defaultValue?

Поиск @христианского ответа в следующем вопросе тоже может помочь:

Entity Framework Обновить контекст?

Qmaster
источник
2
Привет, когда я делаю это, я получаю ошибку компиляции, которая говорит: не может разрешить свойства, например Id или Name, не могли бы вы мне помочь?
Мортеза Азизи
1
@MortezaAzizi Кажется, это ошибка не обработанной или нулевой проблемы со свойством, но не могли бы вы подробнее объяснить или написать какой-нибудь фрагмент кода?
QMaster
3
Не нужно делать, .GetDatabaseValues();если вы просто сохранили свою модель в БД. Идентификатор должен автоматически заполнить модель.
vapcguy
3
@QMaster За исключением того, что EF также может (и выполняет) тот же самый оператор SQL и заполняет идентификатор вашего объекта. В противном случае, как можно сохранить несколько зависимых объектов одновременно? Как это возможно, GetDatabaseValues()если он не знает идентификатор объекта, который нужно искать в базе данных?
крильгар
2
@vapcguy я вижу. Спасибо за внимание. Я проверю это в обеих версиях EF ASAP.
QMaster
16

Пожалуйста, обратитесь по этой ссылке.

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

Вы должны установить свойство StoreGeneratedPattern в идентичность и затем попробовать свой собственный код.

Или вы также можете использовать это.

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

  int id = myNewObject.Id; // Your Identity column ID
}
Азар Мансури
источник
7
То же, что и топ с ответом.
Lewis86
2
StoreGeneratedPatternбыл недостающий кусок для меня.
BurnsBA
1
Так можно работать с ORACLE и ON-Insert Trigger,
Карл
ссылка битая - припаркованный домен
t.durden
Привет, с Oracle мне пришлось установить StoreGeneratedPattern в Entity - перейти в просмотрщик моделей, найти таблицу и PK, выбрать свойство StoreGeneratedPattern и установить Identity. Это работает так, как указано выше. Это для случая, когда у вас есть триггер, который заполняет ваш PK из последовательности при вставке.
Роб
15

Сохраняемый объект должен иметь правильный Idпосле распространения изменений в базе данных.

Snowbear
источник
27
Вы видели внутреннее исключение? ;-)
Snowbear
6

Вы можете получить идентификатор только после сохранения, вместо этого вы можете создать новый Guid и назначить перед сохранением.

Вадуганатан Пиллаппан
источник
4

Я сталкиваюсь с ситуацией, когда мне нужно вставить данные в базу данных и одновременно потребовать первичный идентификатор, используя структуру сущностей. Решение :

long id;
IGenericQueryRepository<myentityclass, Entityname> InfoBase = null;
try
 {
    InfoBase = new GenericQueryRepository<myentityclass, Entityname>();
    InfoBase.Add(generalinfo);
    InfoBase.Context.SaveChanges();
    id = entityclassobj.ID;
    return id;
 }
Мистер кунал
источник
4

Все ответы очень хорошо подходят для их собственных сценариев, но я отличался тем, что назначил int PK непосредственно из объекта (TEntity), который Add () возвратил в переменную int, подобную этой;

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
}

поэтому вместо создания нового объекта вы просто берете то, что хотите :)

РЕДАКТИРОВАТЬ Для г-на @GertArnold:

введите описание изображения здесь

IteratioN7T
источник
3
Не очень полезно добавлять нераскрытый метод для присвоения идентификатора. Это даже не связано с Entity Framework.
Герт Арнольд
1
@GertArnold .. у меня был тот же вопрос, что и у ОП, я нашел ответы и превратил его в однострочное утверждение, и я никогда не слышал о термине нераскрытый метод, который нужно объяснить?
IteratioN7T
3
Это просто означает, что вы не показываете (не раскрываете) все, что делаете. Может быть, это просто неясно описано, я не знаю. Что я знаю, так это то, что Add(если это так DbSet.Add) не магическим образом присваивает значение EmployeeId.
Герт Арнольд
3
Не без SaveChanges. Невозможно. Если у вас нет другого процесса, который присваивает EmployeeId, например, в Employeeконструкторе. ИЛИ вы используете ядро ​​EF ForSqlServerUseSequenceHiLo. Во всяком случае, вы не даете всю картину.
Герт Арнольд
3
Мое последнее замечание будет: это не стандартное поведение EF. Вы должны дать более подробную информацию о вашей среде. Версия EF, марка и версия базы данных, класс сотрудника, детали отображения.
Герт Арнольд
3
Repository.addorupdate(entity, entity.id);
Repository.savechanges();
Var id = entity.id;

Это будет работать

Сакет Чуби
источник
4
Репозиторий не несет ответственности за сохранение изменений в базе данных. Это работа UnitOfWork.
Челидзе
5
Более того, абсолютно неясно, что это Repositoryтакое. И «это сработает» - какое-то объяснение!
Герт Арнольд
2

Есть две стратегии:

  1. Использовать базу данных ID( intили GUID)

    Минусы:

    Вы должны выполнить, SaveChanges()чтобы получить IDтолько что сохраненные объекты.

    Плюсы:

    Можно использовать intличность.

  2. Использовать сгенерированный клиент ID- только GUID.

    Плюсы: минимизация SaveChangesопераций. Возможность вставлять большой граф новых объектов за одну операцию.

    Минусы:

    Разрешено только для GUID

Владислав Фурдак
источник
1

При первом использовании кода EF 6.x

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

и инициализировать таблицу базы данных, он поместит

(newsequentialid())

в свойствах таблицы под заголовком «Значение по умолчанию» или «Связывание», позволяя заполнять идентификатор при его вставке.

Проблема в том, что если вы создадите таблицу и добавите

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]

часть позже, будущие update-database не добавят обратно (newsequentialid ())

Чтобы исправить это, нужно стереть миграцию, удалить базу данных и перенастроить ... или вы можете просто добавить (newsequentialid ()) в конструктор таблиц.

Джефф Ли
источник
Можете ли вы уточнить, куда идет newsequentialid ()? У меня уже есть модели баз данных, созданные вручную, не использующие сначала код EF, и он не заполняет идентификатор, потому что это не мой первичный ключ. Хотел бы найти решение для этого.
Джош
1
@josh Предполагая, что код вначале имеет тип Guid, щелкните правой кнопкой мыши свою таблицу в Sql Server Management Studio, щелкните столбец Id и на вкладке Свойства столбца внутри (Общие) вы увидите значение по умолчанию или привязку. Если вы создаете таблицы БД вручную, обратитесь к NewSequentialId или NewId . Общий случай использования - что-то вродеwhen -column- is NULL, then NEWID()
Джефф Ли