MSF و Software Architecture


چند سال پیش، من یک presentation در مورد Microsoft Solutions Framework و Software Architecture یا همان معماری نرم‌افزار آماده کرده بودم و آن را به صورت نصفه و نیمه در محل کارم ارائه دادم، شاید این Slide Show برای کسانی که مایل هستند با این دو مقوله آشنایی پیدا کنند بد نباشد، از طریق آدرس زیر می‌توانید این Presentation را مشاهده و یا Download نمایید

مشکلات حروف فارسی چندگانه


  
برخی از حروف فارسی به چند شکل و به اصطلاح بر اسا Character Set های مختلف نوشته می‌شوند
مثلا:
"ک" و "ك"
"ی" و "ي"
که در اصل شکل‌های دوم عربی هستند
حالا اگر در پایگاه داده‌های شما هردو "ک" و هردو "ی" ذخیره شده باشند باید چه‌کار کرد؟
مثلا شما می‌خواهید یک Select بنویسید که تمام کسانی که نام آنها "بابک" است را برای شما بیاورد
دو نفر با نام بابک در جدول شما ذخیره شده‌اند یکی با "ک" و دیگری با "ك"
یک روش این است که دو شرط برای Select بگذارید که هردو بابک را چک کند، ولی اگر متنی که در حال جستجوی آن هستید طولانی باشد چه؟ اصلا اگر در متن شما هم "ک" وجود داشت و هم "ی" چه؟ یعنی باید ترکیب تمام حالات مختلف را در شرطتان اعمال کنید؟
در واقع اگر مجبور باشید باید این کار را بکنید ولی به‌جای نوشتن تمام متن در حالات مختلف می‌توانید توابعی بنویسید که این کار را برای شما انجام بدهند، این کدی‌است که من از آن استفاده می‌کردم:

  string MakeitFarsi(string NetString)
    {
        NetString = NetString.Replace((char)1705, (char)1603);
        return NetString.Replace((char)1740, (char)1610);

    }

این تابع کارکترهای عربی را با نظایر فارسی آنها معاوضه می‌کند

می‌توانید سه تابع دیگر هم برای جستجوی کارکترهای عربی و ترکیب آنها بنویسید، ولی پیشنهاد من این است که هنگام data entry یعنی وقتی که در حال ثبت اطلاعات در پایگاه داده‌ها هستید آنها را استاندارد سازی کنید، مثلا همه کارکترهای عربی را در آنها فارسی کنید و هنگام انجام جستجو فقط به‌دنبال کارکترهای فارسی بگردید، به‌این ترتیب یک تابع مثل تابع فوق کفایت می‌کند، البته با این شرط که همانطور که گفتم، یک بار هنگام ثبت اطلاعات از آن استفاده کنید و یک بار هم هنگام بازیابی.

Hashing و Encryption




Hashing و Encryption هردو روشهایی برای رمزنگاری هستند، نکته قابل توجه این است که هنگامی که متنی را Hash می‌کنید دیگر روش ساده‌ای تحت عنوان dehash کردن آن وجود ندارد و شما برای اینکه متنی را با آن مقایسه بکنید مجبورید آن متن را با همان شیوه Hash کنید و سپس با متن hash شده مقایسه نمایید، در صورتی که یک متن Encrypt شده را می‌توانید Decrypt کرده و با یک متن دیگر مقایسه کنید.

احتمالا می‌دانید که روشهای Encryption مختلفی وجود دارند، در اینجا من کدی را که سالها پیش با استفاده از الگوریتم SHA و با VB.NET نوشته بودم در اختیارتان می‌گذارم:

Imports System.Security.Cryptography
Imports System.Text
.
.
.
 Public Function GetEncryptedData(ByVal Data As String) As String

        Dim shaM As New SHA1Managed
        Convert.ToBase64String(shaM.ComputeHash(Encoding.ASCII.GetBytes(Data)))
        Dim eNC_data() As Byte = ASCIIEncoding.ASCII.GetBytes(Data)
        Dim eNC_str As String = Convert.ToBase64String(eNC_data)

        Return eNC_str

    End Function

    Public Function GetDecryptedData(ByVal Data As String) As String

        Dim dEC_data() As Byte = Convert.FromBase64String(Data)
        Dim dEC_Str As String = ASCIIEncoding.ASCII.GetString(dEC_data)
        Return dEC_Str

    End Function

همانطور که ملاحظه کردید دو تابع به منظور Encrypt مردن و Decrypt کردن در کد فوق نوشته شده‌است.

در مورد Hashing که معمولا برای نگهداری passeord ها استفاده می‌شود یکی از روشها استفاده از System.Web.Security است:

System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(txtPwd.Text, "MD5").ToString()

همانطور که ملاحظه کردید من از MD5 به عنوانت فرمت Hashing استفاده کردم

کار با Directory Services



اضافه کردن و یا حذف کردن یک User در یک Domain
اضافه کردن و یا حذف کردن یک Group در یک Domain
اضافه کردن و یا حذف کردن یک User در یک Group
اینها کارهای مدیریتی هستند که یک Network Administrator معمولا انجام می‌دهد، ولی اگر قرار باشد که این کارها از طریق نرم‌افزار شما انجام شود چه؟
در لینک زیر نمونه کدهایی که من سالها پیش به این منظور نوشته‌ام را پیدا می‌کنید، البته ممکن است که روش اجرای این کد کمی قدیمی شده باشد، این کد برای .NET Framework 1.1 و با VB.NET نوشته شده است.

Binding در Template Field


  
همانطور که می‌دانید ما به اشیای درون Template Field ها در کد، دسترسی مستقیم نداریم، شما به راحتی می‌توانید Bound Column به یک GridView اضافه کنید ولی اگر Template Field و یا به قول قدیمی‌تر ها Template Column داشته باشید و بخواهید اشیای موجود در آن را (مثلا متن یک Lable) را Bind کنید باید چه کار کرد؟

پاسخ، تابع Eval است، فرض ما این است که شما قبلا به خود Gridview یک Data source داده‌اید و حالا مشکل فقط نمایش Record های آن در اشیای مورد نظر شما است، در حقیقت Container شما دارای Datasource می‌باشد و کافیست که شما از طریق تابع Eval به فیلدهای آن دسترسی پیدا کنید.

شی DataBinder این تابع را در اختیار ما می‌گذارد:
DataBinder.Eval(container, "dataitem.Title")
در مثال فوق، شما به جای Title نام فیلد مورد نظر خود را خواهید گذاشت.

البته این کار را به این راحتی هم می‌توانید انجام دهید:
Eval(“Title”)
حالا این کد باید کجا نوشته بشود؟
کافیست که در محیط Edit Templates، روی شی مورد نظر کلیک کرده و Edit Bindings را برای آن انتخاب کنید، حالا با انتخاب Property مورد نظر و گزینه Custom binding  برای آن می‌توانید Code Expression مورد نظر را (Eval(“FieldName”)) برای آن بنویسید.

پیغام خطا برای Delete Button Field


  
اگر به یک Gridview یک Button Field اضافه کنید و Command Name آن را Delete بگذارید و ItemCommand Event را برای آن بنویسید، احتمالا وقت Raise شدن، پیغام خطایی مبنی بر Handle نکردن Event دریافت خواهید کرد، این خطا به این دلیل اتفاق می‌افتد که کلیلک کردن روی دکمه‌ای که شما Command Name ‌آن را Delete گذاشته‌اید دو Event را فراخوانی می‌کند: itemCommand و Delete را که شما دومی را Handle نکرده‌اید و این مشکل به دلیل کلمه Delete که یک Reserved word است بوجود می‌اید.

پیشنهاد می‌کنم که برای پیشگیری این مشکل، یا Command name را تغییر دهید و یا از یک Command Filed  از نوع Delete استفاده نمایید.

توابع بازگشتی (Recursive)



مقدمه

