مطالب
نگهداری رشته ها (String) در حافظه به صورت Encrypt
در همین سایت در بخش لینک‌های ارسالی ، لینکی توسط آقای امیر هاشم زاده به اشتراک گذاشته شده بود با عنوان  "چرا هکرها نوع داده String را دوست دارند" ؛ مقاله ای بود در سایت CodeProject که در آن روش هایی که هکرها توسط آن می‌توانند اطلاعات حساس نرم افزار را که در قالب String در حافظه ذخیره شده اند را بررسی نمایند.
اصل مطلب را می‌توانید اینجا مطالعه کنید.

در دات نت فریم ورک کلاسی با عنوان SecureString وجود دارد که توسط آن می‌توان عبارات رشته ای که دارای اطلاعات حساس می‌باشند را به صورت رمز گذاری شده در حافظه ذخیره نمود.

نمونه ای از استفاده این تابع را در زیر مشاهده میکنید:
public class Example
{
    public static void Main()
    {
        .
        SecureString securePwd = new SecureString();
        ConsoleKeyInfo key;

        Console.Write("Enter password: ");
        do {
           key = Console.ReadKey(true);

           // بررسی میشود که کلید فشرده شده جزو حروف الفبا می‌باشد یا کلید دیگری است
           if (((int) key.Key) >= 65 && ((int) key.Key <= 90)) {
              // کاراکتر مربوط به کلید فشرده شده به انتهای متغیر سکوراسترینک اضافه می‌شود
              securePwd.AppendChar(key.KeyChar);
              Console.Write("*");
           }   
        // خروج از حلقه در صورت فشردن کلید اینتر
        } while (key.Key != ConsoleKey.Enter);
        Console.WriteLine();

        try
        {
            MessageBox.Show(securePwd);
        }
        catch (Win32Exception e)
        {
            Console.WriteLine(e.Message);
        }
    }
}

در کدهای بالا رمز عبور از کاربر دریافت شده و در متغیر securepwd که شئی از کلاس SecureString می‌باشد ذخیره می‌شود.پس از آن شئی SecureString عبارت مربوطه را به صورت رمز گذاری شده در حافظه ذخیره میکند.
در این روش ابتدا مقدار کلید فشرده شده در متغیر Key که از نوع ConsoleKeyInfo تعریف شده ذخیره می‌شود. بعد از آن مقدار آن بررسی شده و اگر جزو حروف الفبای انگلیسی بود به انتهای متغیر securepwd افزوده می‌شود. این کار با متد AppendChar انجام می‌شود. این عملیات تا فشرده شدن کلید Enter ادامه پیدا میکند.
مطالب
آشنایی با CLR: قسمت بیست و پنجم
یکی از مهمترین خصوصیات CLR این است که نوع‌ها، ایمن هستند و همواره می‌داند که هر شیء از چه نوعی است. برای اثبات این ادعا می‌توانید متد GetType را صدا بزنید تا به شما بگوید این شیء از چه نوعی است. متد GetType قابلیت رونویسی ندارد و به همین علت می‌توانید مطمئن باشید که خروجی برگشتی دستکاری نشده است.
یکی از نیازهای طراحان این است که مرتبا نیاز به تبدیل نوع‌ها را به یکدیگر دارند. CLR به شما اجازه می‌دهد که هر آبجکتی را به نوع مربوط به خودش یا والدینش تبدیل کنید. بسته به زبانی که انتخاب می‌کنید، این تبدیل شکل متفاوتی دارد و در سی شارپ نیاز به سینتکس خاصی نیست.
سی شارپ برای تبدیل یک شیء به نوع‌های والدش، نیازی به ذکر نوع ندارد ولی اگر قرار است از سمت والد به سمت فرزند Cast شود نیاز است که صریحا نوع آن را اعلام کنید. در این روش اگر نوع تبدیلات با شیء ما سازگاری نداشته باشد، در زمان اجرا، با خطای
 InvalidCastExceptio
 روبرو خواهید شد. کد زیر نمونه‌ای از این تبدیلات است:
internal class Employee {
...
}    
public sealed class Program {
     public static void Main() {
        // بدون ذکر نام والد تبدیل صورت میگیرد
        Object o = new Employee();

      // برای تبدیل والد به یکی از مشتقات آن نیاز است
        // نوع آن به طور صریح ذکر گردد
         // در بعضی زبان‌های مثل ویژوال بیسیک نیازی به ذکر آن نیست
        Employee e = (Employee) o;
     }
  }

استفاده از کلمات as و is در تبدیلات
یکی دیگر از روش‌های امن برای cast کردن اشیاء، استفاده از کلمه‌ی کلیدی is هست. این عبارت چک می‌کند که آیا شیء مورد نظر، از نوع تبدیلی ما پشتیبانی می‌کند یا خیر؛ اگر true بازگرداند به این معنی است که پشتیبانی می‌شود و در حین cast کردن با خطایی روبرو نمی‌شویم.
Object o = new Object();
  Boolean b1 = (o is Object);   // b1 is true.
  Boolean b2 = (o is Employee); // b2 is false.
پی نوشت :در این بررسی اگر شیء نال باشد، مقدار برگشتی همیشه false است. چون به هیچ نوعی قابل تبدیل نیست.
نحوه‌ی استفاده‌ی از کلمه کلیدی is در این تبدیل به شکل زیر است:
if (o is Employee) 
{     
         Employee e = (Employee) o;
}
کد بالا با اینکه ایمنی بیشتری دارد، ولی از نظر کارآیی هزینه بر است. دلیل آن هم این است که عمل تاییدیه، در دو مرتبه انجام می‌شود: اولین مرحله‌ی تایید، استفاده از عبارت is است تا بررسی کند آیا این شیء قابل تبدیل به نوع مورد نظر است یا خیر. دومین بررسی هم در حین تبدیل یا Cast کردن اتفاق می‌افتد که خود این تبدیل هم، همانطور که در بالا اشاره کردیم، بررسی‌هایی برای تبدیل دارد.

