معرفی SequelizeDotNet: فلسفهی Database-First سکوئلایز، اینبار در داتنت
نویسنده: محسن میرشاهرضا
تاریخ: ۱۴۰۵/۰۵/۰۹ ۱۷:۵۵
آدرس: www.dntips.ir
Model.findAll({ where: {...}, include: [...] }) دارید کوئری میزنید. Associationها، hookهای lifecycle، read replica — همه هستن، و همهچیز به اندازهی کافی به SQL نزدیک میمونه که هیچوقت حس نکنید دارید با یک انتزاع مبارزه میکنید.DbContext/entity رو تعریف میکنید، و migrationها توضیح میدن دیتابیس چطور باید تغییر کنه تا با کد شما هماهنگ بشه. این مدل عالیه وقتی اپلیکیشن شما مالک schema است. ولی وقتی دیتابیس از قبل وجود داره، متعلق به یک تیم دیگه (یا حتی یک codebase دیگه، شاید به یک زبان کاملاً متفاوت) است، یا وقتی اصلاً نمیخواید کد اپلیکیشن تصمیم بگیره schema چه شکلی باشه، این مدل خیلی خوب جواب نمیده.RenameColumnAsync، AddColumnAsync، DropTableAsync — که دولوپر خودش عمداً صداشون میزنه. عملیاتهای مخرب نیاز به force: true دارن و در production قفلن مگر اینکه صریحاً override بشن. هیچ چیزی شبیه sync({ alter: true }) سکوئلایز («دیتابیس رو هرجور لازمه با مدلم هماهنگ کن») وجود نداره — چون دقیقاً همین دسته از فیچرهاست که sync خودکار رو در production خطرناک میکنه، و حتی مستندات خود سکوئلایز هم به همین دلیل ازش منع میکنه.EntityTypeFactory با استفاده از System.Reflection.Emit یک CLR type واقعی بهازای هر جدول میسازه، که بهصورت per-table کش میشه با تضمین lazy و thread-safe (دقیقاً یکبار emit میشه، حتی زیر بار concurrent). یک ستون رو در دیتابیس تغییر بدید، همون کوئری بعدی روی اون جدول یک type تازه emitشده که این تغییر رو منعکس میکنه تحویل میده — بدون rebuild، بدون restart، بدون فایل .cs تولیدشدهای که باید sync نگهش دارید.Include("Author.Publisher.Country") سه لایهی foreign key رو بهصورت سه کوئری جداگانه، کششده، و فیلترشده با IN (...) طی میکنه. IncludeMany("Tags", "dbo", "PostTags", "FK_PostTags_Posts", "FK_PostTags_Tags") همین کار رو از طریق یک جدول واسط صریحاً نامگذاریشده انجام میده. هیچوقت چیزی پشت یک query plan مبهم و خودکار مخفی نمیشه.StatementIntentClassifier کلمهی کلیدی ابتدایی کوئری خام رو بررسی میکنه دقیقاً برای اینکه همون مسیر میانبر هم درست مسیریابی بشه.SchemaWatcher پول میکنه، مقایسه میکنه، و type runtime جدول تغییریافته رو بهصورت atomic و نسخهدار جایگزین میکنه — بدون ریاستارت کردن پروسه.CdcChangeWatcher جدولهای change سکوئل سرور رو به یک event سیشارپ تبدیل میکنه که یکبار subscribe میکنید و تمام.IDialect، ولی فقط در سطح unit-test (query translator) تست شدن — من هنوز نمونهی زندهای از اینها ندارم که رفتار end-to-end رو روش تأیید کنم. اگه شما دارید، مشارکتتون فوقالعاده خوشاومده — پورت کردن تستهای زندهی موجود SQL Server/SQLite به یکی از این دیتابیسها با ارزشترین کمکیه که الان میشه کرد.dotnet add package SequelizeDotNet.Core
dotnet add package SequelizeDotNet.Providers.SqlServer
var engine = new QueryEngine(new SqlServerDialect(), new ModelRegistry());
var query = QueryEngine.Query("dbo", "Posts")
.Where("AuthorId", QueryOperator.Equal, 42)
.Include("FK_Posts_Authors");
var rows = await engine.ToListAsync(query, connection);