فئة المجال:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
تكوين الكيان:
public class CategoryConfiguration : IEntityTypeConfiguration<Category>
{
public void Configure(EntityTypeBuilder<Category> entity)
{
entity.HasKey(c => c.Id);
entity.Property(c => c.Id).IsRequired().ValueGeneratedOnAdd();
entity.Property(c => c.Name).IsRequired().HasMaxLength(55);
}
}
المكان الذي أضيف فيه الكيان إلى قاعدة البيانات (CategoryDto مطابق للفئة)
[HttpPost]
public IActionResult CreateCategory([FromBody] CategoryDto categoryDto)
{
var category = _mapper.Map<Category>(categoryDto);
_unitOfWork.Categories.Add(category);
_unitOfWork.Complete();
_mapper.Map(category, categoryDto);
return Created($"{Request.Path}/{category.Id}", categoryDto);
}
يمكنك التحقق من الكود الكامل هنا:
عندما أرسل طلب POST بعمود معرف محدد مسبقًا (على سبيل المثال 1000 أو 15) ، سيكون هذا الرقم المحدد مسبقًا هو معرف الكيان الذي تمت إضافته إلى قاعدة البيانات. كما ستبدأ جميع المعرفات الإضافية من تلك القيمة.
بغض النظر عن المعرف الموجود في الحقل ، عند إنشاء سجل في قاعدة البيانات ، يجب تعيين قيمة العمود بشكل صحيح.
إصدار MySQL: 8.0.19-mysql
نظام التشغيل: Windows 10
Pomelo.EntityFrameworkCore.MySQL الإصدار: 3.1.1
إصدار Microsoft.AspNetCore.App: 3.1.201
هل يمكنك مشاركة مخطط MySQL لجدول الفئات؟ ليس من الواضح تمامًا ما هو السلوك الذي تحتاجه ، هل هذا المعرّف قدمه المستخدم أم يتم إنشاؤه تلقائيًا في DB باستخدام auto_increment
؟ إذا كان الأمر الأول ، فلن تحتاج إلى طريقة ValueGeneratedOnAdd .
هل يمكنك مشاركة مخطط MySQL لجدول الفئات؟ ليس من الواضح تمامًا ما هو السلوك الذي تحتاجه ، هل هذا المعرّف قدمه المستخدم أم يتم إنشاؤه تلقائيًا في DB باستخدام
auto_increment
؟ إذا كان الأمر الأول ، فلن تحتاج إلى طريقة ValueGeneratedOnAdd .
حسنًا ، على سبيل المثال ، عندما أستخدم MS SQL ، إذا قمت بإرسال طلب POST ببيانات مثل هذه:
{
"Id":12137123,
"Name":"Some Category"
}
سيتم حذف هذا المعرف العشوائي عند إنشاء إدخال في قاعدة البيانات وسيتم تعيين الصحيح في عمود المعرف. الهدف من كل ذلك هو أنه إذا حاول المستخدم الضار وضع معرف خاطئ ، فسيتم كسر ديسيبل الخاص بي
ستستخدم MySQL أي قيمة معرّف تقدمها. فقط إذا لم تقدم قيمة ، فستولد MySQL قيمة auto_increment
لك. عند إنشاء معرّف ، سيستخدم أعلى قيمة موجودة بالفعل في الجدول (أو 0
خلاف ذلك) كمبدأ.
ألق نظرة على 3.6.9 باستخدام AUTO_INCREMENT في مستندات MySQL:
يمكن استخدام السمة AUTO_INCREMENT لإنشاء هوية فريدة للصفوف الجديدة:
[...]
لم يتم تحديد أي قيمة لعمود AUTO_INCREMENT ، لذلك قامت MySQL بتعيين أرقام التسلسل تلقائيًا. يمكنك أيضًا تعيين 0 بشكل صريح للعمود لإنشاء أرقام التسلسل
[...]
إذا تم الإعلان عن العمود "ليس فارغًا" ، فمن الممكن أيضًا تعيين "فارغ" للعمود لإنشاء أرقام التسلسل.
[...]
عندما تقوم بإدراج أي قيمة أخرى في عمود AUTO_INCREMENT ، يتم تعيين العمود إلى تلك القيمة ويتم إعادة تعيين التسلسل بحيث تتبع القيمة التالية التي تم إنشاؤها تلقائيًا بالتسلسل من قيمة العمود الأكبر.
يوضح الكود التالي أن:
ج #
باستخدام System.Diagnostics ؛
باستخدام System.Linq ؛
باستخدام Microsoft.EntityFrameworkCore ؛
باستخدام Microsoft.Extensions.Logging ؛
مشكلة مساحة الاسم ConsoleTemplate
{
IceCream من الدرجة العامة
{
عامة int IceCreamId {get؛ يضع؛ }
اسم السلسلة العامة {get؛ يضع؛ }
}
public class Context : DbContext
{
public DbSet<IceCream> IceCreams { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseMySql(
"server=127.0.0.1;port=3306;user=root;password=;database=Issue1092",
b => b.ServerVersion("8.0.20-mysql"))
.UseLoggerFactory(
LoggerFactory.Create(b => b
.AddConsole()
.AddFilter(level => level >= LogLevel.Information)))
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<IceCream>(
entity =>
{
// Because the property contains "Id", the following lines are added automatically,
// so no need to specify them explicitly (does not do any harm however):
// entity.Property(e => e.IceCreamId)
// .ValueGeneratedOnAdd();
});
}
}
internal class Program
{
private static void Main()
{
using var context = new Context();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
context.IceCreams.AddRange(
new IceCream
{
IceCreamId = 42, // explicitly set ID
Name = "Vanilla",
},
new IceCream
{
// <-- let MySQL use it's auto_increment behavior
Name = "Chocolate",
});
context.SaveChanges();
var iceCreams = context.IceCreams
.OrderBy(i => i.IceCreamId)
.ToList();
Debug.Assert(iceCreams.Count == 2);
Debug.Assert(iceCreams[0].IceCreamId == 42);
Debug.Assert(iceCreams[0].Name == "Vanilla");
Debug.Assert(iceCreams[1].IceCreamId == 43);
Debug.Assert(iceCreams[1].Name == "Chocolate");
}
}
}
It generates the following SQL:
```sql
info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
Entity Framework Core 3.1.3 initialized 'Context' using provider 'Pomelo.EntityFrameworkCore.MySql' with options: ServerVersion 8.0.20 MySql SensitiveDataLoggingEnabled DetailedErrorsEnabled
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (12ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
CREATE DATABASE `Issue1092`;
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (56ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
CREATE TABLE `IceCreams` (
`IceCreamId` int NOT NULL AUTO_INCREMENT,
`Name` longtext CHARACTER SET utf8mb4 NULL,
CONSTRAINT `PK_IceCreams` PRIMARY KEY (`IceCreamId`)
);
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (8ms) [Parameters=[@p0='42', @p1='Vanilla' (Size = 4000)], CommandType='Text', CommandTimeout='30']
INSERT INTO `IceCreams` (`IceCreamId`, `Name`)
VALUES (<strong i="6">@p0</strong>, @p1);
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (4ms) [Parameters=[@p0='Chocolate' (Size = 4000)], CommandType='Text', CommandTimeout='30']
INSERT INTO `IceCreams` (`Name`)
VALUES (@p0);
SELECT `IceCreamId`
FROM `IceCreams`
WHERE ROW_COUNT() = 1 AND `IceCreamId` = LAST_INSERT_ID();
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT `i`.`IceCreamId`, `i`.`Name`
FROM `IceCreams` AS `i`
ORDER BY `i`.`IceCreamId`
لذا فإن السلوك الذي تواجهه متوقع ومتوافق مع الوثائق.
لحل مشكلتك ، ما عليك سوى إعادة تعيين Category.Id
إلى 0
قبل الاتصال بـ SaveChanges()
.
نظرًا لأنك تستخدم نمطًا صريحًا لمستودع التخزين ، فقد يكون الحل البسيط هو تجاوز طريقة Add()
:
ج #
مستودع فئة الملخص العام
{
الفراغ الظاهري العام إضافة (كيان TEntity) // <- جعلها افتراضية
{
السياق
}
}
فئة عامة فئة المستودع: المستودع
{
تجاوز عام باطل إضافة (كيان الفئة)
{
الكيان معرف = 0 ؛
base.Add (كيان) ؛
}
}
""
شكرا على الجواب والمساعدة
التعليق الأكثر فائدة
ستستخدم MySQL أي قيمة معرّف تقدمها. فقط إذا لم تقدم قيمة ، فستولد MySQL قيمة
auto_increment
لك. عند إنشاء معرّف ، سيستخدم أعلى قيمة موجودة بالفعل في الجدول (أو0
خلاف ذلك) كمبدأ.ألق نظرة على 3.6.9 باستخدام AUTO_INCREMENT في مستندات MySQL:
يوضح الكود التالي أن:
ج #
باستخدام System.Diagnostics ؛
باستخدام System.Linq ؛
باستخدام Microsoft.EntityFrameworkCore ؛
باستخدام Microsoft.Extensions.Logging ؛
مشكلة مساحة الاسم ConsoleTemplate
{
IceCream من الدرجة العامة
{
عامة int IceCreamId {get؛ يضع؛ }
اسم السلسلة العامة {get؛ يضع؛ }
}
}
لذا فإن السلوك الذي تواجهه متوقع ومتوافق مع الوثائق.
حل ممكن:
لحل مشكلتك ، ما عليك سوى إعادة تعيين
Category.Id
إلى0
قبل الاتصال بـSaveChanges()
.نظرًا لأنك تستخدم نمطًا صريحًا لمستودع التخزين ، فقد يكون الحل البسيط هو تجاوز طريقة
Add()
:ج #: IRepositoryحيث TEntity: class() .Add (الكيان) ؛
مستودع فئة الملخص العام
{
الفراغ الظاهري العام إضافة (كيان TEntity) // <- جعلها افتراضية
{
السياق
}
}
فئة عامة فئة المستودع: المستودع، IC categoryRepository
{
تجاوز عام باطل إضافة (كيان الفئة)
{
الكيان معرف = 0 ؛
base.Add (كيان) ؛
}
}
""