أهم أقسام المدونة

الصفحات

VB.NET ASP.NET MVC Core


 
في هذا المنشور تكلمت عن استخدام تعبيرات XML في فيجوال بيزيك لإنتاج عروض الويب Views.. وقد وعدتكم أن أشرح لكم كيفية استخدام هذه الفكرة في إنشاء محرك عرض لاستخدامه في مشاريع ASP.NET MVC Core.. ولكني تأخرت بسبب محاولتي لتطوير هذا المحرك ليدعم كل إمكانيات محرك Razor.. لهذا أعتذر عن تأجيل هذا الأمر قليلا، وسأقدم فكرة مؤقتة لمبرمجي فيجوال بيزيك، لإنشاء مشاريع الويب بفيجوال بيزيك دوت نت لكنها ستستخدم صفحات cshtml! .. نعم هذا ممكن، ولكنه ليس له قالب جاهز في فيجوال ستديو، لهذا يجب إنشاء المشروع يدويا.. وأسهل طريقة لهذا هي نسخ مشروع سي شارب وإبدال ملفات الكود المكتوبة بسي شارب إلى ملفات كود مكتوبة بفيجوال بيزيك، وسيعمل كل شيء على ما يرام!

لمن يستخدم الإصدار 2.2 من نواة دوت نت .NET Core 2.2 أو أقل، يمكنه تنزيل هذا المشروع من موقع Github والبناء عليه.

وقد قمت بإنشاء مشروع آخر يستخدم النسخة التجريبية من الإصدار الثالث من نواة دوت نت، لأن هناك اختلافات كثيرة في هذا الإصدار تجعل المشروع القديم لا يعمل عليه.. يمكنكم تنزيل هذا المشروع  من هنا.

ملحوظة: إذا غيرت اسم المشروع (في أي من الإصدارين)، فعليك أن تستخدم مربع البحث والاستبدال للبحث عن الاسم القديم وتغييره إلى الاسم الجديد في كل ملفات المشروع.

وبإذن الله سأحاول أن أنشئ قالبا لمشاريع فيجوال بيزيك التي تستخدم نواة دوت نت لبرمجة الويب.

ولكن: هل من المنطقي تصميم العروض بلغة سي شارب وكتاب الكود بلغة فيجوال بيزيك؟

 لو شئت رأيي، فهذه ليست مشكلة، لأنك مضطر لكتابة كود جافا سكربت وهو شبيه بكود سي شارب، كما أن معظم الكود الذي يوضع في الصفحات كود مختصر وبسيط، ولو شئت أن تكتب كودا طويلا أو معقدا، فاكتبه في فيجوال بيزيك كخاصية ومرر ناتجها إلى الصفحة من خلال حقيبة العرض ViewBag.. ولو عممت هذا الطريقة، فقد لا تكتب كود سي شارب على الإطلاق، وتتحول صفحة العرض إلى كود HTML يحتوي على بعض الخصائص للتعويض عن قيمها فحسب!

وكما قلت، أنا أعمل على تطوير محرك Vazor (اختصار لـ VB Razor) حتى يمكن تلافي هذه المشكلة.. غير هذا، نحن ننتظر Blazor الذي سيخلصنا من كود جافا سكربت.. ما زال هذا المشروع تجريبيا حتى الآن وسيأخذ وقتا وسيصدر بلغة سي شارب، ولكن نأمل بعدها أن توجد نسخة بفيجوال بيزيك، وإلا فلنوجدها نحن :)

لكن حتى ذلك الحين، لديك طريقة لكتابة مشاريع الويب بفيجوال بيزيك دوت نت.. فاستمتع :)

VB.NET Razor

مفاجأة مدهشة: أنشأت تطبيق ASP.NET MVC Core بلغة فيجوال بيزيك وعمل بشكل صحيح!
 

