Почему-то не могу получить Identity после вставки записи
Вот такой код:
public void Save(DataTable save)
{
using var conn = new OleDbConnection(_connectionString);
using var dataAdapter = new OleDbDataAdapter(new OleDbCommand(GetSelectCommand(), conn));
var commandBuilder = new OleDbCommandBuilder(dataAdapter);
dataAdapter.InsertCommand = commandBuilder.GetInsertCommand();
dataAdapter.UpdateCommand = commandBuilder.GetUpdateCommand();
dataAdapter.RowUpdated += (s, e) =>
{
if (e.StatementType != StatementType.Insert) return;
OleDbCommand cmd = new(_identityQueryRepository.GetQuery(_dictionarySettings.ConnectionSettings.DbKind), e.Command.Connection);
e.Row[_dictionarySettings.KeyColumnName] = (int)cmd.ExecuteScalar();
};
dataAdapter.Update(save);
}
public class IdentityQueryRepository: IIdentityQueryRepository
{
public string GetQuery(DbKind dbKind)
{
if (dbKind == DbKind.MSAccess)
return "SELECT @@IDENTITY";
if (dbKind == DbKind.MSSQL)
return "SELECT SCOPE_IDENTITY()";
throw new NotImplementedException("Данная БД не поддерживается");
}
}
По идее, я должен получить Identity после вставки, но получаю null...
Работаю в данном случае с SQL Server.
Ответы (1 шт):
Я попробовал - у меня не с первого раза получилось, но получилось. Жаль что вы не привели самодостаточный код, надо хорошо угадать что написать что бы выполнилась вставка.
Ваш код надо поменять совсем чуть-чуть. Секрет в том, что надо использовать текущую команду. Где её найти? В аргументах. Команда должна остаться та же
public void Save(DataTable save)
{
using var conn = new OleDbConnection(_connectionString);
using var dataAdapter = new OleDbDataAdapter(new OleDbCommand(GetSelectCommand(), conn));
var commandBuilder = new OleDbCommandBuilder(dataAdapter);
dataAdapter.InsertCommand = commandBuilder.GetInsertCommand();
dataAdapter.UpdateCommand = commandBuilder.GetUpdateCommand();
dataAdapter.RowUpdated += (s, e) =>
{
if (e.StatementType != StatementType.Insert) return;
// меняем тут ***********
e.Command.CommandText =_identityQueryRepository.GetQuery(_dictionarySettings.ConnectionSettings.DbKind);
// И тут
e.Row[_dictionarySettings.KeyColumnName] = Convert(e.Command.ExecuteScalar()).ToInt32();
};
dataAdapter.Update(save);
}
И ещё маленькое но. Мелкософту не нравится почему то int32, он зачем-то в int32 засовывает decimal (у меня в int поле всунул), - лучше делать через Convert.ToInt32, потому что прямой каст может дать ошибку.
Касательно MSSMS - можно легко убедится что он не закрывает команду в рамках одной вкладке выполнив select @@spid;. Не запрещено не закрывать команду. (Хотя возможно он хранит не команду а сессию или как то ещё) В singlethread если закрыть команду и открыть новую - из пула подтянется старая, но из-за DataAdapter - этого у вас не произошло. После spid можете так же посмотреть в "зеркало" так
select cast(replace(replace(replace((select text from sys.dm_exec_sql_text(r.sql_handle)),'&','&') ,'<','<') ,'>','>') as xml) sql_text,
* from sys.dm_exec_requests r with(nolock) where sql_handle is not null
`
P.S. Так же понравилась ссылка https://stackoverflow.com/questions/42648/best-way-to-get-identity-of-inserted-row по ней можно почитать какую инфу можно с базы по автогенераторам вытрусить.
Ну и приведу недостающий код (больше времени на это потратил чем на придумать ответ)
var conn = new OleDbConnection(_connectionString);
var command = new OleDbCommand("select * from my_table");
var da = new OleDbDataAdapter(command);
DataTable dt = new DataTable();
da.Fill(dt);
dt.Rows.Add(new object[] { null }); // По числу столбцов, один создал для теста
Save(dt);