برای بهبود کد بالا، سی شارپ کلمه‌ی کلیدی as را ارائه می‌کند. کلمه کلیدی as باعث می‌شود اگر شیء به آن نوع قابل تبدیل باشد، ارجاعی صورت بگیرد؛ در غیر این صورت مقدار نال بازگشت داده می‌شود. شاید شما بگویید که در خط بعدی ما نیز دوباره مجددا یک عبارت شرطی داریم و دوباره داریم عمل تاییدیه را انجام می‌دهیم. ولی باید گفت این if به مراتب هزینه‌ی کمتری نسبت به بررسی‌های تبدیل یا Cast به شیوه‌ی بالاست.
Employee e = o as Employee;
  if (e != null) {
     .....
  }

فضاهای نام و اسمبلی ها
همانطور که مطلع هستید، فضاهای نام به ما این اجازه را می‌دهند تا نوع‌ها را به صورت منطقی گروه بندی کنیم تا دسترسی به آنان راحت‌تر باشد. برای مثال مطمئنا با نگاه به اسم فضای نام
System.Text
متوجه می‌شویم که داخل آن، نوع‌های متفاوتی برای کار با رشته‌ها وجود دارد. برای دسترسی به یک نوع، ابتدا باید از فضاهای نام آن شروع کرد و به شیوه‌ی زیر، به نوع‌ها دسترسی پیدا کرد:
public sealed class Program {
     public static void Main() {
        System.IO.FileStream fs = new System.IO.FileStream(...);
        System.Text.StringBuilder sb = new System.Text.StringBuilder();
     }
  }
احتمالا متوجه‌ی شلوغی و طولانی شدن بی جهت کدها شده‌اید. برای رفع این مشکل، هر زبان شیوه‌ای را می‌تواند بکار بگیرد که سی شارپ از کلمه‌ی کلیدی Using و مثلا ویژوال بیسیک از کلمه‌ی کلیدی Import و ... استفاده می‌کنند و حال می‌توانیم کد بالا را خلاصه‌تر و منظم‌تر بنویسیم:
using System.IO;    // Try prepending "System.IO." 
 using System.Text;  // Try prepending "System.Text." 
  
public sealed class Program {
     public static void Main() {
        FileStream fs = new FileStream(...); 
       StringBuilder sb = new StringBuilder();
     }
  }

موقعیکه شما نوعی را در یک فضای نام استفاده می‌کنید، این نوع به ترتیب بررسی می‌کند که نوع، در کدام فضای نام و کدام اسمبلی مورد استفاده قرار گرفته است. این اسمبلی‌ها شامل FCL و اسمبلی‌های خارجی است که به آن لینک کرده‌اید. حال ممکن است این سؤال پیش بیاید که ممکن است نام دو نوع، در دو فضای نام متفاوت، یکی باشد و در یک جا مورد استفاده قرار گرفته‌اند. چگونه می‌توان تشخیص داد که کدام نوع، متعلق به دیگری است؟ نظر مایکروسافت این است که تا می‌توانید سعی کنید از اسامی متفاوت استفاده کنید. ولی در بعضی شرایط این مورد ممکن نیست. به همین علت باید هر دو کلاس یا به طور کامل، به همراه فضای نام نوشته شوند؛ یا اینکه یکی از آن‌ها بدین شکل باشد و فضای نام نوع دیگر، با using صدا زده شود.
در مثال زیر ما دو نوع را با نام Widget داریم که در دو فضای نام Microsoft و Dotnettips وجود دارند:
using Microsoft; 
using Dotnettips ;
public sealed class Program {
     public static void Main() {
        Widget w = new Widget();// An ambiguous reference
     } 
 }
در کد بالا به دلیل اینکه مشخص نیست نوعی که مدنظر شماست، کدام است، با خطای زیر روبرو می‌شوید:
 'Widget' is an ambiguous reference between 'Microsoft.Widget' and 'Dotnettips.Widget
به همین علت کد را به شکل زیر تغییر می‌دهیم:
using Microsoft; 
using Dotnettips;
   
public sealed class Program {
     public static void Main() {
        Dotnettips.Widget w = new Dotnettips.Widget(); // Not ambiguous
     }
  }
یا بدین صورت:
using Microsoft; 
using Dotnettips;
using DotnettipsWidget = Dotnettips.Widget;   

public sealed class Program {
     public static void Main() {
        DotnettipsWidget w = new DotnettipsWidget (); // No error now
     }
  }
حال بیایید تصور کنیم که فضای‌های نام هم یکسان شده‌اند. مثلا شرکتی به اسم Australian Boomerang Company و شرکت دیگری به اسم Alaskan Boat Corporation یک اسمبلی با نام Widget را تولید کرده اند و تحت فضای نام ABC منتشر کرده‌اند.با اینکه مایکروسافت سفارش زیادی کرده است که از نام کامل استفاده شود و مخفف‌ها را مورد استفاده قرار ندهید ولی از اتفاقاتی است که ممکن است رخ بدهد. در این حالت خوشبختانه کمپایلر سی شارپ قابلیتی به نام Extern را معرفی کرده است.
مطالب
نگاشت IDictionary در Fluent NHibernate

نگاشت خودکار مجموعه‌ها در Fluent NHibernate ساده است و نیاز به تنظیم خاصی ندارد. برای مثال IList به صورت خودکار به Bag ترجمه می‌شود و الی آخر.
البته شاید سؤال بپرسید که این Bag از کجا آمده؟ کلا 6 نوع مجموعه در NHibernate پشتیبانی می‌شوند که شامل Array، Primitive-Array ، Bag ، Set ، List و Map هستند؛ این‌ اسامی هم جهت حفظ سازگاری با جاوا تغییر نکرده‌اند و گرنه معادل‌های آن‌ها در دات نت به این شرح هستند:
Bag=IList
Set=Iesi.Collections.ISet
List=IList
Map=IDictionary

البته در دات نت 4 ، ISet هم به صورت توکار اضافه شده، اما NHibernate از مدت‌ها قبل آن‌را از کتابخانه‌ی Iesi.Collections به عاریت گرفته است. مهم‌ترین تفاوت‌های این مجموعه‌ها هم در پذیرفتن یا عدم پذیرش اعضای تکراری است. Set و Map اعضای تکراری نمی‌پذیرند.
در ادامه می‌خواهیم طرز کار با Map یا همان IDictionary دات نت را بررسی کنیم:

الف) حالتی که نوع کلید و مقدار (در یک عضو Dictionary تعریف شده)، Entity نیستند
using System.Collections.Generic;

namespace Test1.Model12
{
public class User
{
public virtual int Id { set; get; }
public virtual string Name { get; set; }
public virtual IDictionary<string, string> Preferences { get; set; }
}
}

نحوه تعریف نگاشت که مبتنی است بر مشخص سازی تعاریف کلید و مقدار آن جهت تشکیل یک Map یا همان Dictionary :
using FluentNHibernate.Automapping;
using FluentNHibernate.Automapping.Alterations;

namespace Test1.Model12
{
public class UserMapping : IAutoMappingOverride<User>
{
public void Override(AutoMapping<User> mapping)
{
mapping.Id(x => x.Id);
mapping.HasMany(x => x.Preferences)
.AsMap<string>("FieldKey")
.Element("FieldValue", x => x.Type<string>().Length(500));
}
}
}

خروجی SQL متناظر:
create table "User" (
Id INT IDENTITY NOT NULL,
Name NVARCHAR(255) null,
primary key (Id)
)

create table Preferences (
User_id INT not null,
FieldValue NVARCHAR(500) null,
FieldKey NVARCHAR(255) not null,
primary key (User_id, FieldKey)
)

alter table Preferences
add constraint FKD6CB18523B1FD789
foreign key (User_id)
references "User"

ب) حالتی که مقدار، Entity است
using System.Collections.Generic;

namespace Test1.Model13
{
public class User
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IDictionary<string, Property> Properties { get; set; }
}

public class Property
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string Value { get; set; }
public virtual User User { get; set; }
}
}

نحوه تعریف نگاشت:
using FluentNHibernate.Automapping;
using FluentNHibernate.Automapping.Alterations;

namespace Test1.Model13
{
public class UserMapping : IAutoMappingOverride<User>
{
public void Override(AutoMapping<User> mapping)
{
mapping.Id(x => x.Id);
mapping.HasMany<Property>(x => x.Properties)
.AsMap<string>("FieldKey")
.Component(x => x.Map(c => c.Id));
}
}
}
خروجی SQL متناظر:
create table "Property" (
Id INT IDENTITY NOT NULL,
Name NVARCHAR(255) null,
Value NVARCHAR(255) null,
User_id INT null,
primary key (Id)
)

create table "User" (
Id INT IDENTITY NOT NULL,
Name NVARCHAR(255) null,
primary key (Id)
)

create table Properties (
User_id INT not null,
Id INT null,
FieldKey NVARCHAR(255) not null,
primary key (User_id, FieldKey)
)

alter table "Property"
add constraint FKF9F4D85A3B1FD7A2
foreign key (User_id)
references "User"

alter table Properties
add constraint FK63646D853B1FD7A2
foreign key (User_id)
references "User"

ج) حالتی که کلید، Entity است
using System;
using System.Collections.Generic;

namespace Test1.Model14
{
public class FormData
{
public virtual int Id { get; set; }
public virtual DateTime? DateTime { get; set; }
public virtual IDictionary<FormField, string> FormPropertyValues { get; set; }
}

public class FormField
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
}

نحوه تعریف نگاشت:
using FluentNHibernate.Automapping;
using FluentNHibernate.Automapping.Alterations;

namespace Test1.Model14
{
public class FormDataMapping : IAutoMappingOverride<FormData>
{
public void Override(AutoMapping<FormData> mapping)
{
mapping.Id(x => x.Id);
mapping.HasMany<FormField>(x => x.FormPropertyValues)
.AsEntityMap("FieldId")
.Element("FieldValue", x => x.Type<string>().Length(500))
.Cascade.All();
}
}
}
خروجی SQL متناظر:
create table "FormData" (
Id INT IDENTITY NOT NULL,
DateTime DATETIME null,
primary key (Id)
)

create table FormPropertyValues (
FormData_id INT not null,
FieldValue NVARCHAR(500) null,
FieldId INT not null,
primary key (FormData_id, FieldId)
)

create table "FormField" (
Id INT IDENTITY NOT NULL,
Name NVARCHAR(255) null,
primary key (Id)
)

alter table FormPropertyValues
add constraint FKB807B9C090849E
foreign key (FormData_id)
references "FormData"

alter table FormPropertyValues
add constraint FKB807B97165898A
foreign key (FieldId)
references "FormField"

یک مثال عملی:
امکانات فوق جهت طراحی قسمت ثبت اطلاعات یک برنامه «فرم ساز» مبتنی بر Key-Value بسیار مناسب هستند؛ برای مثال:
برنامه‌ای را در نظر بگیرید که می‌تواند تعدادی خدمات داشته باشد که توسط مدیر برنامه قابل اضافه شدن است؛ برای نمونه خدمات درخواست نصب نرم افزار، خدمات درخواست تعویض کارت پرسنلی، خدمات درخواست مساعده، خدمات ... :
public class Service
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<ServiceFormField> Fields { get; set; }
public virtual IList<ServiceFormData> Forms { get; set; }
}

برای هر خدمات باید بتوان یک فرم طراحی کرد. هر فرم هم از یک سری فیلد خاص آن خدمات تشکیل شده است. برای مثال:
public class ServiceFormField
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual bool IsRequired { get; set; }
public virtual Service Service { get; set; }
}

در اینجا نیازی نیست به ازای هر فیلد جدید واقعا یک فیلد متناظر به دیتابیس اضافه شود و ساختار آن تغییر کند (برخلاف حالت dynamic components که پیشتر در مورد آن بحث شد).
اکنون با داشتن یک خدمات و فیلدهای پویای آن که توسط مدیربرنامه تعریف شده‌اند، می‌توان اطلاعات وارد کرد. مهم‌ترین نکته‌ی آن هم IDictionary تعریف شده است که حاوی لیستی از فیلدها به همراه مقادیر وارد شده توسط کاربر خواهد بود:
public class ServiceFormData
{
public virtual int Id { get; set; }
public virtual IDictionary<ServiceFormField, string> FormPropertyValues { get; set; }
public virtual DateTime? DateTime { get; set; }
public virtual Service Service { get; set; }
}

