السلام عليكم ورحمة الله وبركاته
بعد التحية
في الأغلب كل من قام بعمل تطبيقات ويب بتكنولوجيا ASP.NET قام بإستخدام ConnectionString الموجودة داخل الملف web.config، حيث أنها تسهل عليك تحديد بيانات الخادم الذي يحوي قاعدة البيانات التي يتم إستخدامها للتطبيق المطروح وتغيرها في أي وقت عوضا عن أن تكون مكتوبة في داخل الكود ( Hard coded ) والذي يلزم recompile في كل مرة أردت أن تغيرها. تخيل أنك قمت ببرمجة CustomControl حيث أنه يقوم بعملية معينة ما وبالإعتماد على Classes تم تعريفها تساعده على أداء مهمته، أو أنك قمت ببرمجة مكتبة تقوم بالقيام ببعض الإحصائيات أو كانت تستخدم الـ ASP.NET Membership ولكن بشكل آخر غير الشكل المطروح من قبل Microsoft. بالتأكيد هناك بيانات يجب إدخالها من قبل من سيقوم بإستخدام ما قمت بتحقيقة، مثلا لو كانت برمجتك تعتمد على قاعدة بيانات فيجب عليك الحصول على الـ ConnectionString التي تخص من سيقوم بالإستفادة من برنامجك أو مكتبتك. يمكنك فعل ذلك عن طريق الطلب من كل من سيقوم بإستخدام مكتبتك بتعريف مخدلات في AppSettings الموجودة في الـ web.config لمن يريد الإستفادة من مكتبتك، وهناك أيضا طريقة أخرى وهي عن طريق تعريف section خاص بك في ملف الـ web.config، دعونا ناخذ المثال التالي :
أفرض أن لدينا الـ Class التالي :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Configuration;
/// <summary>
/// Summary description for MyClass
/// </summary>
namespace MyConfiguration
{
public class MyClass : ConfigurationSection
{
public MyClass()
{
//
// TODO: Add constructor logic here
//
}
public string Model
{
get {}
set {}
}
public int Size
{
get {}
set {}
}
}
}ونريد أن نقراء الخواص (Properties) المعرفة بداخله وهي Model و Size عن طريق الملف web.config بدون إستخدام الـ AppSettings، ينبغي علينا أولا تعريف section خاص بنا ويتناسب مع الـ Class الموجود لدينا. المقصود بـ section هو جزء يتم من خلالة تخزين قيم تساعدنا على آداء المهمة الموكلة لنا مثلها مثل الـ AppSettings و ConnectionString و authentication و compilation ... إلخ الموجودة في الملف web.config. نقوم بذلك داخل العنصر configurations عن طريق إستخدام العنصر section حيث أنه يأخذ name attribute وهو يدل على اسم الجزء و type attribute وهو يدل على نوع الـ class واسم الـ name space أو ملف الـ dll الذي يحويه، كما هو موضح في الكود التالي :
<section name="section name" type="NameSpace.ClassName [, DLL file name ]" />
في حالتنا هنا يجب قبل كتابة أي شيء في ملف الـ web.config أن نقوم بالوراثة ما بين MyClass و System.Configuration.ConfigurationSection حتى يستطيع الـ MyClass من أخذ بعض الخصائص التي تخوله لبناء section خاص به في ملف web.config ومن أهمها الـ Indexer الذي يتم إرسال اسم الـ attribute المقابل في ملف web.config حتى يتم إستخلاص قيمته، سيكون MyClass بالشكل التالي :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Configuration;
/// <summary>
/// Summary description for MyClass
/// </summary>
namespace MyConfiguration
{
public class MyClass : ConfigurationSection
{
public MyClass()
{
//
// TODO: Add constructor logic here
//
}
[ConfigurationProperty("model")]
public string Model
{
get { return this["model"].ToString(); }
set { this["model"] = value; }
}
[ConfigurationProperty("size")]
[IntegerValidator(MinValue = 1, MaxValue = int.MaxValue)]
public int Size
{
get { return int.Parse(this["size"].ToString()); }
set { this["size"] = value; }
}
}
}لاحظوا أنه فوق كل property هناك التعبير التالي
[ConfigurationProperty("model")]حيث هذا هو المسؤل عن عملية الربط ما بين الخاصية الموجودة في الـ Class والـ Attribute الموجود في الـ section الذي سيتم تعريفة في ملف الـ web.config، وأيضا تلاحظون هنا التعبير التالي في الخاصية Size
[IntegerValidator(MinValue = 1, MaxValue = int.MaxValue)]
وهذا التعبير كما هو واضح من اسمه عبارة عن Validator مثلة مثل CompareValidator ولكنه سيكون فقط للخاصية التي تليه حيث تم تحديد أعلى قيمة وأقل قيمة ممكنه، بمعنى آخر عندما تقوم بإستخدام الـ section المربوط بـ MyClass يجب عليك مراعاة قيمة الـ Size Attribute حسب ما هو محدد في الـ IntegerValidator، وهناك أيضا FloatValidator و StringValidator وما إلى ذلك. المهم سيكون تعريف الـ section في ملف الـ web.config بالشكل التالي :
<configuration>
<configSections>
<!-- Begin of my custom section -->
<section name="myclass" type="MyConfiguration.MyClass" />
<!-- End of my custom section -->
<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
<sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="Everywhere" />
<section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication" />
<section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication" />
<section name="roleService" type="System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication" />
</sectionGroup>
</sectionGroup>
</sectionGroup>
</configSections>بعد أن قمنا بتعريف الـ section الخاص بنا سنقوم الآن بإستخدامة كما هو موضح في الكود التالي مباشرة بعد العنصر configSections :
<!-- Begin using my custom sections --> <myclass model="whatever" size="2" /> <!-- End using my custom section -->
الآن، بعد أن تقوم ببناء (build) مشروعك قم بتشغيلة وذلك لنرى هل سيقوم الـ compiler بالإعتراض على ما هو موجود في ملف الـ web.config ؟
ستواجهك رسالة خطاء مفادها أن الـ attribute size الموجود في الـ section myclass يجب أن يكون محصورا ما بين 1 و int.MaxValue ، على الرغم من أن قيمته هي 2، لتفسير ما حدث يجب علينا أولا معرفة أن الـ compiler يقوم بإعطاء قيمة إفتراضية للـ attributes العددية، وفي حالة عدم توفر قيمة إفتراضية سيقوم بإعطاء العدد 0 لهذا الـ attribute العددي، وفي حالتنا هنا الـ size attribute هو عبارة عن عدد صحيح وهو بدون قيمة إفتراضية فبالتالي سيكون في البداية يحمل القيمة صفر والتي بدورها ستقوم بمخالفة الـ IntegerValidator المعرف في MyClass، ولحل هذه المشكلة سنقوم بإعطاء قيمة إبتدائية تتناسب مع الـ IntegerValidator عن طريق إستخدام الصورة الثانية للتعبير ConfigurationProperty الذي يأخذ اسم الـ attribute وقيمته الإفتراضية، سيكون MyClass على النحو التالي :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Configuration;
/// <summary>
/// Summary description for MyClass
/// </summary>
namespace MyConfiguration
{
public class MyClass : ConfigurationSection
{
public MyClass()
{
//
// TODO: Add constructor logic here
//
}
[ConfigurationProperty("model")]
public string Model
{
get { return this["model"].ToString(); }
set { this["model"] = value; }
}
[ConfigurationProperty("size", DefaultValue = 1)]
[IntegerValidator(MinValue = 1, MaxValue = int.MaxValue)]
public int Size
{
get { return int.Parse(this["size"].ToString()); }
set { this["size"] = value; }
}
}
}الآن كل ما علينا فعله هو إختبار ما قمنا به لنرى هل فعلا سيتم أخذ القيم من الـ section الذي قمنا بتعريفة أم لا، سنضيف حدث Page_Load وفي داخلة سنقوم بتعريف instance من MyClass ثم سنقوم بأخذ الـ myclass section وبعد ذلك سنقوم بكتابة كلا من Model و Size ونرا ماذا سيحدث، الكود كالتالي :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Configuration;
using MyConfiguration;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
MyClass config = (MyClass)ConfigurationManager.GetSection("myclass");
if (config != null)
Response.Write(string.Format("Model: {0}<br />Size: {1}", config.Model, config.Size));
}
}
}الآن سنقوم بإضافة مكتبة حيث سيتم تعريف قسم خاص بها، المكتبة سيكون الهدف منها التجريب فقط وقد يرى البعض أن المثال غير مجدي بالنسببة للتطبيقات الواقعية ولكن نقول أن الأمثال دائما غير فعالة، سيكون الغرض من المكتبة هو هو بناء Class بإسم ImageRep حيث أ، هذا الكلاس سيحوي خصائص الصورة وهي البعد السيني X والبعد الصادي Y وطول وعرض الصورة و رابط الصورة وأخير سيكون هناك Method بإسم GetImageHtml والتي ستقوم بإرجاع كود الـ HTML لرسم الصورة حسب خصائصها، سيكون كود المكتبة كالتالي :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
namespace ImageEntity
{
public class ImageRep : ConfigurationSection
{
[ConfigurationProperty("x", DefaultValue = 0)]
[IntegerValidator(MinValue = 0, MaxValue = 800)]
public int X
{
get { return int.Parse(this["x"].ToString()); }
set { this["x"] = value; }
}
[ConfigurationProperty("y", DefaultValue = 0)]
[IntegerValidator(MinValue = 0, MaxValue = 800)]
public int Y
{
get { return int.Parse(this["y"].ToString()); }
set { this["y"] = value; }
}
[ConfigurationProperty("width", DefaultValue = 1)]
[IntegerValidator(MinValue = 1, MaxValue = 200)]
public int Width
{
get { return int.Parse(this["width"].ToString()); }
set { this["width"] = value; }
}
[ConfigurationProperty("height", DefaultValue = 1)]
[IntegerValidator(MinValue = 1, MaxValue = 200)]
public int Height
{
get { return int.Parse(this["height"].ToString()); }
set { this["height"] = value; }
}
[ConfigurationProperty("imageUrl", IsRequired = true)]
public string ImageUrl
{
get { return this["imageUrl"].ToString(); }
set { this["imageUrl"] = value; }
}
public string GetImageTag()
{
System.Web.UI.WebControls.Image image = new System.Web.UI.WebControls.Image();
image.ImageUrl = ImageUrl;
image.Height = Height;
image.Attributes.Add("style",
string.Format("position: absolute; left: {0}px; top: {1}px;", X, Y));
System.IO.StringWriter tag = new System.IO.StringWriter();
using (System.Web.UI.HtmlTextWriter tw = new System.Web.UI.HtmlTextWriter(tag))
{
image.RenderControl(tw);
}
return tag.ToString();
}
}
}سيكون تعريف الـ section كالتالي (طبعا يجب إضاة reference لمشروعك لهذه المكتبة) :
<configSections>
<!-- Begin of my custom section -->
<section name="image" type="ImageEntity.ImageRep, ImageEntity" />
<!-- End of my custom section -->
<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
<sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="Everywhere" />
<section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication" />
<section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication" />
<section name="roleService" type="System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication" />
</sectionGroup>
</sectionGroup>
</sectionGroup>
</configSections>سيكون إستخدام القسم الذي تم إضافتة على النحو التالي :
<!-- Begin using my custom sections --> <image x="300" y="300" imageUrl="~/resource/Winter.jpg" width="200" height="200" /> <!-- End using my custom section -->
سيكون الكود في الحدث Page_Load كالتالي :
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
ImageEntity.ImageRep imageRep = (ImageEntity.ImageRep)ConfigurationManager.GetSection("image");
Response.Write(imageRep.GetImageTag());
}
}ستجدون في المرفقات كل من ملف المصدر للمكتبة و المشروع الذي بداخلة سيتم تطبيق ما تم شرحه هنا
أتمنى للجميع التوفيق والنجاح