العرض 02
منطق الأعمال
BusinessLogicLayer
الكيانات والإعدادات والأدوات المشتركة — كل ذلك دون أن تعلم بوجود متصفّح أصلًا.
ما دور هذه الطبقة؟
- هنا تعيش الكيانات (Car وStudent…) وقواعد العمل الخاصة بها.
- ترث من
Recordفي طبقة البيانات — ولذلك يعرف كل كيان أصلًا كيف يُحفَظ ويُحمَّل. - يُمنع عليها أن تعرف شيئًا عن الواجهة أو HTTP: لا HttpContext، ولا HTML، ولا Session.
- بفضل ذلك تخدم الطبقة نفسها موقعًا وتطبيق MAUI وJob ليليًا واختبارات.
كيان الأعمال = فئة صغيرة
public class Student : Record
{
[Column(Size = 100)] public string FirstName { get; set; }
[Column(Size = 100)] public string LastName { get; set; }
[Column(Size = 20)] public string IdNumber { get; set; }
[Column] public DateTime BirthDate { get; set; }
[Column] public int Grade { get; set; }
[Column] public double Average { get; set; }
[Column] public bool IsActive { get; set; }
[Column(Size = 255)] public string Picture { get; set; }
}
- الحقول = أعمدة. لا شيء أكثر من ذلك.
- منطق الأعمال (حساب المعدّل، التحقّق من رقم الهوية) يُضاف كدوال على الفئة نفسها.
- علاقة بكيان آخر؟ خاصية من نوع ذلك الكيان — تُحفظ تلقائيًا كقيمة
BIGINTتحمل معرّفه.
Settings — إعدادات تعيش في قاعدة البيانات
// القراءة (مخزَّنة في الذاكرة، تُحمَّل مرة واحدة عند إقلاع التطبيق):
string url = Settings.Val["MeshulamUrl"];
// الكتابة:
await Settings.SetAsync("MeshulamUrl", "https://...");
// زرع القيم الافتراضية — كل مشروع يتجاوز CreateAsync:
public override async Task CreateAsync()
{
await base.CreateAsync();
await AddAsync("MeshulamUrl", "عنوان بوابة الدفع", "https://sandbox...");
}
- لماذا قاعدة البيانات لا appsettings.json؟ لأنها تتيح التغيير في زمن التشغيل، ولكل عميل على حدة، دون نشر جديد.
- الزرع الأولي مُتماثل النتيجة — يعمل مع كل إقلاع ولا يضيف سوى المفاتيح الناقصة.
Util — صندوق الأدوات المشترك
- التواريخ:
DateStringوToDateوالأشهر العبرية والميلادية وDecode("תשע\"ו")=776. - التحقّق:
IsValidTz(رقم تدقيق) وIsPhoneوIsValidEmail. - النصوص:
StripHTMLوCleanHiddenCharsوUrlEscape.
- الملفات:
Mime()والتحقّق من الامتدادات وCompress/Decompress(GZip). - الأمان:
ProtectAppState— تشفير حالة التطبيق المرسَلة إلى العميل. - الشبكة:
SendHttpRequestAsyncباستخدام HttpClient مشترك.
reuse-first: قبل كتابة دالة مساعدة جديدة — تأكّد من أنها ليست موجودة هنا أصلًا.
تصدير المستندات — بدعم كامل للعبرية
- فئة أساس واحدة:
Exporter— تُعيدbyte[]؛ ووحدة التحكّم تغلّفها بـFile(...). - Excel (NPOI) — ورقة عمل RTL حقيقية؛ Word (OpenXML) — جداول BiDi.
- PDF (MigraDoc) — خط مضمَّن داخل ملف الـDLL و
FlipHebrewالتي ترتّب العبرية بصريًا.
Exporter exporter = new ExcelExporter("Students.xlsx", rtl: true);
exporter.AddTable(dataTable);
return File(exporter.GetBytes(), exporter.ContentType, exporter.FileName);
القواعد الذهبية للطبقة
- غير متزامن حتى النهاية — كل دالة تمسّ قاعدة البيانات تُعيد
Task. - بلا ارتباط بالواجهة — وما يحتاج إلى بيانات البيئة يتلقّاها كمعامل.
- كيانات نحيفة؛ والمنطق المشترك يُرفع إلى
Utilأو إلى فئة أساس. -
Settingsإلى قاعدة البيانات، لا إلى ملفات الإعدادات. - وما يصحّ لجميع المشاريع — يُصلَح هنا أولًا، في القالب.