العرض 01
طبقة البيانات
DataAccessLayer
كيف يتحوّل كيان واحد إلى جدول في قاعدة البيانات، بلا SQL يدوي وبلا عمليات ترحيل.
ما دور هذه الطبقة؟
- كل وصول إلى قاعدة البيانات يمرّ من هنا — ولا تكتب أي طبقة أخرى SQL.
- ORM صغير ومركَّز من صنعنا:
Record+ سمات، بلا Entity Framework. - تدعم ثلاثة مزوّدين بالكود نفسه تمامًا: SQL Server وMySQL وSQLite.
- غير متزامنة من طرف إلى طرف — كل نداء لقاعدة البيانات هو
await، ولا يُحجب أي Thread.
Record — قلب الطبقة
- لكل كيان
ID(مفتاح أساسي) وDescription. - ضَع السمة
[Column]على خاصية — فتصبح عمودًا في قاعدة البيانات. - اسم الجدول = اسم الفئة؛ والمفتاح =
الاسم+Id. - لا حاجة إلى ALTER يدوي: تُنشئ
SetupAsyncعند إقلاع التطبيق الجداول الناقصة، وتضيف الأعمدة أو توسّعها وفق الكود.
public class Car : Record
{
[Column(Size = 300)]
public override string Description { get; set; }
[Column] public bool IsAutomatic { get; set; }
[Column] public double Volume { get; set; }
[Column] public string Color { get; set; }
[Column] public DateTime ProductionDate { get; set; }
}
هذا هو كل الكود اللازم كي يُنشأ جدول Car في قاعدة البيانات ويُدار من الشاشة.
CRUD غير متزامن
Car c = new Car { Description = "مازدا 3", Volume = 2.0 };
await c.SaveAsync(); // INSERT (ID جديد) أو UPDATE — تلقائيًا
Car loaded = await Record.GetAsync<Car>(c.ID); // تحميل حسب المفتاح
List<Car> all = await Record.AllAsync<Car>(); // كل السجلات
int n = await Record.CountAsync<Car>(); // عدّ
c.Color = "أحمر";
await c.UpdatePropertiesAsync("Color"); // تحديث عمود واحد — وليس Save كاملًا!
await c.DeleteAsync();
- قاعدة البيت: التحديث النقطي =
UpdatePropertiesAsyncوليسSaveAsync— تفاديًا للآثار الجانبية التي يجرّها الحفظ الكامل. DateTime.MinValueيعني "لا يوجد تاريخ" — ويُخزَّن بالقيمة NULL.
استعلامات مُصفّاة — AndFilter
AndFilter f = new AndFilter();
f.Add("color", "أحمر"); // مساواة، مع معامل
f.Add("volume", 1.6, EqalityOperators.GreaterThan);
f.AddBetween("productionDate", from, to);
List<Car> cars = await Record.PartialAsync<Car>(f, "productionDate desc",
pageNumber: 0, pageSize: 20);
int total = await Record.CountAsync<Car>(f);
- يبني المُرشِّح جملة WHERE بمعاملات — بلا دمج نصوص وبلا خطر SQL Injection.
- ويكمّل
OrFilterشروط "أو"؛ أما ترقيم الصفحات فيتمّ عبرPagingTemplateالخاص بكل مزوّد.
كيف يعمل الكود نفسه على ثلاث قواعد بيانات؟
- يُختار المزوّد وفق سلسلة الاتصال:
Server=← MySQL، وData Source=*.db← SQLite، وفي ما عدا ذلك ← SQL Server. - يُكتب الكود بلغة T-SQL؛ وتتولّى
FixSqlلدى كل مزوّد الترجمة: GETDATE()وISNULLو[أقواس]وIDENTITY… - ويحصل SQLite أيضًا على الدوال
YEAR/MONTH/DAYالمعرَّفة في C#. - التطوير محليًا على SQLite، والإنتاج على SQL Server/MySQL — دون تغيير سطر واحد.
أدوات إضافية في الصندوق
- التشفير:
[Column(IsEncrypted = true)]— تُشفَّر القيمة بخوارزمية AES قبل الكتابة وتُفكّ عند القراءة. - التسلسل:
SerializeType = Jsonيحفظ كائنًا كاملًا في عمود نصي. - الفهارس:
CreateIndexAsync("color")— يُنشأ فقط إن لم يكن موجودًا، لدى كل مزوّد. - الاستيراد:
ImportAsyncيتعرّف على السجلات القائمة عبرIsUniqueIdويحدّثها بدل تكرارها. - المعاملات:
BeginTransactionAsync / CommitTransactionAsync.
باختصار: عرِّف الكيان، وحدِّد الأعمدة — وتتكفّل الطبقة بكل ما تبقّى.