در مورد این توابع در کتابهای مختلف مطالب بسیاری نوشته شده‌است ولی معمولا برنامه‌نویسهای نه‌چندان کهنه‌کار با نوشتن این‌گونه توابع مشکلاتی دارند که به نظر من این مشکلات از عدم تسلط به مفهوم Recursive ناشی می‌شوند.

تابعی که خودش را صدا کند یک تابع بازگشتی و یا Recursive است، بنابراین تشخیص یک تابع بازگشتی کار سختی نیست،‌کافی است که در متن کد یک تابع نام خودش را مشاهده کنید، این یعنی که ما با یک تابع بازگشتی سرو کار داریم.

شاید در مورد Trigger های SQL اصطلاحات Direct Recursion و Indirect Recursion را شنیده باشید، می‌خواهم بگویم که مفاهیم بازگشت مستقیم و غیر مستقیم در مورد توابع هم صدق می‌کنند، به این معنی که اگر تابعی خودش خودش را صدا کند باعث یک Direct Recursion شده است ولی اگر تابع یک تابع دو را صدا کند و تابع دو تابع یک را صدا بزند این پروسه موجب یک Indirect Recursion می‌شود.


Sample Scenario

یک Tree Control را در نظر بگیرید، نگران نشوید موضوع پیچیده نیست منظورم یک شی مثل Windows Explorer است که Folder ها را در یک ساختار درختی نمایش می‌دهد فقط همین، فرض می‌کنیم که هریک از Node های موجود در این درخت یک Property به نام ID دارند مثل اکثر Object ها که یک مشخصه به نام ID دارند، ما می‌خواهیم تابعی بنویسیم که با گرفتن یک ID جستجو بکند و Node مربوط به آن ID را برای ما برگرداند.

یک مثال ساده از توابع بازگشتی، مثال تابع فاکتوریل است که نمونه C++ آن را اینجا نوشته‌ام:
int Factorial(int a)
{
      if (a>1)
      {
            return (a * Factorial(a-1));
      }
      else
      {
            return 1;
      }
}
استفاده از توابع بازگشتی مثل بقیه توابع است، یعنی فقط Call می‌شوند همین:

cout << Factorial(3) <<"\n";

Sample Code

آین کد برای VB.NET نوشته شده است اگر به syntax دیگری نیاز داشتید به من اطلاع بدهید
همینطور که ملاحظه می‌کنید این تابع دو ورودی می‌گیرد،‌ یک Node به عنوان نقطه شروع جستجو و یک ID به عنوان مشخصه Node مورد جستجو.
توجه داشته باشید که هیچ محدودیتی در Hierarchy وجود ندارد یعنی هر Node می‌تواند بی‌نهایت Node به عنوان فرزند داشته باشد و همچنین هریک از فرزندها . اصلا به همین دلیل است که ما به یک تابع یازگشتی نیاز داریم.

Private Function FindNodeByIDinTree(StartNode as treeNodeObject,ID as string)
           
            Dim M as TreeNode
            Dim n as TreeNode
            if StartNode.ID = ID then
                        Return StartNode
            else
                        If StartNode.nodes.count <>o then
                                    for each n in StartNode.nodes
                                                m = FindNodeByIDinTree (n,ID)                   
                                                If not m is nothing then
                                                            return m
                                                End If
                                    next
                        else
                                    retirn nothing
                        end if
            end if

End Function

Master-Detail در Grid


پیش فرض:
در Database دو Table به مرتبط با مشخصات زیر وجود دارند:

“Categories” table
ID, CategoryName
“Products” table
      ID, ProductName, CategoryID

بین این دو Table یک Relation بواسطه ID در “categories” و CategoryID در “Products” وجود دارد.

هدف:
نمایش همزمان Category ها و Product ها را در یک Gridview