در مورد نحوه نگاشت آن هم در حالت «ج» فوق توضیح داده شد.

مطالب
مروری بر طراحی Schema less بانک اطلاعاتی SisoDb
اس کیوال سرور، از سال 2005 به بعد، به صورت توکار امکان تعریف و ذخیره سازی اطلاعات schema less و یا schema free را به کمک فیلدهایی از نوع XML ارائه داده است؛ به همراه یکپارچگی آن با زبان XQuery برای تهیه کوئری‌های سریع سمت سرور. در فیلدهای XML می‌توان اطلاعات انواع و اقسام اشیاء را بدون اینکه نیازی به تعریف تک تک فیلدهای مورد نیاز، در بانک اطلاعاتی وجود داشته باشد، ذخیره کرد. یک نمونه از کاربرد چنین امکانی، نوشتن برنامه‌های «فرم ساز» است. برنامه‌هایی که کاربران آن می‌توانند فیلد اضافه و کم کرده و نهایتا اطلاعات را ذخیره و از آن‌ها کوئری بگیرند.
خوب، این فیلد کمتر بحث شده XML، فقط در اس کیوال سرور و نگارش‌های اخیر آن وجود دارد. اگر نیاز به کار با بانک‌های اطلاعاتی سبک‌تری وجود داشت چطور؟ یک راه حل عمومی برای این مساله مراجعه به روش‌های NoSQL است. یعنی بطور کلی بانک‌های اطلاعاتی رابطه‌ای کنار گذاشته شده و به یک سکوی کاری دیگر سوئیچ کرد. در این بین، SisoDb راه حل میانه‌ای را ارائه داده است. با کمک SisoDb می‌توان اطلاعات را به صورت schema less و بدون نیاز به تعریف فیلدهای متناظر آن‌ها، در انواع و اقسام بانک‌های اطلاعاتی SQL Server با فرمت JSON ذخیره و بازیابی کرد. این انواع و اقسام، شامل SQL Server CE نیز می‌شود.


دریافت و نصب SisoDb

دریافت و نصب SisoDb بسیار ساده است. به کمک package manager و امکانات NuGet، کلمه Sisodb را جستجو کنید. در بین مداخل ظاهر شده، پروایدر مورد علاقه خود را انتخاب و نصب نمائید. برای مثال اگر قصد دارید با SQL Server CE کار کنید، SisoDb.SqlCe4 را انتخاب و یا اگر SQL Server 2008 مدنظر شما است، SisoDb.Sql2008 را انتخاب و نصب نمائید.


ثبت و بازیابی اطلاعات به کمک SisoDb

کار با SisoDb بسیار روان است. نیازی به تعاریف نگاشت‌ها و ORM خاصی نیست. یک مثال مقدماتی آن‌را در ادامه ملاحظه می‌کنید:
using SisoDb.Sql2008;

namespace SisoDbTests
{
    public class Customer 
    {
        public int Id { get; set; } 
        public int CustomerNo { get; set; }
        public string Name { get; set; } 
    }

    class Program
    {
        static void Main(string[] args)
        {
            /*var cnInfo = new SqlCe4ConnectionInfo(@"Data source=sisodb2013.sdf;");
            var db = new SqlCe4DbFactory().CreateDatabase(cnInfo);
            db.EnsureNewDatabase();*/

            var cnInfo = new Sql2008ConnectionInfo(@"Data Source=(local);Initial Catalog=sisodb2013;Integrated Security = true");            
            var db = new Sql2008DbFactory().CreateDatabase(cnInfo);            
            db.EnsureNewDatabase();

            var customer = new Customer 
            {
                CustomerNo = 20,
                Name = "Vahid" 
            };
            db.UseOnceTo().Insert(customer);

            using (var session = db.BeginSession()) 
            {
                var info = session.Query<Customer>().Where(c => c.CustomerNo == 20).FirstOrDefault();

                var info2 = session.Query<Customer>().Where(c => c.CustomerNo == 20 && c.Name=="Vahid").FirstOrDefault();
            }
        }
    }
}
در این مثال، ابتدا اتصال به بانک اطلاعاتی برقرار شده و سپس بانک اطلاعاتی جدید تهیه می‌شود. سپس یک وهله از شیء مشتری ایجاد و ذخیره می‌گردد. در ادامه دو کوئری بر روی بانک اطلاعاتی انجام شده است.


ساختار داخلی SisoDb

SisoDb به ازای هر کلاس، حداقل 9 جدول را ایجاد می‌کند. در ادامه نحوه ذخیره سازی شیء مشتری ایجاد شده و مقادیر خواص آن‌را نیز مشاهده می‌نمائید:


ذخیره سازی جداگانه خواص عددی

ذخیره سازی جداگانه خواص رشته‌ای

ذخیره سازی کلی شیء مشتری با فرمت JSON به صورت یک رشته


همانطور که ملاحظه می‌کنید، یک جدول کلی SisoDbIdentities ایجاد شده است که اطلاعات نام اشیاء را در خود نگهداری می‌کند. سپس اطلاعات خواص اشیاء یکبار به صورت JSON ذخیره می‌شوند؛ با تمام اطلاعات تو در توی ذخیره شده در آن‌ها و همچنین یکبار هم هر خاصیت را به صورت یک رکورد جداگانه، بر اساس نوع کلی آن‌ها، در جداول رشته‌ای، عددی و امثال آن ذخیره می‌کند.
شاید بپرسید که چرا به همان فیلد رشته‌ای JSON اکتفاء نشده است؟ از این جهت که پردازشگر سمت بانک اطلاعاتی آن همانند فیلدهای XML در SQL Server و نگارش‌های مختلف آن وجود ندارد (برای مثال به کمک زبان T-SQL می‌توان از زبان XQuery در خود بانک اطلاعاتی، بدون نیاز به واکشی کل اطلاعات در سمت کلاینت، به صورت یکپارچه استفاده کرد). به همین جهت برای کوئری گرفتن و یا تهیه ایندکس، نیاز است این موارد جداگانه ذخیره شوند.
به این ترتیب زمانیکه کوئری تهیه می‌شود، برای مثال:
 var info = session.Query<Customer>().Where(c => c.CustomerNo == 20).FirstOrDefault();
