#sql-server #.net-core #entity-framework-core #asp.net-core-2.0
#sql-сервер #.net-ядро #entity-framework-core #asp.net-core-2.0
Вопрос:
Я пытаюсь сделать простую вещь: получить максимальное значение столбца. Знаете, это довольно просто, просто запустите SELECT MAX (столбец) ИЗ таблицы;
Проблема заключается в том, когда я пытаюсь сделать это в моем проекте .NET Core 2.1, используя Entity Framework.
У меня есть функция, которая должна возвращать следующее значение столбца.
private int getColumNextValue(string table, string column)
{
string query = $"SELECT MAX({column}) 1 FROM {table};";
return base.context.Database.ExecuteSqlCommand(query);
}
Запрос генерируется корректно:
Однако вместо реального значения возвращается значение -1.
Но когда я запускаю точно такой же запрос в Sql Server Management Studio, результат оказывается правильным:
Что происходит?
Комментарии:
1.
MAX(ID) 1
может легко возвращать дубликаты, если, например, удалена последняя строка. Вот почему он редко используется для генерации идентификаторов. Вместо этого используйтеIDENTITY
или ПОСЛЕДОВАТЕЛЬНОСТЬ . У EF также нет проблем при работе со значениями идентификаторов. Для настройки внешних ключей не обязательно заранее знать идентификаторы. При добавлении нового корневого объекта и вложенных объектов EF сгенерирует соответствующие инструкции для установки всех идентификаторов в соответствии с отношениями, настроенными в DbContext2. Привет @PanagiotisKanavos; Спасибо за совет. Я понимаю стратегию идентификации, но дело в том, что я имею дело с устаревшей базой данных, которую ее создатель не использовал
IDENTITY
, поэтому я должен использовать стратегию MAX 1… Печально, но это реальность. Итак, мне нужно выполнить запрос insert в устаревшую базу данных через вторичное соединение, вот почему мне нужно выполнить запрос «на лету», не привязывая его к модели.3. Dapper не меняет того факта, что MAX 1 создает дубликаты. Вы все равно получили бы их, независимо от того, использовали ли вы Dapper или straight ADO.NET SqlCommand. Фактически, если у вас более 1 одновременного пользователя, вы рискуете, что оба они получат одинаковое максимальное значение и попытаются вставить данные, используя один и тот же идентификатор
4. Фактически, EF Core предлагает стратегию HiLo key для безопасной генерации идентификаторов на клиенте.
5. Большое вам спасибо за помощь, @PanagiotisKanavos.
Ответ №1:
Для ExecuteSqlCommand
она возвращает только количество затронутых строк. Это не привело бы к выполнению запроса и возврату результата.
В качестве обходного пути вы могли бы попробовать, например:
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<User> User { get; set; }
public async Task<T> ExecuteScalarAsync<T>(string rawSql, params object[] parameters)
{
var conn = Database.GetDbConnection();
using (var command = conn.CreateCommand())
{
command.CommandText = rawSql;
if (parameters != null)
foreach (var p in parameters)
command.Parameters.Add(p);
await conn.OpenAsync();
return (T)await command.ExecuteScalarAsync();
}
}
}
И используйте ExecuteScalarAsync
подобное
public async Task<IActionResult> Index()
{
string query = $"SELECT MAX(SEQ) 1 FROM [User];";
var result = await _context.ExecuteScalarAsync<int>(query);
return View();
}
Комментарии:
1. Вы открываете соединение, связанное с контекстом, но не закрываете его впоследствии, что нехорошо.
Ответ №2:
Я использовал QueryFirstOrDefault<int>
для решения своей проблемы. Я написал этот вспомогательный метод для повторного использования:
private int GetColumNextValue(string table, string column)
{
using (SqlConnection conn = new SqlConnection(this.configuration.GetConnectionString("MyConnection")))
{
string query = $"SELECT MAX({column}) 1 FROM {table};";
return conn.QueryFirstOrDefault<int>(query);
}
}
Я надеюсь, что это может помочь другим людям.