تعرفون بالطبع أنني سقطت في عشق لغة فيجوال بيزيك من أول نظرة عام 1998 وما زلت مغرما بها حتى اليوم، وأرى أنها أرقى ما وصلت إليه لغات البرمجة من حيث صياغتها القريبة من اللغة الإنجليزية العادية، والتسهيلات الذكية الكثيرة التي تمنحها للمبرمج فتجعله يكتب الكود ويحصل على النتائج الصحيحة بسرعة.. كل هذا جعلني مستاءا للغاية من إهمال ميكروسوفت لفيجوال بيزيك وتركها حبيس الويندوز وعدم استخدامها في Xamarin و ASP.NET Core.. وكلما سألهم سائل عن ذلك، تكون الإجابة إنك تستطيع استخدام فيجوال بيزيك بالفعل في هذه المنتجات، لكن في مكتبات الكود وليس تصميم الواجهات، وإن دعم ملفات vbhtml في تقنية Razor أمر يحتاج وقتا وجهدا وتكلفة عالية، وهم مشغولون في أشياء كثيرة أولى!
وقد ظللت أنتظر عامين لعل هذه الساسة تتغير في أي لحظة، لكن بلا جدوى!.. لهذا بدأت أميل إلى الاقتناع إلى أن ميكروسوفت ستهمل فيجوال بيزيك بالتدريج وتسقطها من حساباتها إلى أن تموت موتا بطيئا مؤلما!
وقد قررت ألا أجعل هذا الأمر سهلا بالنسبة لهم، لهذا عرضت عليهم الأسبوع الماضي أن أتطوع لإنشاء VB.NET Razor حتى لو استغرق هذا ستة أشهر، وأن يعوني بعض التوجيهات لإنجاز المهام المطلوبة، لكن ردهم جاء كما توقعت برفض الفكرة، بحجة أن دعم وتطوير VB Razor بعد أن أكتبه لهم مكلف في حد ذاته وليس لديهم استعداد لهذا!!
في هذه اللحظة قررت أن أمضي قدما بمفردي، ولأن الموضوع معقد فعلا، قررت تبسيطه بأن أفصل كود فيجوال بيزيك في ملف خلفي code-behind file مثلما يحدث في WPF، وأن يقتصر ملف vbhtml على كود HTML مع وجود إشارات موضعية لخصائص معرفة في ملف الكود الخلفي يتم التعويض عنها بقيمها مباشرة.
وبتأمل هذه الفكرة، تذكرت فجأة أن فيجوال بيزيك تدعم كتابة كود XML مباشرة في الكود مثل:
Dim X = <p>sample</p>
مع توفير التنسيق والتلوين وتدقيق الصياغة لهذه التعبيرات!.. والأجمل من هذا أنني أستطيع التعويض بقيم المتغيرات داخل هذه الصيغ، بوضعها داهل القوسين المستخدمين في ASP على الصيغة <%=  %>.. مثل:

معامل معالجة العدم Null-Coalesce Operator

معامل معالجة العدم Null-Coalesce Operator:
تمتلك كل من فيجوال بيزيك وسي شارب معامل معالجة العدم Null-Coalesce Operator، وهو يستقبل متغيرين، حيث يعيد قيمة المتغير الأول لو كانت لا تساوي null (أو Nothing في فيجوال بيزيك)، وإلا فإنه يعيد قيمة المتغير الثاني.
وتمتاز هذه الصيغة في سي شارب بأنها مختصرة وتسمح بالاختيار من سلسلة طويلة من البدائل ببساطة:
var r = a ?? b ?? c ?? d ?? e;
العبارة (a ?? b ?? c ?? d ?? e) ستعيد قيمة أول متغير غير منعدم (قيمته لا تساوي null)، فإذا كانت كل المتغيرات منعدمة، فستعيد null.
للأسف فعل نفس الشيء في فيجوال بيزيك ليس بنفس البساطة، لأنها تستخدم if() لفحص قيمة متغيرين فقط، ولو أردت تنفيذ الكود السابق فستحتاج إلى استخدام عدة جمل If متداخلة كالتالي:
Dim r = If(a, If(b, If(c, If(d, e))))
قد لا يبدو لك الأمر هاما، لكن واقعيا، a, b , c, d, f تكون عبارات برمجية طويلة وليست مجرد أسماء مختصرة كهذه.. مثلا:
C#:
var t = CheckToolStrip(c as ToolStrip) ??
            CheckToolStrip(c.ContextMenuStrip) ??
            CheckControls(c);
 