به کوئری زیر ترجمه می‌گردد:
SELECT DISTINCT TOP(1) (s.[StructureId]),
                           s.[Json]
                    FROM   [CustomerStructure] s
                           LEFT JOIN [CustomerIntegers] mem0
                                ON  mem0.[StructureId] = s.[StructureId]
                                AND mem0.[MemberPath] = 'CustomerNo'
                    WHERE  (mem0.[Value] = 20);
و یا کوئری ذیل:
var info2 = session.Query<Customer>().Where(c => c.CustomerNo == 20 && c.Name=="Vahid").FirstOrDefault();
معادل زیر را خواهد داشت:
SELECT DISTINCT TOP(1) (s.[StructureId]),
                           s.[Json]
                    FROM   [CustomerStructure] s
                           LEFT JOIN [CustomerIntegers] mem0
                                ON  mem0.[StructureId] = s.[StructureId]
                                AND mem0.[MemberPath] = 'CustomerNo'
                           LEFT JOIN [CustomerStrings] mem1
                                ON  mem1.[StructureId] = s.[StructureId]
                                AND mem1.[MemberPath] = 'Name'
                    WHERE  ((mem0.[Value] = 20) AND (mem1.[Value] = 'Vahid'));
در هر دو حالت از جداول کمکی تعریف شده برای تهیه کوئری استفاده کرده و نهایتا فیلد JSON اصلی را برای نگاشت نهایی به اشیاء تعریف شده در برنامه بازگشت می‌دهد.

در کل این هم یک روش تفکر و طراحی Schema less است که با بسیاری از بانک‌های اطلاعاتی موجود سازگاری دارد.
برای مشاهده اطلاعات بیشتری در مورد جزئیات این روش می‌توان به Wiki آن مراجعه کرد.
نظرات مطالب
نمایش تاریخ بر حسب تعداد روزهای گذشته
سلام؛ در قسمتی از سایت، بخش مطالب این ماه قرار داره. شما برای به دست آوردن مطالب این ماه، چطور تاریخ رو محاسبه می‌کنید؟ من خودم به این روش رسیدم:
public class Post
{
       public int Id { get; set; }
       public string Title { get; set; }
       public DateTime dt { get; set; }
}
static void Main(string[] args)
{
       List<Post> ListOfPost = new List<Post>();
       DateTime dt = DateTime.Now;
       PersianCalendar pc = new PersianCalendar();
       int day = pc.GetDayOfMonth(dt);
       int month = pc.GetMonth(dt);
       int year = pc.GetYear(dt);
       int DaysInMonth = pc.GetDaysInMonth(year, month);
       DateTime FirstDayOfCurrentMonth = dt.AddDays(-day).Date;
       DateTime LastDayOfCurrentMonth = 
       FirstDayOfCurrentMonth.AddDays(DaysInMonth);
       var query = ListOfPost
                   .Where(x => x.dt.Date > FirstDayOfCurrentMonth.Date)
                   .Where(x => x.dt.Date <= LastDayOfCurrentMonth.Date)
                   .ToList();
}
این روش بهینه هست ؟
مطالب
مروری بر کاربردهای Action و Func - قسمت سوم
در ادامه مثال سوم قسمت قبل، در مورد حذف کدهای تکراری توسط Action و Func، در این قسمت به یک مثال نسبتا پرکاربرد دیگر آن جهت ساده سازی try/catch/finally اشاره خواهد شد.
احتمالا هزاران بار در کدهای خود چنین قطعه کدی را تکرار کرده‌اید:
try {
       // code
} catch(Exception ex) {
       // do something
}
این مورد را نیز می‌توان توسط Actionها کپسوله کرد و پیاده سازی قسمت بدنه try آن‌را به فراخوان واگذار نمود:
void Execute(Action action) {
    try {
       action();
    } catch(Exception ex) {
       // log errors
    }
}
و برای نمونه جهت استفاده از آن خواهیم داشت:
Execute(() => {open a file});

یا اگر عمل انجام شده باید خروجی خاصی را بازگرداند (برخلاف یک Action که خروجی از آن انتظار نمی‌رود)، می‌توان طراحی متد Execute را با Func انجام داد:
public static class SafeExecutor
{
    public static T Execute<T>(Func<T> operation)
    {
        try
        {
            return operation();
        }
        catch (Exception ex)
        {
            // Log Exception
        }
        return default(T);
    }
}
در این حالت فراخوانی متد Execute به نحو زیر خواهد بود:
var data = SafeExecutor.Execute<string>(() =>
{
    // do something
    return "result";
});
و اگر در این بین استثنایی رخ دهد، علاوه بر ثبت جزئیات خطای رخ داده شده، نال را بازگشت خواهد داد.

از همین دست می‌توان به کپسوله سازی منطق «سعی مجدد» در انجام کاری اشاره کرد:
public static class RetryHelper
{
   public static void RetryOperation(Action action, int numRetries, int retryTimeout)
   {
       if( action == null )
           throw new ArgumentNullException("action");

       do
       {
          try {  action(); return;  }
          catch
          { 
              if( numRetries <= 0 ) throw;
              else 
                 Thread.Sleep( retryTimeout );
           }
       } while( numRetries-- > 0 );
   }
}
برای مثال فرض کنید برنامه قرار است اطلاعاتی را از وب دریافت کند. ممکن است در سعی اول آن، خطای اتصال یا در دسترس نبودن لحظه‌ای سایت رخ دهد. در اینجا نیاز خواهد بود تا این عملیات چندین بار تکرار شود؛ که نمونه‌ای از آن‌را در ذیل ملاحظه می‌کنید:
RetryHelper.RetryOperation(() => SomeFunction(), 3, 1000);

