AutoIncrement в Entity Framework
Использовал раньше для работы с БД sql команды. Потихоньку перехожу на EF.
Раньше, я просто пропускал поле id при добавлении строки и БД сама создавала следующий id. Теперь, поле id оставлять пустым нельзя, а если использовать
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)],
то id генерируется какой-то рандомный.
Во первых, не попадет ли он таким образом в уже существующий, во вторых, я хочу чтобы мои id продолжали идти по порядку, так, как это происходит с sql командами. Как это сделать?
И сразу добавлю еще вопрос. После добавления новой строки, мне нужно сразу получить ее id, раньше для этого я отправлял sql запрос с max(id), но это костыль, как это сделать по человечески?
var bill = new Bill();
bill.user_id = Program.f1.user_id.ToString();
bill.shop_id = Program.f1.shop_id.ToString();
bill.client_id = client.id;
bill.total = sumToPay.ToString();
bill.discount = discount.ToString();
bill.date = MySql.DateTimeNow();
billContext.bills.Add(bill);
billContext.SaveChanges();
internal class Bill
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string id { get; set; }
public string user_id { get; set; }
public string shop_id { get; set; }
public string client_id { get; set; }
public string total { get; set; }
public string discount { get; set; }
public string date { get; set; }
}
CREATE TABLE `bills` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`shop_id` int(11) DEFAULT NULL,
`client_id` int(11) DEFAULT NULL,
`total` float DEFAULT NULL,
`discount` float DEFAULT NULL,
`date` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
UPDATE
Создаю класс для таблицы Ремонтов. В данной таблице есть много связей, и по связям как раз и вопрос. Конечно, перед тем как задавать вопрос, пытался его найти в гугле, но там описывают кейс создания связей используя CodeFirst. У меня же используются КривыеРукиFirst, так что ответ найти не вышло. Имеем:
public class Remont
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
[Column("status")]
public string Status { get; set; }
[Column("client_id")]
public int ClientId { get; set; }
[Column("details")]
public string Details { get; set; }
[Column("user_id")]
public int UserId { get; set; }
[Column("price_sum")]
public decimal PriceSum { get; set; }
[Column("date_added")]
public DateTime Created { get; set; }
[Column("date_mod")]
public DateTime Updated { get; set; }
[Column("shop_id")]
public int ShopId { get; set; }
[Column("user_id_master")]
public int MasterId { get; set; }
[Column("deleted")]
public int Deleted { get; set; }
ClientContext clientContext = new ClientContext();
public Client Client
{
get { return clientContext.clients.Where(n => n.id == ClientId).First(); }
set { ClientId = Client.id; }
}
//public List<Remont_> RemontTovars { get; set; }
//[ForeignKey("user_id")]
//public User User { get; set; }
//[ForeignKey("shop_id")]
//public Shop Shop { get; set; }
//[ForeignKey("user_id_master")]
// User Master { get; set; }
}
Проблема следующая, есть поле Client, которое должно содержать клиента, с id = ClientId. Как я понял, в CodeFirst эта связь создалась бы автоматом. Я попытался выйти из ситуации костылем
get { return clientContext.clients.Where(n => n.id == ClientId).First(); }
set { ClientId = Client.id; }
Но это тоже не работает. Как это организовать по-человечески?
P.S. Закомментированные поля, так же нужно реализовать, думаю, у меня это выйдет по примеру с Клиентом. Возможно, вопросы будут только к списку.
P.P.S. Русский мною никогда не изучался и грамматика на интуитивном уровне. Так что не обессудьте.
Ответы (1 шт):
Раньше, я просто пропускал поле id при добавлении строки и БД сама создавала следующий id. Теперь, поле id оставлять пустым нельзя.
Свойство id можно оставлять пустым, если явно указано, что значение для него будет генерироваться в базе данных.
После добавления новой строки, мне нужно сразу получить ее id
var bill = new Bill();
// ...
billContext.bills.Add(bill);
billContext.SaveChanges();
Console.WriteLine(bill.id); // здесь будет значение из БД
При выполнении этого кода провайдер EF Core сгенерирует SQL-запрос INSERT с последующим SELECT для получения ID.
После выполнения строки billContext.SaveChanges(); экземпляр bill обретёт значение ID, полученное из базы.
Это при заданном атрибуте DatabaseGenerated(DatabaseGeneratedOption.Identity).
раньше для этого я отправлял sql запрос с max(id), но это костыль, как это сделать по человечески?
Да, это костыль. В MySql для получения ID последней вставленной строки используется LAST_INSERT_ID().
id генерируется какой-то рандомный
Значение генерируется в БД. БД ничего не знает, кто и как к ней обращается. Неважно, будет это SQL-запрос с помощью чистого ADO.NET, через micro-ORM наподобие Dapper, или с помощью LINQ-ORM типа Entity Framework. БД просто выдаст очередное значение - всё!
Почему на скриншоте такое большое различие между айдишниками? Скорее всего в промежутке между вставками этих строк были другие неудачные вставки, от которых отказались. Или строки были просто удалены.
Также нельзя исключать, что есть какие-то особенности СУБД MySql. Я не являюсь специалистом по ней. Но это сильно вряд ли.
А главное, вас не должны интересовать эти айдишники. Когда вы создаёте объект в C# (C++, Java, любом другом языке), вас же не заботит, по какому адресу в памяти он будет размещён? Точно так же не должно заботить, какой номер назначен строке в таблице БД.
Оставьте компьютеру ковыряться с циферками, он для этого предназначен. Человек должен заниматься творчеством!
Пройдёмся теперь по другим ошибкам.
В C# для именования свойств принят стиль PascalCase. Следовательно, класс должен выглядеть так:
internal class Bill
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("id")]
public int Id { get; set; }
[Column("user_id")]
public int UserId { get; set; }
[Column("shop_id")]
public int ShopId { get; set; }
[Column("client_id")]
public int ClientId { get; set; }
[Column("total")]
public decimal Total { get; set; }
[Column("discount")]
public decimal Discount { get; set; }
[Column("date")]
public DateTime Date { get; set; }
}
В атрибуте Column задан маппинг для колонок таблицы в БД.
В комментариях вы пишете:
Как я понимаю, бд все равно возвращает все значения в виде строк.
Это не так.
Вон же у вас в CREATE TABLE явно заданы типы колонкам: int(11), float, datetime. Неужто эти типы затем не используются?
Ещё замечание. Судя по названиям: total и discount - в этих колонках хранятся денежные значения. Никогда не используйте для них вещественные типы! В БД для этого используйте decimal(15,2), а в C# ему соответствует тип decimal.
Вы сперва создали базу данных с помощью DDL. А затем вручную написали классы сущностей для Entity Framework. Такой подход называется Database First.
Можно сгенерировать классы контекста и сущностей автоматически по уже существующей БД:
Reverse Engineering
Creating a Model for an Existing Database in Entity Framework Core