روش:
در حقیقت در شکل فوق دو gridview وجود دارد، به این معنی که در template column در gridview اول، gridview دیگر گذاشته شده است.
پس از ایجاد دو gridview به ترتیبی که گفته شد کدهای زیر را برای پر کردن Gridview ها در Page_Load می نویسیم.
دقت کنید که در Select Command برای Data adapter برای دو Table دستور Select نوشته شده است:
C#:
            System.Data.SqlClient.SqlDataAdapter da;
            da = new System.Data.SqlClient.SqlDataAdapter("select * from categories;select * from products","Data Source=.;Initial Catalog=TrainingDB;Integrated Security=True");
            System.Data.DataSet ds;
            ds = new System.Data.DataSet();
            da.Fill(ds);
ds.Tables[0].TableName = "Categories";
            ds.Tables[1].TableName = "Products";
VB.NET:
Dim da As New SqlClient.SqlDataAdapter("select * from maincategories;select * from Products", SqlConnection1)
      Dim ds As New DataSet
 da.Fill(ds)
 ds.Tables(0).TableName = "Categories"
 ds.Tables(1).TableName = "Products"

ساختن Relation و اضافه کردن آن به Dataset:

C#:
            System.Data.DataColumn ParentDC = ds.Tables["Categories"].Columns["id"];
            System.Data.DataColumn ChildDC = ds.Tables["Products"].Columns["Categoryid"];
            System.Data.DataRelation dr;
            dr = new System.Data.DataRelation("Categories_Products", ParentDC, ChildDC);
            ds.Relations.Add(dr);

VB.NET
        Dim Parent As DataColumn = ds.Tables("Categories").Columns("ID")
        Dim Child As DataColumn = ds.Tables("Products").Columns("CategoryID")

        Dim CategoryRelation As DataRelation = New DataRelation("Categories_Products", Parent, Child, False)
        ds.Relations.Add(Categories_Products)

پر کردن Gridview اول:

C#
            GridView1.DataSource = ds.Tables["Categories"].DefaultView;
            GridView1.DataBind();
VB.NET:
        DataGrid1.DataSource = ds.Tables("Categories").DefaultView
        DataGrid1.DataBind()

پر کردن Gridview دوم:
این کد باید برای Datasource مربوط به Gridview دوم نوشته شود و نه در Page_load، اگر نمی دانید که چگونه آنرا از طریق Property و Wizard وارد کنید، آن را در محیط  HTMLبرای Gridview دوم و در DataSource property آن بنویسید.

خود کد برای VB.NET به این ترتیب است:

CType(Container.DataItem,DataRowView).CreateChildView("Categories_Products”)
حتما متوجه شده اید این کد برساس ID هر سط و Relation تعریف شده برای Gridview موجود در آن سطر، Datasource سازد.
کد برای C#:

((System.Data.DataRowView)Container.DataItem).CreateChildView("Categories_Products")



Client side و Server side



--الف--
یکی از بهترین روشها برای اضافه کردن Client side code به یک Server side object استفاده از attributes property است.

btnsrv.Attributes.Add("onclick", "window.close();");

البته می توانید، به جای نوشتن کد، یک Function را که قبلا نوشته اید صدا بزنید


--ب--
می توانید Script های Client side را از طریق کد Server side بنویسید، این یک Function برای نوشتن یک Client side script است
int CloseWindow()
    {
        try
        {
            string sb;
            sb = "";
                Page.RegisterStartupScript("window.closeHandler", sb.ToString());
            return 1;
        }
        catch
        {
            return 0;        }     }

کافیست که این Function را در Page load صدا کنید، این کد Client side را می توانید کنار کد html با view source مشاهده کنید، حالا این Function می تواند در انواع client side event ها استفاده بشود.

--ج--
برای نوشتن کد Server side میان کدهای html (نه کدهای script نویسی) به این شکل عمل می کنیم

به این ترتیب می توان کدهای Client side را در کنار کدهای html نوشت.


--د
برای کارکردن با یک Server side control در client side به این ترتیب عمل کنید

alert(document.all("myserversidecontrol").id);

با اینکه all در property های document دیده نمی شود ولی هنوز قابل استفاده است