پاسخ به بازخورد‌های پروژه‌ها
مشکل عمل نکردن فونت فارسی
- شما فونت سلکتور رو به صورت یک فیلد استاتیک تعریف کردید. یعنی هربار دارید به انتهای آن فونت‌های جدید را اضافه می‌کنید. عملکرد فونت سلکتور با اولین فونت اضافه شده به آن در صورتی که تعاریف آن حرف در فونت صفر موجود باشد، پایان خواهد یافت. به همین جهت تمام متون شما دارای یک فونت هستند چون کار به سایر فونت‌ها نرسیده است و نخواهد رسید.
روش صحیح تعریف فونت سلکتور با سربار کم به صورت زیر است:
        static FontSelector getFontSelector(string fontFileName, int size, int style, BaseColor color)
        {
            var systemRoot = Environment.GetEnvironmentVariable("SystemRoot");
            var mainPath = Path.Combine(systemRoot, "fonts\\" + fontFileName);
            if (!File.Exists(mainPath))
            {
                throw new FileNotFoundException(mainPath + " not found.");
            }
            if (!FontFactory.IsRegistered(mainPath))
            {
                FontFactory.Register(mainPath);
            }

            var tahomaPath = Path.Combine(systemRoot, "fonts\\tahoma.ttf");
            if (!File.Exists(tahomaPath))
            {
                throw new FileNotFoundException(tahomaPath + " not found.");
            }
            if (!FontFactory.IsRegistered(tahomaPath))
            {
                FontFactory.Register(tahomaPath);
            }

            var fontSelector = new FontSelector();
            fontSelector.AddFont(FontFactory.GetFont(mainPath, BaseFont.IDENTITY_H, true, size, style, color));
            fontSelector.AddFont(FontFactory.GetFont(tahomaPath, BaseFont.IDENTITY_H, true, size, style, color));
            return fontSelector;
        }
- در مورد گروه بندی، نیاز است منبع داده شما موجود باشد و همچنین sort صحیحی باید صورت گرفته باشد. یک نمونه مشابه برای بررسی.
مطالب
C# 6 - Index Initializers
زمان زیادی از ارائه‌ی امکان Collection Initializer برای ایجاد یک متغیر از نوع Collection می‌گذرد؛ برای نمونه به مثال زیر توجه کنید:
enum USState {...}
var AreaCodeUSState = new Dictionary<string, USState>
    {
        {"408", USState.California},
        {"701", USState.NorthDakota},
        ...
    };
در پشت صحنه، کامپایلر، Collection Initializer را می‌گیرد، با استفاده از یک <Dictionary<TKey, TValue و با فراخوانی متد Add آن بر روی لیست Collection Initializer شروع به درج آن در دیکشنری ساخته شده می‌کند. Collection Initializer فقط بر روی کلاس هایی که در آن‌ها IEnumerable پیاده سازی شده باشد امکان پذیر است چرا که کامپایلر کار اضافه کردن مقادیر اولیه را به ()IEnumerable.Add می‌سپارد.

اکنون در C# 6.0 ما می‌توانیم از Index Initializer استفاده کنیم:
enum USState {...}
var AreaCodeUSState = new Dictionary<string, USState>
    {
        ["408"] = USState.California,
        ["701"] = USState.NorthDakota,
        ...
    };
اولین تفاوتی که این دو روش با هم دارند این است که در حالت استفاده‌ی از Index Initializer پس از کامپایل، ()IEnumerable.Add فراخوانی نمی‌شود. این تفاوت بسیار مهم است و کار اضافه کردن مقادیر اولیه را با استفاده از کلید (Key) ویژه انجام می‌دهد.
شبه کد مثال بالا به صورت زیر می‌شود:

Collection Initializer
create a Dictionary<string, USState>
add to new Dictionary the following items: 
     "408", USState.California
     "701", USState.NorthDakota
Index Initializer
create a Dictionary<string, USState> then
using AreaCodeUSState's default Indexed property
    set the Value of Key "408" to USState.California
    set the Value of Key "701" to USState.NorthDakota
حال به مثال زیر توجه کنید:

Collection Initializer 
enum USState {...}
var AreaCodeUSState = new Dictionary<string, USState>
        {
            { "408", USState.Confusion},
            { "701", USState.NorthDakota },
            { "408", USState.California},
            ...
        };
Console.WriteLine( AreaCodeUSState.Where(x => x.Key == "408").FirstOrDefault().Value );
Index Initializer
enum USState {...}
var AreaCodeUSState = new Dictionary<string, USState>
    {
        ["408"] = USState.Confusion,
        ["701"] = USState.NorthDakota,
        ["408"] = USState.California,
        ...
    };
Console.WriteLine( AreaCodeUSState2.Where(x => x.Key == "408").FirstOrDefault().Value );  // output = California
هر دو کد بالا با موفقیت کامپایل و اجرا می‌شود، اما در زمان اجرای Collection Initializer هنگامیکه می‌خواهد مقدار دوم "408" را اضافه کند با استثناء ArgumentException متوقف می‌شود چرا که کلید "408" از قبل وجود دارد.
اما در زمان اجرا، Index Initializer به صورت کامل و بدون خطا این کار را انجام می‌دهد و در کلید "408" مقدار USState.Confusion قرار می‌گیرد. سپس "701" مقدار USState.NorthDakota و بعد از استفاده‌ی مجدد از کلید "408" مقدار USState.California جایگزین مقدار قبلی می‌شود.

var fibonaccis = new List<int>
    {
        [0] = 1,
        [1] = 2,
        [3] = 5,
        [5] = 13
    }
