یک ORM دیتابیس-محور و مبتنی بر DDL صریح برای داتنت — Typeهای emitشده در runtime، eager loading بدون JOIN، اعلانهای push مبتنی بر CDC، و تولید GraphQL/OpenAPI مستقیماً از schema.
اگه با Sequelize.js در دنیای Node.js کار کرده باشید، این حس رو میشناسید: اشاره میکنید به یک دیتابیس، یک مدل تعریف (یا از روی دیتابیس موجود استخراج) میکنید، و در کمتر از چند دقیقه با Model.findAll({ where: {...}, include: [...] }) دارید کوئری میزنید. Associationها، hookهای lifecycle، read replica — همه هستن، و همهچیز به اندازهی کافی به SQL نزدیک میمونه که هیچوقت حس نکنید دارید با یک انتزاع مبارزه میکنید.
داستان اصلی ORM در داتنت فرق میکنه. Entity Framework Core کتابخانهی فوقالعادهایه — ولی اساساً code-first کار میکنه: شما کلاسهای DbContext/entity رو تعریف میکنید، و migrationها توضیح میدن دیتابیس چطور باید تغییر کنه تا با کد شما هماهنگ بشه. این مدل عالیه وقتی اپلیکیشن شما مالک schema است. ولی وقتی دیتابیس از قبل وجود داره، متعلق به یک تیم دیگه (یا حتی یک codebase دیگه، شاید به یک زبان کاملاً متفاوت) است، یا وقتی اصلاً نمیخواید کد اپلیکیشن تصمیم بگیره schema چه شکلی باشه، این مدل خیلی خوب جواب نمیده.
دقیقاً همین خلأ دلیل ساختن SequelizeDotNet بود.
این کتابخانه واقعاً چیکار میکنه
SequelizeDotNet یک ORM با فلسفهی database-first برای داتنته، روی شش دیتابیس رابطهای — SQL Server، SQLite، PostgreSQL، MySQL/MariaDB، Oracle، و DB2 — که روی سه تصمیم طراحی غیرقابلمذاکره ساخته شده:
دیتابیس همیشه منبع حقیقته. هیچچیزی از روی یک schema که خودتون نوشتید حدس نمیزنه کد شما «باید» چه شکلی باشه. مدل از دیتابیس میاد، نه برعکس.
DDL همیشه صریحه، هیچوقت diff خودکار نیست. تغییرات ساختاری فقط از طریق متدهایی با نام واضح انجام میشن — RenameColumnAsync، AddColumnAsync، DropTableAsync — که دولوپر خودش عمداً صداشون میزنه. عملیاتهای مخرب نیاز به force: true دارن و در production قفلن مگر اینکه صریحاً override بشن. هیچ چیزی شبیه sync({ alter: true }) سکوئلایز («دیتابیس رو هرجور لازمه با مدلم هماهنگ کن») وجود نداره — چون دقیقاً همین دسته از فیچرهاست که sync خودکار رو در production خطرناک میکنه، و حتی مستندات خود سکوئلایز هم به همین دلیل ازش منع میکنه.
Typeهای Runtime، نه Code Generation. اینجا داتنت کاری میکنه که جاوااسکریپت نمیتونه: EntityTypeFactory با استفاده از System.Reflection.Emit یک CLR type واقعی بهازای هر جدول میسازه، که بهصورت per-table کش میشه با تضمین lazy و thread-safe (دقیقاً یکبار emit میشه، حتی زیر بار concurrent). یک ستون رو در دیتابیس تغییر بدید، همون کوئری بعدی روی اون جدول یک type تازه emitشده که این تغییر رو منعکس میکنه تحویل میده — بدون rebuild، بدون restart، بدون فایل .cs تولیدشدهای که باید sync نگهش دارید.
چند فیچر که خود سکوئلایز نداره
چون سیستم runtime و type داتنت اجازهی ساختنشون رو میده:
Include تودرتو و many-to-many، همیشه بدون JOIN. Include("Author.Publisher.Country") سه لایهی foreign key رو بهصورت سه کوئری جداگانه، کششده، و فیلترشده با IN (...) طی میکنه. IncludeMany("Tags", "dbo", "PostTags", "FK_PostTags_Posts", "FK_PostTags_Tags") همین کار رو از طریق یک جدول واسط صریحاً نامگذاریشده انجام میده. هیچوقت چیزی پشت یک query plan مبهم و خودکار مخفی نمیشه.
مسیریابی Read/Write که واقعاً SQL خام رو هم میفهمه. پشتیبانی read replication خود سکوئلایز سابقهی گزارش باگهای تکرارشونده داره که کوئری خام بیسروصدا از replica pool رد میشه. StatementIntentClassifier کلمهی کلیدی ابتدایی کوئری خام رو بررسی میکنه دقیقاً برای اینکه همون مسیر میانبر هم درست مسیریابی بشه.
Hot-reload بدون restart برای schema. SchemaWatcher پول میکنه، مقایسه میکنه، و type runtime جدول تغییریافته رو بهصورت atomic و نسخهدار جایگزین میکنه — بدون ریاستارت کردن پروسه.
Change Data Capture سکوئل سرور بهعنوان یک event قابلsubscribe. اکثر ORMها هیچ پشتیبانی built-in برای CDC ندارن. CdcChangeWatcher جدولهای change سکوئل سرور رو به یک event سیشارپ تبدیل میکنه که یکبار subscribe میکنید و تمام.
تولید GraphQL و OpenAPI مستقیماً از schema اینتروسپکتشده. بدون هیچ مرحلهی مدلسازی جدا — یکبار schema رو بخونید، و یک schema سبک GraphQL یا سند OpenAPI 3.0 مستقیماً از چیزی که دیتابیس از قبل میدونه بسازید.
الان دقیقاً کجا ایستاده — صادقانه
این یک پروژهی جوونه (نسخهی ۰.۱.۰) و میخوام دقیقاً بگم تا کجا واقعاً تست شده، نه فقط چی پیادهسازی شده:
SQL Server و SQLite بهطور کامل و زنده تست شدن — بیش از ۱۴۰ تست بین این دو، که هر نوع داده، هر عملگر کوئری، تراکنشها، hookها، schema drift، hot-reload، و bulk insert رو پوشش میده، روی LocalDB واقعی و دیتابیسهای موقت اجرا شده.
PostgreSQL، MySQL، Oracle، و DB2 کاملاً پیادهسازی شدن، پشت همون قرارداد IDialect، ولی فقط در سطح unit-test (query translator) تست شدن — من هنوز نمونهی زندهای از اینها ندارم که رفتار end-to-end رو روش تأیید کنم. اگه شما دارید، مشارکتتون فوقالعاده خوشاومده — پورت کردن تستهای زندهی موجود SQL Server/SQLite به یکی از این دیتابیسها با ارزشترین کمکیه که الان میشه کرد.
مجموعاً ۲۲۳ تست خودکار، صفر شکست، در این نسخه.
اگه codebase شما از قبل مالک schema خودشه و LINQ چکشده در compile-time میخواید، احتمالاً EF Core همچنان انتخاب پیشفرض بهتریه — بالغتره، توسط مایکروسافت پشتیبانی میشه، و اکوسیستم بهمراتب بزرگتری داره. SequelizeDotNet برای همون سناریوی خاصیه که دیتابیس منبع حقیقته و ترجیح میدید همونطوری کار کنید که سکوئلایز به یک نسل از دولوپرها یاد داد.
امتحانش کنید
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);
مستندات کامل: Developer Guide · سورس و issueها: github.com/mirshahreza/SequelizeDotNet · لایسنس: MIT.
تشکر فراوان از همهی کسانی که تا امروز به Sequelize.js کمک کردن — این پروژه به این دلیل وجود داره که اون پروژه بیش از یک دهه ثابت کرد این سبک کار با دیتابیس، در هر زبانی، ارزش داشتنش رو داره.
اگه تا حالا از یک ابزار migration که اشتباه حدس زده ضربه خوردید، یا دارید یک سرویس داتنتی رو نگهداری میکنید که دیتابیسش مال یک تیم دیگهست، خوشحال میشم بازخوردتون رو بشنوم — و اگه یک نمونهی PostgreSQL، MySQL، Oracle یا DB2 دارید که بشه یک test suite رو روش امتحان کرد، حتی بیشتر خوشحال میشم.
SequelizeDotNet · لایسنس MIT · github.com/mirshahreza/SequelizeDotNet
Top comments (0)