VB.NET:
Dim t = If(CheckToolStrip(TryCast(c, ToolStrip)),
                  If(CheckToolStrip(c.ContextMenuStrip),
                       CheckControls(c)))
لو حاولت أن تضيف قيمة رابعة للكود السابق في سي شارب، فيكفي أن تضيف العلامة ؟؟ في السطر الأخير ثم تكتب العبارة الجديدة.. أما في فيجوال بيزيك، فعليك أن تضيف جملة If جديدة في البداية، وتجعل معاملها الأول هو العبارة الجديد، ومعاملها الثاني هو كل الكود السابق!
لحل هذه المشكلة، كتبت هذه الدالة:
Function Coalesce(Of T)(ParamArray expressions() As T) As T
        For Each expr In expressions
            If expr IsNot Nothing Then Return expr
        Next
End Function
وبهذا يمكن استخدامها كالتالي:
dim x = Coalesce(a, b, c, d, e)
هذا الكود أوضح وأسهل، لكن فيه مشكلة تجعله أقل كفاءة، وهي أن فيجوال بيزيك ستحسب قيم كل العبارات لإرسالها إلى معاملات الدالة Coalesce.. هذه العبارات قد تكون دوالا كما في هذا المثال:
Dim t = Coalesce(CheckToolStrip(TryCast(c, ToolStrip)),
              CheckToolStrip(c.ContextMenuStrip),
              CheckControls(c))
استخدام الدالة Coalesce في الكود السابق سيضمن استدعاء الدوال الثلاث حتى لو كانت الدالة الأولى فقط هي التي ستعيد قيمتها.. وفي المشروع الذي كتبت فيه هذا الكود، هذه الدوال دوال ارتدادية Recursive Functions، أي أنها تظل تستدعي نفسها إلى أن تصل إلى نهاية شجرة العناصر، وهذا معناه أن تنفيذ دالة أو دالتين بدون داعي سيستهلك وقتا ملموسا!
هذا لا يحدث عند استخدام If() لأنها تتجاهل تنفيذ أي عبارات لا لزوم لها (يسمى هذا Short-Circuiting  وهو تعبير مأخوذ من مصطلح جعل الدائرة الكهربية قصيرة، أو بالتعبير الشائع: عمل قفلة).
لحل هذه المشكلة، يمكن اللجوء إلى حيلة صغيرة، وهي ألا نرسل العبارات مباشرة، وإنما نضع كل عبارة في دالة بدون معاملات كل مهمتها أن تحسب قيمة العبارة وتعيدها.. ونطور الدالة Coalesce لنجعلها تستقبل دالة العبارات بدلا من العبارات نفسها كالتالي:
Function Coalesce(Of T)(ParamArray expressions( ) As Func(Of T)) As T
        For Each exp In expressions
            Try
                Dim v = exp( )
                If v IsNot Nothing Then Return v
            Catch
            End Try
        Next
        Return Nothing
End Function
لاحظ أنني طورت كود الدالة بحيث تعالج أي أخطاء تحدث عند استدعاء دالة حساب قيمة العبارة، وهذا معناه أن الدالة Coalesce ستتجاهل القيم المنعدمة أو التي حدث خطأ عند حسابها.
الآن، وباستخدام الدوال الفورية، صار من السهل استخدام هذه الدالة كالتالي:
Dim t = Coalesce(
              Function( ) CheckToolStrip(TryCast(c, ToolStrip)),
              Function( ) CheckToolStrip(c.ContextMenuStrip),
              Function( ) CheckControls(c))