این کد هم معتبر است و هم کامپایل می‌شود. البته معتبر است، ولی صحیح نیست. <List<T اجازه‌ی تخصیص اندیسی فراتر از اندازه‌ی فعلی را نمی‌دهد.
تلاش برای تخصیص مقدار 1 با کلید 0 به <List<int، سبب بروز استثناء ArguementOutOfRangeException می شود. وقتی (List<T>.Add(item فراخوانی می‌شود اندازه‌ی لیست یک واحد افزایش می‌یابد. بنابراین باید دقت داشت که Index Initializer از ()Add. استفاده نمی‌کند؛ در عوض با استفاده از خصوصیت اندیس پیش فرض، مقداری را برای یک کلید تعیین می‌کند.
برای چنین حالتی بهتر است از همان روش قدیمی Collection Initializer استفاده کنیم:
var fibonaccis = new List<int>()
    {
        1,
        3,
        5,
        13
    };
مطالب
پیاده سازی UnitOfWork به وسیله MEF
در این پست قصد دارم یک UnitOfWork به روش MEF پیاده سازی کنم. ORM مورد نظر EntityFramework CodeFirst است. در صورتی که با UnitOfWork , MEF آشنایی ندارید از لینک‌های زیر استفاده کنید:
 برای شروع ابتدا مدل برنامه رو به صورت زیر تعریف کنید.
 public class Category
    {
        public int Id { get; set; }

        public string Title { get; set; }
    }
سپس فایل Map  رو برای مدل بالا به صورت زیر تعریف کنید.
 public class CategoryMap : EntityTypeConfiguration<Entity.Category>
    {
        public CategoryMap()
        {
            ToTable( "Category" );

            HasKey( _field => _field.Id );

            Property( _field => _field.Title )
            .IsRequired();            
        }
    }
برای پیاده سازی الگوی واحد کار ابتدا باید یک اینترفیس به صورت زیر تعریف کنید.
using System.Data.Entity;
using System.Data.Entity.Infrastructure;

namespace DataAccess
{
    public interface IUnitOfWork
    {
        DbSet<TEntity> Set<TEntity>() where TEntity : class;
        DbEntityEntry<TEntity> Entry<TEntity>() where TEntity : class;
        void SaveChanges();     
        void Dispose();
    }
}
DbContext مورد نظر باید اینترفیس مورد نظر را پیاده سازی کند و برای اینکه بتونیم اونو در CompositionContainer اضافه کنیم باید از Export Attribute استفاده کنیم.
چون کلاس DatabaseContext از اینترفیس IUnitOfWork ارث برده است برای همین از InheritedExport استفاده می‌کنیم.
[InheritedExport( typeof( IUnitOfWork ) )]
    public class DatabaseContext : DbContext, IUnitOfWork
    {
        private DbTransaction transaction = null;

        public DatabaseContext()           
        {
            this.Configuration.AutoDetectChangesEnabled = false;
            this.Configuration.LazyLoadingEnabled = true;
        }

        protected override void OnModelCreating( DbModelBuilder modelBuilder )
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

            modelBuilder.AddFormAssembly( Assembly.GetAssembly( typeof( Entity.Map.CategoryMap ) ) );
        }

        public DbEntityEntry<TEntity> Entry<TEntity>() where TEntity : class
        {
            return this.Entry<TEntity>();
        }      
    }
نکته قابل ذکر در قسمت OnModelCreating این است که یک Extension Methodبه نام AddFromAssembly (همانند NHibernate) اضافه شده است که از Assembly  مورد نظر تمام کلاس‌های Map رو پیدا می‌کنه و اونو به ModelBuilder اضافه می‌کنه. کد متد به صورت زیر است:
 public static class ModelBuilderExtension
    {
        public static void AddFormAssembly( this DbModelBuilder modelBuilder, Assembly assembly )
        {
            Array.ForEach<Type>( assembly.GetTypes().Where( type => type.BaseType != null && type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof( EntityTypeConfiguration<> ) ).ToArray(), delegate( Type type )
            {
                dynamic instance = Activator.CreateInstance( type );
                modelBuilder.Configurations.Add( instance );
            } );
        }
    }

برای پیاده سازی قسمت BusinessLogic ابتدا کلاس BusiessBase را در آن قرار دهید:
public class BusinessBase<TEntity> where TEntity : class
    {        
        public BusinessBase( IUnitOfWork unitOfWork )
        {
            this.UnitOfWork = unitOfWork;
        }

        [Import]
        public IUnitOfWork UnitOfWork
        {
            get;
            private set;
        }

        public virtual IEnumerable<TEntity> GetAll()
        {
            return UnitOfWork.Set<TEntity>().AsNoTracking();
        }

        public virtual void Add( TEntity entity )
        {
            try
            {             
                UnitOfWork.Set<TEntity>().Add( entity );
                UnitOfWork.SaveChanges();
            }
            catch
            {              
                throw;
            }
            finally
            {
                UnitOfWork.Dispose();
            }
        }
    }

تمام متد‌های پایه مورد نظر را باید در این کلاس قرار داد که برای مثال من متد Add , GetAll را براتون پیاده سازی کردم. UnitOfWork توسط ImportAttribute مقدار دهی می‌شود و نیاز به وهله سازی از آن نیست 
کلاس Category رو هم باید به صورت زیر اضافه کنید.
 public class Category : BusinessBase<Entity.Category>
    {      
        [ImportingConstructor]
        public Category( [Import( typeof( IUnitOfWork ) )] IUnitOfWork unitOfWork )
            : base( unitOfWork )
        {
        }
    }
.در انتها باید UI مورد نظر طراحی شود که من در اینجا از Console Application استفاده کردم. یک کلاس به نام Plugin ایجاد کنید  و کد‌های زیر را در آن قرار دهید.
public class Plugin
    {        
        public void Run()
        {
            AggregateCatalog catalog = new AggregateCatalog();

            Container = new CompositionContainer( catalog );

            CompositionBatch batch = new CompositionBatch();

            catalog.Catalogs.Add( new AssemblyCatalog( Assembly.GetExecutingAssembly() ) );

            batch.AddPart( this );

            Container.Compose( batch );
        }

        public CompositionContainer Container 
        {
            get; 
            private set;
        }
    }
در کلاس Plugin  توسط AssemblyCatalog تمام Export Attribute‌های موجود جستجو می‌شود و بعد به عنوان کاتالوگ مورد نظر به Container اضافه می‌شود. انواع Catalog در MEF به شرح زیر است:
  • AssemblyCatalog : در اسمبلی مورد نظر به دنبال تمام Export Attribute‌ها می‌گردد و آن‌ها را به عنوان ExportedValue در Container اضافه می‌کند.
  • TypeCatalog: فقط یک نوع مشخص را به عنوان ExportAttribute در نظر می‌گیرد.
  • DirectoryCatalog :  در یک مسیر مشخص تمام Assembly مورد نظر را از نظر Export Attribute جستجو می‌کند و آن‌ها را به عنوان ExportedValue در Container اضافه می‌کند. 
  • ApplicationCatalog :  در اسمبلی  و فایل‌های (EXE) مورد نظر به دنبال تمام Export Attribute‌ها می‌گردد و آن‌ها را به عنوان ExportedValue در Container اضافه می‌کند. 
  • AggregateCatalog : تمام موارد فوق را Support می‌کند.
کلاس Program  رو به صورت زیر بازنویسی کنید.
  class Program
    {
        static void Main( string[] args )
        {
            Plugin plugin = new Plugin();
            plugin.Run();

            Category category = new Category(plugin.Container.GetExportedValue<IUnitOfWork>());
            category.GetAll().ToList().ForEach( _record => Console.Write( _record.Title ) );
        }
    }
پروژه اجرا کرده و نتیجه رو مشاهده کنید.
مطالب
روش دیگر نوشتن Model binderهای سفارشی در ASP.NET Core 7x با معرفی IParseable
ASP.NET MVC از روش بکارگیری binding providerها برای تدارک زیرساخت model binding استفاده می‌کند که در این روش، داده‌های پارامترهای یک action method از طریق هدرها، کوئری استرینگ‌ها، بدنه‌ی درخواست و غیره تهیه می‌شوند. در حالت پیش‌فرض اگر این پارامترها از نوع‌های ساده‌ای مانند اعداد و یا DateTime تشکیل شده باشند و یا به همراه یک TypeConverter باشند که امکان تبدیل این رشته را به آن نوع خاص بدهد، به صورت خودکار bind خواهند شد و هر نوع دیگری، به صورت یک نوع پیچیده درنظر گرفته می‌شود. نوع پیچیده یعنی bind برای مثال اطلاعات بدنه‌ی درخواست به تک تک خواص آن نوع. برای نمونه در کنترلر زیر:
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private static readonly string[] Summaries =
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy",
        "Hot", "Sweltering", "Scorching",
    };

    // /WeatherForecast/GetForecast2?from=1&to=3
    [HttpGet("[action]")]
    public IEnumerable<WeatherForecast> GetForecast2(Duration days)
    {
        return Enumerable.Range(days.From, days.To)
                         .Select(index => new WeatherForecast
                                          {
                                              Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                                              TemperatureC = Random.Shared.Next(-20, 55),
                                              Summary = Summaries[Random.Shared.Next(Summaries.Length)],
                                          })
                         .ToArray();
    }
}
که از دو مدل زیر استفاده می‌کند:
public class Duration
{
    public int From { get; set; }
    public int To { get; set; }
}

public class WeatherForecast
{
    public DateOnly Date { get; set; }

    public int TemperatureC { get; set; }

    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);

    public string? Summary { get; set; }
}
می‌توان خواص پارامتر days را از طریق کوئری استرینگ‌های HttpGet، برای مثال با ارائه‌ی آدرس WeatherForecast/GetForecast2?from=1&to=3 به صورت خودکار تامین کرد. زمانیکه اطلاعات رسیده چنین شکلی را داشته باشند، کار پردازش و bind آن‌ها در حالت HttpGet، خودکار است.


روش دیگر پردازش اطلاعات رشته‌ای رسیده و تشکیل یک Model Binder سفارشی در ASP.NET Core 7x

اکنون فرض کنید بجای آدرس WeatherForecast/GetForecast2?from=1&to=3 که اطلاعات را از طریق کوئری استرینگ مشخص و استانداردی دریافت می‌کند، می‌خواهیم اطلاعات آن‌را از طریق یک قالب سفارشی و غیراستاندارد مانند WeatherForecast/GetForecast3/1-3 دریافت کنیم:
// /WeatherForecast/GetForecast3/1-3
[HttpGet("[action]/{days}")]
public IEnumerable<WeatherForecast> GetForecast3(Days days)
    {
        return Enumerable.Range(days.From, days.To)
                         .Select(index => new WeatherForecast
                                          {
                                              Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                                              TemperatureC = Random.Shared.Next(-20, 55),
                                              Summary = Summaries[Random.Shared.Next(Summaries.Length)],
                                          })
                         .ToArray();
    }
یکی از راه‌های انجام اینکار، نوشتن model binderهای سفارشی مخصوص است و یا اکنون در ASP.NET Core 7x می‌توان با پیاده سازی اینترفیس IParsable به صورت خودکار و با روشی دیگر به این مقصود رسید:
using System.Diagnostics.CodeAnalysis;
using System.Globalization;

namespace NET7Mvc.Models;

public class Days : IParsable<Days>
{
    public Days(int from, int to)
    {
        From = from;
        To = to;
    }

    public int From { get; }
    public int To { get; }

    public static bool TryParse([NotNullWhen(true)] string? value,
                                IFormatProvider? provider,
                                [MaybeNullWhen(false)] out Days result)
    {
        var items = value?.Split('-', StringSplitOptions.RemoveEmptyEntries);
        if (items is { Length: 2 })
        {
            if (int.TryParse(items[0], NumberStyles.None, provider, out var from)
                && int.TryParse(items[1], NumberStyles.None, provider, out var to))
            {
                result = new Days(from, to);
                return true;
            }
        }

        result = default;
        return false;
    }

    public static Days Parse(string value, IFormatProvider? provider)
    {
        if (!TryParse(value, provider, out var result))
        {
            throw new ArgumentException("Could not parse the given value.", nameof(value));
        }

        return result;
    }
}
- برای پیاده سازی این اینترفیس باید دو متد TryParse و Parse آن‌را به صورت فوق پیاده سازی کرد و توسط آن، روش تبدیل رشته‌ی دریافتی از کاربر را به شیء مدنظر، مشخص کرد.
- همینقدر که مدلی IParsable را پیاده سازی کرده باشد، از امکانات آن به صورت خودکار استفاده خواهد شد و نیازی به معرفی و یا تنظیمات خاص دیگری ندارد.
- البته این قابلیت جدید نیست و پشتیبانی از IParsable، پیشتر در Minimal API دات نت 6 ارائه شده بود؛ اما در دات نت 7 توسط ASP.NET Core MVC نیز قابل استفاده شده‌است.