هذا الكود سيرسل عناوين الدوال إلى الدالة Coalesce، وهي التي ستقوم باستدعاء كل دالة واحدة تلو أخرى، إلى أن تجد قيمة غير منعدمة فتعيدها وتتوقف عن استدعاء باقي الدوال.. وهذا هو الأداء المطلوب.
قد يبدو هذا الكود أطول قليلا من استخدام if ولكنه أكثر وضوحا بسبب عدم تداخل جمل if وأقواسها الكثيرة.
وقد اقترحت أن تضيف فيجوال بيزيك الجملة Coalesce لتعمل بآلية short-circuiting مباشرة بدون أن نضطر نحن إلى استخدام الدوال الفورية، وأتمنى أن يستجيبوا لهذا المقترح.
 

نطاق المتغيرات الموضعية

نطاق المتغيرات الموضعية
 
تعرفون أن المتغيرات المحلية Local Variables في فيجوال بيزيك وسي شارب تكون مرئية في النطاق الذي توجد فيه.. فيمكنك مثلا تعريف متغير داخل جملة شرط، ولن تستطيع استخدامه خارجها:
C#
var x = 2;
if (x > 1)
{
    var msg = "x > 2";
    Console.WriteLine(msg);
}
else
{
    var msg = "x <= 2";
    Console.WriteLine(msg);
}
تلاحظ أنك استطعت تعريف متغيرين كل منهما اسمه msg لأن كلا منهما مرئي فقط في المقطع الصغير الذي تم تعريفه فيه.
لكنك لو حاولت تعريف متغير اسمه msg بعد نهاية الجملة if..else فستحصل على خطأ لأن المتغير الثالث مرئي داخل جملة if..else لأنه معرف في المقطع الخارجي الذي يحتوي على if..else.
VB:
Dim x = 2
If x > 1 Then
    Dim msg = "x > 2" ' خطأ
    Console.WriteLine(msg)
else
    Dim msg = "x <= 2" ' خطأ
    Console.WriteLine(msg)
End If
Dim msg = "done"
Console.WriteLine(msg)
 
 قد يجعلك هذا تتعجب، لأن المتغير الثالث سيتم تعريفه بعد انتهاء جملة الشرط، وبالتالي لن يؤثر عليها.. لكن ليس هكذا تعمل لغة البرمجة.. على سبيل المثال: لو وضعت كل هذا الكود في حلقة دوران مثل Do Loop فهذا معناه أن التنفيذ سيعود لأعلى مرة أخرى وسيختبر جملة الشرط مرة أخرى، وسيحدث ارتباك بين قيمتي المتغيرين اللذين لهما نفس لاسم!
يمكنك حل هذه المشكلة في سي شارب بتعريف نطاق خاص بالمتغير الثالث، بوضعه في القوسين {}:
C#
var x = 2;
if (x > 1)
{
    var msg = "x > 2";
    Console.WriteLine(msg);
}
else
{
    var msg = "x <= 2";
    Console.WriteLine(msg);
}
 
{
    var msg = "done";
    Console.WriteLine(msg);
}
 
لا يوجد شيء مثل هذا في فيجوال بيزيك، لكن يمكن الحصول عليه بحيلة صغيرة، مثل وضع المتغير الثالث في جملة شرط صحيحة دائما:
Dim x = 2
If x > 1 Then
    Dim msg = "x > 2" ' خطأ
    Console.WriteLine(msg)
else
    Dim msg = "x <= 2" ' خطأ
    Console.WriteLine(msg)
End If
 
If True Then
   Dim msg = "done"
   Console.WriteLine(msg)
End If
أو وضعه في المقطع With بالحيلة التالية:
Witn Nothing
   Dim msg = "done"
   Console.WriteLine(msg)
End With