مطالب
PowerShell 7.x - قسمت چهارم - نوشتن اولین اسکریپت
دستوراتی که درون کنسول مینویسیم، تک خطی یا one-linear هستند؛ هر چند میتوان با زدن کلیدهای Shift + Enter دستورات چندخطی هم نوشت یا حتی با گذاشتن semicolon بعد از هر دستور میتوانیم دریک خط چندین دستور را پشت‌سر هم بنویسیم. اما برای نوشتن دستورات طولانی‌تر بهتر است دستورات را درون فایل‌های جدایی قرار دهیم و از VSCode یا PowerShell ISE (فقط در ویندوز) نیز برای نوشتن اسکریپت‌ها استفاده کرد. اسکریپت‌های PowerShell با پسوند ps1 و psm1 (برای نوشتن ماژول) هستند؛ هر چند چندین پسوند دیگر نیز برای فایل‌های PowerShell وجود دارند که در اینجا میتوانید لیست آنها را مشاهده کنید. درون یک فایل ps1 امکان نوشتن و ترکیب دستورات مختلف را داریم. همچنین میتوانیم از امکانات زبان سی‌شارپ هم استفاده کنیم؛ زیرا PowerShell در واقع اپلیکیشنی است که توسط NET Core. و با زبان #C نوشته شده‌است. در نتیجه میتوانیم بگوئیم زبان اسکریپتی که در PowerShell استفاده میشود، یک DSL برای زبان #C است. در PowerShell همه چیز یک آبجکت محسوب میشود. برای تست این مورد میتوانید درون کنسول PowerShell دستور زیر را وارد کنید:
PS> "" | Get-Member
دستور فوق یک لیست از تمامی توابع و پراپرتی‌های نوع System.String را نمایش خواهد داد:
   TypeName: System.String

Name                 MemberType            Definition
----                 ----------            ----------
Clone                Method                System.Object Clone(), System.Object ICloneable.Clone()
CompareTo            Method                int CompareTo(System.Object value), int CompareTo(strin…
Contains             Method                bool Contains(string value), bool Contains(string value…
CopyTo               Method                void CopyTo(int sourceIndex, char[] destination, int de…
EndsWith             Method                bool EndsWith(string value), bool EndsWith(string value…
EnumerateRunes       Method                System.Text.StringRuneEnumerator EnumerateRunes()
Equals               Method                bool Equals(System.Object obj), bool Equals(string valu…
GetEnumerator        Method                System.CharEnumerator GetEnumerator(), System.Collectio…
GetHashCode          Method                int GetHashCode(), int GetHashCode(System.StringCompari…
GetPinnableReference Method                System.Char&, System.Private.CoreLib, Version=6.0.0.0, …
GetType              Method                type GetType()
GetTypeCode          Method                System.TypeCode GetTypeCode(), System.TypeCode IConvert…
IndexOf              Method                int IndexOf(char value), int IndexOf(char value, int st…
IndexOfAny           Method                int IndexOfAny(char[] anyOf), int IndexOfAny(char[] any…
Insert               Method                string Insert(int startIndex, string value)
IsNormalized         Method                bool IsNormalized(), bool IsNormalized(System.Text.Norm…
LastIndexOf          Method                int LastIndexOf(string value, int startIndex), int Last…
LastIndexOfAny       Method                int LastIndexOfAny(char[] anyOf), int LastIndexOfAny(ch…
Normalize            Method                string Normalize(), string Normalize(System.Text.Normal…
PadLeft              Method                string PadLeft(int totalWidth), string PadLeft(int tota…
PadRight             Method                string PadRight(int totalWidth), string PadRight(int to…
Remove               Method                string Remove(int startIndex, int count), string Remove…
Replace              Method                string Replace(string oldValue, string newValue, bool i…
ReplaceLineEndings   Method                string ReplaceLineEndings(), string ReplaceLineEndings(…
Split                Method                string[] Split(char separator, System.StringSplitOption…
StartsWith           Method                bool StartsWith(string value), bool StartsWith(string v…
Substring            Method                string Substring(int startIndex), string Substring(int …
ToBoolean            Method                bool IConvertible.ToBoolean(System.IFormatProvider prov…
ToByte               Method                byte IConvertible.ToByte(System.IFormatProvider provide…
ToChar               Method                char IConvertible.ToChar(System.IFormatProvider provide…
ToCharArray          Method                char[] ToCharArray(), char[] ToCharArray(int startIndex…
ToDateTime           Method                datetime IConvertible.ToDateTime(System.IFormatProvider…
ToDecimal            Method                decimal IConvertible.ToDecimal(System.IFormatProvider p…
ToDouble             Method                double IConvertible.ToDouble(System.IFormatProvider pro…
ToInt16              Method                short IConvertible.ToInt16(System.IFormatProvider provi…
ToInt32              Method                int IConvertible.ToInt32(System.IFormatProvider provide…
ToInt64              Method                long IConvertible.ToInt64(System.IFormatProvider provid…
ToLower              Method                string ToLower(), string ToLower(cultureinfo culture)
ToLowerInvariant     Method                string ToLowerInvariant()
ToSByte              Method                sbyte IConvertible.ToSByte(System.IFormatProvider provi…
ToSingle             Method                float IConvertible.ToSingle(System.IFormatProvider prov…
ToString             Method                string ToString(), string ToString(System.IFormatProvid…
ToType               Method                System.Object IConvertible.ToType(type conversionType, …
ToUInt16             Method                ushort IConvertible.ToUInt16(System.IFormatProvider pro…
ToUInt32             Method                uint IConvertible.ToUInt32(System.IFormatProvider provi…
ToUInt64             Method                ulong IConvertible.ToUInt64(System.IFormatProvider prov…
ToUpper              Method                string ToUpper(), string ToUpper(cultureinfo culture)
ToUpperInvariant     Method                string ToUpperInvariant()
Trim                 Method                string Trim(), string Trim(char trimChar), string Trim(…
TrimEnd              Method                string TrimEnd(), string TrimEnd(char trimChar), string…
TrimStart            Method                string TrimStart(), string TrimStart(char trimChar), st…
TryCopyTo            Method                bool TryCopyTo(System.Span[char] destination)
Chars                ParameterizedProperty char Chars(int index) {get;}
Length               Property              int Length {get;}
در واقع میتوانیم بگوئیم هرچیزی در PowerShell یک آبجکت NET. است. در ادامه لیستی را از قابلیت‌های PowerShell به عنوان یک زبان اسکریپتی، بررسی خواهیم کرد.

تعریف متغیر
برای تعریف یک متغیر از علامت $ قبل از نام متغیر استفاده میکنیم. نوع متغیر نیز براساس مقداری که به آن انتساب داده میشود، تعیین خواهد شد: 
$stringVariable = "Hello World"
$letter = 'A'
$isEnabled = $false
$age = 33
$height = 76
$doubleVar = 54321.21
$singleVar = 76549.11
$longVar = 2382.22
$dateVar = "July 24, 1986"
$arrayVar = "A", "B", "C"
$hashtableVar = @{ Name = "Sirwan"; Age = 33; }
همچنین میتوانیم نوع متغیر را نیز به صورت صریح تعیین کنیم: 
[string]$stringVariable = "Hello World"
[char]$letter = 'A'
[bool]$isEnabled = $false
[int]$age = 33
[decimal]$height = 76
[double]$doubleVar = 54321.21
[single]$singleVar = 76549.11
[long]$longVar = 2382.22
[DateTime]$dateVar = "July 24, 1986"
[array]$arrayVar = "A", "B", "C"
[hashtable]$hashtableVar = @{ Name = "Sirwan"; Age = 33; }
لازم به ذکر است scope متغیرها در حالت پیش‌فرض به local تنظیم میشود. به این معنا که در جایی که تعریف میشوند، قابل دسترسی خواهند بود. قاعدتاً اگر متغیرها را در ابتدای اسکریپت تعریف کنید، میدان دید آن به صورت سراسری خواهد بود و در هرجایی از کد در دسترس خواهند بود. اما برای اینکه به صورت صریح یک متغیر را به صورت سراسری تعریف کنیم میتوانیم از کلمه‌کلیدی global بعد از تعیین نوع متغیر و قبل از علامت $ استفاده کنیم: 
$global:stringVariable = "Hello World"

// Or

[string]$global:stringVariable = "Hello World"

عبارات شرطی
همانند دیگر زبان‌های اسکریپتی، در PowerShell نیز قابلیت تعریف ساختارهای شرطی وجود دارد: 
$guess = 20
switch ($guess) {
    {$_ -eq 20} { Write-Host "You guessed right!" }
    {$_ -gt 20} { Write-Host "You guessed too high!" }
    {$_ -lt 20} { Write-Host "You guessed too low!" }
    default { Write-Host "You didn't guess a number!" }
}

$guess = 20
if ($guess -eq 20) { 
    Write-Host "You guessed right!" 
}
elseif ($guess -gt 20) { 
    Write-Host "You guessed too high!" 
}
elseif ($guess -lt 20) { 
    Write-Host "You guessed too low!" 
}
else { 
  Write-Host "You didn't guess a number!"
}
همانطور که مشاهده میکنید از یکسری اپراتور برای بررسی شرط‌ها استفاده شده است. در اینجا میتوانید لیست کامل آنها را مشاهده کنید. لازم به ذکر است که از PowerShell 7.0 به بعد نیز Ternary Operator اضافه شده است: 
$message = (Test-Path $path) ? "Path exists" : "Path not found"

حلقه‌ها
همچنین از حالت‌های مختلف loop نیز پشتیبانی میشود: 
[int]$num = 10
for ($i = 1; $i -le $num; $i++) {
    Write-Host "`n"

    for ($j = 1; $j -le $num; $j++) {
        Write-Host -NoNewline -ForegroundColor Green ($i * $j).ToString().PadLeft(6)
    }
}
Write-Host "`n`n"

[int]$counter = 1
while ($counter -le 10) {
    Write-Host "Hello World"
    $counter++
}

do {
    Write-Host "Hello World"
    $counter++
} while ($counter -le 10)

foreach ($i in 1..10) {
    Write-Host "Hello World"
}

$items = "Hello", "World"
$items | ForEach-Object {
    Write-Host $_
}

لازم به ذکر است برای ForEach-Object از % نیز میتوانید استفاده کنید. اما بطور کلی بهتر است تا حد امکان از aliaseها استفاده نکنید؛ زیرا خوانایی کد را مقداری سخت میکند. این مورد توسط خود VSCode هم هشدار داده میشود: 


آرایه‌ها
در PowerShell به صورت پیش‌فرض آرایه‌ها از نوع []System.Object در نظر گرفته میشوند. در اینجا نیز آرایه‌ها immutable هستند. چندین روش برای ایجاد آرایه وجود دارد. در ادامه یک آرایه خالی را تعریف کرده‌ایم: 
$array = @()
یک روش دیگر تعریف آرایه اینگونه است: 
$myArray = [Object[]]::new(10)
$byteArray = [Byte[]]::new(100)
$ipAddresses = [IPAddress[]]::new(5)

$mylist = [System.Collections.Generic.List[int]]::new()
از PowerShell 5.0 به بعد میتوانیم سینتکس namespaceها را با کمک using خلاصه‌تر بنویسیم:
using namespace System.Collections.Generic
$mylist = [List[int]]::new()
از range operator هم میتوانیم برای مقداردهی یک آرایه استفاده کرد: 
$numbers = 1..10
$alphabet = "a".."z"

foreach ($item in 1..10) {
    Write-Host "Hello World"
}
برای دسترسی به عناصر یک آرایه نیز میتوانیم از range operator استفاده کنیم: 
$numbers = 1..10

$numbers
Write-Output "".PadRight(10, "-")
$numbers[1..2 + 2..4]
همانطور که مشاهده میکنید از اپراتور + برای append کردن یک بازه از اعداد، به آرایه استفاده کرده‌ایم. دقت داشته باشید که با هر بار اضافه/حذف آیتم‌ها، یک آرایه جدید ساخته میشود. این مورد در آرایه‌هایی با سایز بزرگ میتواند مشکل کارآیی ایجاد کند. بنابراین اگر عملیات اضافه کردن و حذف کردن از یک آرایه را زیاد انجام میدهید، بهتر است از ArrayList استفاده کنید. تفاوت آن نیز این است که برخلاف آرایه‌های عادی، سایز ArrayList ثابت نیست. تعریف ArrayList نیز اینگونه است: 
$colours = [System.Collections.ArrayList]@("Red", "Green", "Blue")
$colours.Add("Yellow")
$colours.Remove("Red")
از دیگر کالکشن‌های NET. نیز میتوانید استفاده کنید؛ به عنوان مثال در مثال زیر از List برای اضافه کردن ده هزار آیتم استفاده کرده‌ایم:
using namespace System.Collections.Generic

$mylist = [List[int]]::new()
Measure-Command { 1..100000 | ForEach-Object { $mylist.Add($_) } }
Measure-Command { $mylist.Where({ $_ -gt 10000 }) }
Measure-Command { $mylist.Contains(10000) }
نکته: کد فوق در حالت استفاده از ArrayList مقداری کندتر است. دلیل آن نیز این است که ArrayList امکان اضافه کردن هر آبجکتی را به ما میدهد. در نتیجه موقع جستجو باید یکبار عملیات unboxing را برای تشخیص نوع درخواست شده انجام دهد.

Hashtable
ساختار دیگری که میتوانید استفاده کنید Hash Tableها هستند؛ از این ساختار برای ایجاد custom objects و همچنین پاس دادن پارامترها به یک command استفاده میشود:
$sensors = @{
    tempreture = "Temperature"
    humidity   = "Humidity"
    pressure   = "Pressure"
    light      = "Light"
    noise      = "Noise"
    co2        = "CO2"
    battery    = "Battery"
    min_temp   = "Min Temp"
    max_temp   = "Max Temp"
}
با دستور زیر میتوانید لیستی از Commandهایی که ورودی‌شان از نوع Hash table است را مشاهده کنید:
PS> Get-Command -ParameterType Hashtable


CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Function        TabExpansion2                                                 
Cmdlet          Add-Member                                         7.0.0.0    Microsoft.Powe…
Cmdlet          ConvertTo-Html                                     7.0.0.0    Microsoft.Powe…
Cmdlet          Get-Job                                            7.2.7.500  Microsoft.Powe…
Cmdlet          Invoke-Command                                     7.2.7.500  Microsoft.Powe…
Cmdlet          Invoke-RestMethod                                  7.0.0.0    Microsoft.Powe…
Cmdlet          Invoke-WebRequest                                  7.0.0.0    Microsoft.Powe…
Cmdlet          New-Object                                         7.0.0.0    Microsoft.Powe…
Cmdlet          New-PSRoleCapabilityFile                           7.2.7.500  Microsoft.Powe…
Cmdlet          New-PSSession                                      7.2.7.500  Microsoft.Powe…
Cmdlet          Remove-Job                                         7.2.7.500  Microsoft.Powe…
Cmdlet          Select-Xml                                         7.0.0.0    Microsoft.Powe…
Cmdlet          Set-PSReadLineOption                               2.1.0      PSReadLine
Cmdlet          Stop-Job                                           7.2.7.500  Microsoft.Powe…
Cmdlet          Wait-Job                                           7.2.7.500  Microsoft.Powe…

توابع
برای ایجاد توابع در PowerShell میتوانید از سینتکس زیر استفاده کنید:
function Write-HelloWorld {
    Write-Host "Hello World"
}
برای نامگذاری توابع بهتر است از قالب verb-noun استفاده کنید. برای قسمت verb نیز بهتر است از یکسری افعال تائیدشده (approved verbs) که مستندات مایکروسافت پیشنهاد میدهد استفاده کنید (+) در این زمینه VSCode در صورت انتخاب یک نام نامناسب به شما هشدار خواهد:

تفاوت بین Function و Cmdlet چیست؟
توابع و cmdletها عملاً از لحاظ کاربرد باهم تفاوتی ندارند. تنها تفاوت آنها در نحوه ساخت‌شان است. cmdletها با #C ساخته میشوند. به این معنا که یکسری DLL کامپایل شده هستند که در نهایت از آنها استفاده خواهیم کرد. اما توابع در PowerShell با کمک سینتکسی که اشاره شد ایجاد میشوند. توسط دستور زیر میتوانیم لیستی از توابعی را که درون سشن PowerShellمان بارگذاری شده‌اند، ببینیم: 
PS> Get-Command -CommandType Function

در PowerShell نیز توابع قابلیت دریافت ورودی را نیز دارند. در ساده‌ترین حالت میتوانیم از آرایه args$ استفاده کنیم؛ دقیقاً چیزی مشابه arguments در JavaScript است:
function Write-HelloWorld {
    Write-Host "First Argument: $($args[0])"
    Write-Host "Second Argument: $($args[1])"
    Write-Host "Third Argument: $($args[2])"
    Write-Host "Fourth Argument: $($args[3])"
}
به این حالت Positional Parameters گفته میشود. یک روش دیگر تعریف پارامتر استفاده از Named Parameters است:
function Write-HelloWorld(
    [string]$first,
    [string]$second,
    [string]$third,
    [string]$fourth
) {
    Write-Host "First Argument: $($first)"
    Write-Host "Second Argument: $($second)"
    Write-Host "Third Argument: $($third)"
    Write-Host "Fourth Argument: $($fourth)"
}

در قسمت بعد در مورد Advanced Functionها صحبت خواهیم کرد و اجزای دیگر توابع را بیشتر توضیح خواهیم داد.

یک نکته در مورد خروجی دستورات درون کنسول
در ویندوز میتوانیم خروجی کنسول را به Out-GridView پایپ کنیم که در واقع یک GUI برای نمایش دادن خروجی کنسول است. این کامند فقط در ویندوز قابل استفاده است:

یک نسخه cross-platform آن نیز که مناسب کنسول است تهیه شده که میتوانید از آن استفاده کنید: 
Get-Command -ParameterType Hashtable | Out-ConsoleGridview
با این خروجی:

البته به صورت پیش‌فرض نصب نیست و باید از طریق PowerShell Gallery آن را نصب کنید:

Install-Module -Name Microsoft.PowerShell.ConsoleGuiTools


مطالب
اعمال متداول با select (یا همان DropDownList / ComboBox) توسط jQuery

در این مقاله مروری خواهیم داشت بر نحوه‌ی انجام یک سری از اعمال متداول با استفاده از jQuery بر روی select استاندارد HTML. قبل از شروع به عنوان قالب کار، صفحه ساده‌ی زیر را در نظر بگیرید که از یک DropDownList استاندارد ASP.Net تشکیل شده است:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="select.aspx.cs" Inherits="TestJQueryAjax.select" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script src="js/jquery.js" type="text/javascript"></script>

<script type="text/javascript">
//...
</script>

</head>
<body>
<form id="form1" runat="server">
<asp:DropDownList ID="ddlTest" runat="server">
<asp:ListItem Value="1">آیتم یک</asp:ListItem>
<asp:ListItem Value="2">آیتم دو</asp:ListItem>
<asp:ListItem Value="3">آیتم سه</asp:ListItem>
<asp:ListItem Value="4">آیتم چهار</asp:ListItem>
<asp:ListItem Value="5">آیتم پنج</asp:ListItem>
</asp:DropDownList>
</form>
</body>
</html>
1) بدست آوردن value آیتم انتخاب شده

$("#<%=ddlTest.ClientID %>").val()
2) بدست آوردن text آیتم انتخاب شده

$("#<%=ddlTest.ClientID %> option:selected").text()
3) عکس العمل نشان دادن به انتخاب آیتم‌های مختلف

<script type="text/javascript">
$(document).ready(function() {
$("#<%=ddlTest.ClientID %>").change(function() {
alert($("#<%=ddlTest.ClientID %>").val());
});
});
</script>
4) انتخاب یک آیتم از طریق برنامه نویسی

$("#<%=ddlTest.ClientID %>").val(2);
5) افزودن یک آیتم به لیست از طریق برنامه نویسی

$("<option value='6'>آیتم شش</option>").appendTo("#<%=ddlTest.ClientID %>");

6) خالی و حذف کردن تمامی آیتم‌ها

$("#<%=ddlTest.ClientID %>").html("");

و برای تکمیل بحث می‌توان به این برگه مرجع مراجعه کرد:
jQuery – Select element cheat sheet


اشتراک‌ها
معرفی جی‌سان، JSON

جی‌سان یا JSON (JavaScript Object Notation) یک فرمت تبادل داده سبک (lightweight) می‌باشد که خواندن و نوشتن آن برای انسان و تولید و پارس کردن آن برای ماشین‌ها آسان است. این زبان بر مبنای یک زیر مجموعه از زبان برنامه‌نویسی JavaScript ، استاندارد ECMA-262 ویرایش سوم - دسامبر 1999‌، است....

معرفی جی‌سان، JSON
نظرات مطالب
lambda expression در Vb.net
یعنی واقعاً به نظر شما VB نسبت به C# به زبان محاوره نزدیک تره؟!
خواهشاً همین کدی که بالا نوشته شده رو بخونید ببینید آیا به زبان محاوره نزدیک هست یا نه؟
یکی از سوالایی که همیشه تو ذهن من وجود داشته این بوده که چرا برای آموزش از VB استفاده میکنن؟!
مطالب
تکنیک‌های ایجاد سایت‌های چند زبانه
امروزه چند زبانه بودن سایت‌ها، از اهمیت بالایی برخوردار شده است و هر سایتی که نیاز داشته باشد در سایر نقاط جهان شناخته شود و کاربران مناطق مختلف، به راحتی از آن استفاده کنند، سایت‌های خود را بر پایه‌ی چندین زبان ایجاد می‌کنند. در این نوشتار سعی داریم بر این موضوع بررسی اجمالی داشته باشیم و نکات زیر را بررسی نماییم.

  • طراحی دیتابیس یا بانک اطلاعاتی بر پایه چند زبانه بودن و بررسی سناریوهای مختلف.
  • نکاتی که باید در ساخت سایت‌های چند زبانه به آن‌ها دقت کرد.
  • شیوه‌ی تشخیص و تغییر زبان سایت
  • معرفی چند کامپوننت وب، برای مباحث چند زبانه

طراحی مدل دیتابیس

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

مزایا:

  1. پیاده سازی آسان

معایب:

  1. در این روش با زیاد شدن هر زبان، تعداد ستون‌ها افزایش می‌یابد که باعث می‌شود طراحی مناسبی نداشته باشد.
  2. در ضمن این مورد باید توسط برنامه نویس مرتبا اضافه گردد یا اینکه برنامه نویس این امکان را در سیستم قرار دهد که مدیر سایت بتواند در پشت صحنه کوئری افزودن ستون را ایجاد کند که باید جدول مرتبا مورد alter گرفتن قرار بگیرد.
  3. ممکن است همیشه برای هر زبانی مطلبی قرار نگیرد و این مورد باعث می‌شود بی جهت فضایی برای آن در نظر گرفته شود.

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

پی نوشت دوم : در صورتی که فیلد شما مانند متن مقاله که عموما از نوع داده (varchar(max است استفاده می‌کنید و در صورتی که زبان مورد استفاده قرار نگیرد در خیلی از اوقات بی جهت فیلد‌های Blob ساخته اید که بهینه سازی آن را نیز باید در نظر بگیرید.


در مرحله‌ی بعد برای رفع مشکلات بالا یک جدول از زبان‌ها، مانند جدول زیر را ایجاد می‌کنیم:
 
 ID  کد
 Language  زبان
 ISO  کد دو رقمی آن زبان
 Flag  پرچم آن کشور
بعد از آن هر مقاله برای یک زبان ایجاد خواهد شد؛ چیزی مانند تصویر زیر:

مزایا:

  1. پیاده سازی آسان

معایب:

  1. ایجاد رکوردهای تکراری، هر مقاله برای بعضی از اطلاعاتش که چند زبانه نیستند داده‌های تکراری خواهد داشت.
  2. هر مقاله یک مقاله‌ی جدا شناخته می‌شود و ارتباطی میان آنان نخواهد بود. بدین ترتیب توانایی ایجاد گزارش‌هایی چون هر گروه از مقاله و دسته بندی آن‌ها از بین خواهد رفت. در ضمن مدیر عموما در یک سیستم مدیریتی می‌خواهد تنها یک لینک را به یک مقاله بدهد و سایت بنا به تشخیص در زبان مزبور، یکی از این مقالات را به کاربر نمایش دهد؛ نه اینکه مرتبا مدیر برای هر زبان، لینکی را مهیا کند و در این حالت چنین چیزی ممکن نخواهد بود.
  3. در یک سیستم فروشگاهی همانند تصویر بالا کار هم سخت‌تر می‌شود و هر رکورد، یک محصول جدا شناخته می‌شود و ویرایش‌ها هم برای هر کدام باید جداگانه صورت بگیرد که در عمل این طرح را رد می‌کند.


سومین راه حل این است که سه جدول ایجاد کنیم:

یک. جدول زبان‌ها (که بالاتر ایجاد شده بود)

دو . جدول نام مقاله به همراه اطلاعات پایه و فیلدها بی نیاز به چند زبانه بودن

سه : یک جدول که هر دو ستون آن کدهای کلید دو جدول بالا را دارند و فیلدهای چند زبانه در آن وجود دارند.

جدول پایه

 ID  کد
 Name  نام مقاله
 CreationDate  تاریخ ایجاد
 Writer  نویسنده
 Visibilty  وضعیت نمایش
 جدول مقالات
 LanguageCode کد زبان
 ArticleID  کد مقاله
 CreationDate  تاریخ ایجاد
 Visibility  وضعیت نمایش مقاله
Title
عنوان مقاله
ContentText
متن مقاله

 در جدول پایه یک مقاله ایجاد می‌شود که اطلاعات عمومی همه مقالات را دارد و حتی خصوصیت وضعیت نمایش آن، روی همه‌ی مقالات با هر زبانی تاثیر می‌گذارد. در جدول دو، هر مقاله یک رکورد دارد که کد زبان و کد مقاله برای آن یک کلید ترکیبی به حساب می‌آیند. پس، از هر مقاله یک یا چند زبان خواهیم داشت. همچنین دارای فیلدهایی با وضعیت مخصوص به خود هم هستند؛ مثل فیلد وضعیت نمایش مقاله که فقط برای این مقاله با این زبان کاربرد دارد.

مزایا:

  1. گزارش گیری آسان برای هر دسته مقاله با زبان‌های مختلف و ارتباط و یکپارچگی
  2. آسان در افزودن زبان.

معایب:

  1. ایجاد کوئری‌های پیچیده‌تر و جوین دار که به نسبت روش‌های قبلی کوئری‌ها پیچیده‌تر شده اند.
  2. کدنویسی زیادتر.

استفاده از ساختارهای XML یا JSON برای ذخیره سازی اطلاعات چند زبانه مانند ساختارهای زیر:

XML
<Articles>
<Article>
this is english text
</Article>
<Article>
این یک متن فارسی است
</Article>
</Articles>

یا 
<Articles>
<en-us>
this is english text
</en-us>
<fa-ir>
این یک متن فارسی است
</fa-ir>
</Articles>
JSON
"Articles":["en-us':{"title":"this is english text","content":" english content"},"fa-ir":{"title":"متن فارسی","content":"محتوای فارسی"}]
ازSQL Server 2005 به بعد از نوع داده xml پشتیبانی می‌شود و در نسخه‌ی 2016 آن نیز پشتیبانی از Json اضافه شده است که حتی شامل اندیکس‌های اختصاصی هم برای این دو نوع می‌باشد.
از مزایای این روش ذخیره‌ی همه داده‌ها در یک ستون و یک جدول است و نیازی به ستون‌های اضافه یا جداول اضافه نیست ولی معایب این روش استفاده از کوئری‌های پیچیده‌تر جهت ارتباط و خواندن است.

استفاده از بانک‌های اطلاعاتی NO SQL
در این بانک‌ها دیگر درگیر تعداد ستون‌ها و جنس آن‌ها نیستیم و میتوانیم برای هر مقاله یا محصول، هر تعداد زبان و یا فیلد را که می‌خواهیم، در نظر بگیریم و اضافه کنیم. برای آشنایی بیشتر با این نوع بانک‌ها و انواع آن، مقالات مربوط به nosql را در سایت دنبال کنید.

نکاتی که در یک سایت چند زبانه باید به آن‌ها توجه کرد.

یک . زبان آن صفحه را معرفی کنید: این کار هم به موتورهای جست و جو برای ثبت سایت شما کمک می‌کند و هم برای معلولین که از ابزارهای صفحه خوان استفاده می‌کنند، کمک بزرگی است. در این روش، صفحه خوان‌ها و دستگاه‌های خط بریل که زبان صفحه را تشخیص نمی‌دهند با خواندن کد زبان می‌توانند زبان صفحه را تشخیص دهند. با استفاده از خط زیر میتوانید زبان اصلی صفحه‌ی خود را تنظیم نمایید:
<html lang="en">

اگر از XHTML استفاده می‌کنید خاصیت زیر را فراموش نکنید. دریافت W3C Validation بدون آن امکان پذیر نخواهد بود.
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
با تغییر زبان هر صفحه، باید تنظیم زبان آن تغییر یابد:



دو. چند زبانه بودن صفحه: در بالا یاد گرفتیم که چگونه زبان اصلی صفحه را تنظیم کنیم، ولی گاهی اوقات صفحه به غیر از زبان اصلی، شامل زبان‌های دیگر هم می‌شود؛ مثل نقل قول‌ها یا موارد دیگر. برای این‌کار می‌توانید از خصوصیت lang که در اکثر تگ‌ها پشتیبانی می‌شود، استفاده کنید. مثال پایین یک نقل قول فرانسوی است که ما آن را به خصوصیت lang، جهت تایید زبانش مزین کرده‌ایم:
<blockquote lang=”fr”>

<p>Le plus grand faible des hommes, c'est l'amour qu'ils ont de la vie.</p>

</blockquote>

سه. لینک ها : اگر دارید در صفحه‌ای لینک به جایی می‌دهید که متفاوت از زبان شماست، حتما باید زبان صفحه یا سایت مقصد را مشخص کنید. مثلا لینک زیر برای صفحه‌ای است که از یک زبان غیر فرانسوی به یک صفحه‌ی با زبان فرانسوی هدایت می‌شود:
<a href="" hreflang="fr">French</a>

همچنین اگر متن لینک شما هم به زبان فرانسوی باشد خیلی خوب می‌شود که آن را هم بیان کنید و از خاصیت lang و هم hreflang همزمان استفاده کنید:
<a href="" hreflang="fr">Francais</a>

چهار. جهت صفحه: به طور پیش فرض شما این مورد را به خاطر طراحی ظاهر صفحه رعایت می‌کنید ولی خالی از لطف نیست که بگوییم جهت زبان را هم با خصوصیت dir  که مقدار پیش فرضش راست به چپ هست، ذکر کنید. هر چند که عموما وب سایت‌های چند زبانه شامل یک قالب دیگر به صورت راست چین یا چپ چین هم هستند که بر اساس زبان انتخاب شده، قالب خود را تغییر خواهند داد.

پنج. انکودینگ صفحه را مشخص کنید: برای اینکه نحوه‌ی رمزگذاری و رمزگشایی حروف و نمادها مشخص گردد، باید انکودینگ تنظیم شود و حتی برای بعضی از موتورهای جست و جو که ممکن است با وب سایت شما به مشکل بر بخورند. امروزه بیشتر از صفحات یونیکد استفاده می‌شود که سطح وسیعی از کاراکترها را پشتیبانی می‌کند.
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">

HTML5
<meta charset="UTF-8">

شش. اندازه‌ی فونت:
موقعی که یک سایت چند زبانه را طراحی می‌کنید این نکته خیلی مهم هست که بدانید اندازه فونت‌های زبان پیش فرض، برای باقی زبان‌ها مناسب نیستند. به عنوان مثال ممکن است اندازه فونتی برای زبان‌های انگلیسی، فرانسوی و آلمانی مناسب باشد ولی برای زبان‌های فارسی و عربی و چینی و ... مناسب نباشد و خواندن آن سخت شود. به همین جهت یکی از راه‌های حل این مشکل استفاده از قالب css است که وابسته به خصوصیت lang ای است که شما برای صفحه و هر المان یا تگی که از این خصوصیت استفاده می‌کند، تعیین کرده‌اید.
:lang(en) {

font-size: 85%;

font-family: arial, verdana, sans-serif;

}

:lang(zh) {

font-size: 125%;

font-family: helvetica, verdana, sans-serif;

}

خط زیر تعیین میکند که از استایل اول استفاده شود:
<html lang="en">
و خط زیر تعیین می‌کند که از استایل دوم استفاده شود:
<html lang="zh">
البته این کد بالا در مرورگرهای فایرفاکس، اپرا و IE8 به بالا پاسخ می‌دهد. برای سایر مروگرها چون کروم و نسخه‌های پیشین IE باید از شیوه‌ی زیر بهره ببرید:
<body class="english"> or <body class="chinese">

و استایل:
.english {

font-size: 85%;

font-family: arial, verdana, sans-serif;

}

.chinese {

font-size: 125%;

font-family: helvetica, verdana, sans-serif;

}
در این شیوه برای تگ مربوطه یک کلاس با نام آن زبان ایجاد کرده که محتوای آن تنظیمات قلم آن زبان می‌باشد.

هفت. اندازه‌ی کلمات و خطوط: در این حالت اندازه کلمات بین هر زبان متفاوت است و میزان فضایی که در بر می‌گیرند با زبانی دیگر تفاوت دارد. ممکن است کلمه‌ای در یک زبان شش حرف باشد، ولی در زبانی دیگر دو یا چهار حرف و به خصوص در یک عبارت یا حتی جمله این نکته بیشتر به چشم می‌آید. تصاویر زیر نمونه‌ای از حل این مشکل توسط سایت آمازون می‌باشند که بعضی المان‌ها مانند لیست علاقمندی‌ها یا بعضی از آیتم‌ها را جابجا کرده است. بهترین جواب برای اینکار این است که قالب خود را برای هر زبان بهینه کنید.

هشت : زمان را نیز تغییر دهید: یکی از مواردی که در کمتر سایت چند زبانه‌ای به چشم می‌خورد و به نظر بنده می‌تواند بسیار مهم باشد این است که time zone منطقه‌ی هر زبان را بدانید. به عنوان مثال برای مقاله‌ی خود، تاریخ ایجاد را به صورت UTC ذخیره کنید و سپس نمایش را بر اساس زبان یا حتی بهتر و دقیق‌تر از طریق IP کشور مربوطه به دست آورید. برای کاربران ثبت نام شده این تاریخ می‌تواند دقیق‌تر باشد همانند انجمن‌های وی بولتین.


شیوه‌های تشخیص زبان سایت

یکی از راه‌های تشخیص زبان این است که موقعی که برای اولین بار کاربری به سایت مراجعه می‌کند، زبان مورد نظرش را سوال کنید و این اطلاعات را در یک کوکی بدون تاریخ انقضاء ذخیره کنید تا در دفعات بعدی آن را بررسی نمایید.

دومین راه، استفاده از IP کاربر مراجعه کننده است تا بر اساس آن زبان مورد نظر را انتخاب کنید.

در سومین شیوه که اغلب استفاده می‌شود، زبان سایت به طور پیش فرض بر روی یک زبان خاص که بهتر است انگلیسی باشد تنظیم شده است و سپس کاربر از طریق یک منو یا ابزارهای موجود در سایت، زبان سایت را تغییر دهد.

پی نوشت: فراموش نگردد که امکان تغییر زبان همیشه برای کاربر مهیا باشد و طوری نباشد که کاربر در آینده نتواند زبان سایت را تغییر دهد؛ حتی اگر تشخیص خودکار سایت برای زبان فعال باشد.

پی نوشت: در روش‌های بالا بهتر است همان مرتبه‌ی اول اطلاعات را در یک کوکی ذخیره کنید تا مراحل پیگیری راحت‌تر و آسان‌تر شود.


پلاگین‌ها و ابزارهای مدیریت زبان

اولین ابزاری که به شما برای تغییر زبان سایت معرفی می‌کنیم، توسط کاربر مورد استفاده قرار می‌گیرد و یک پلاگین جی کوئری می‌باشد. این پلاگین شامل حالت‌های مختلفی است که به نظر من بهترین حالت آن است که پرچم کشور مربوطه نیز نمایش یابد تا کاربر ارتباط راحت‌تری با سایت داشته باشد.

دومین ابزار که بیشتر برای انتخاب کشور می‌باشد و من خودم در بخش مدیریتی سیستم‌ها از آن استفاده میکنم، ابزار CountrySelector است. این پلاگین قابلیت جست و جو زبان را همزمان با تایپ کاربر نیر داراست. اسامی کشورها به صورت انگلیسی شروع شده و به زبان آن کشور در داخل پرانتز خاتمه می‌یابند و پرچم هر کشور نیز در کنار آن قرار دارد. کار کردن با آن بسیار راحت بوده و مستنداتش به طور کامل کار با آن را توضیح می‌دهد.

پلاگین بعدی International Telephone Input است که  پیاده سازی پلاگین بالا می‌باشد. برای مواردی مفید است که شما نیاز دارید کد تلفنی کشوری را انتخاب کنید.

در مقاله‌های زیر که در سایت جاری است در مورد Globalization و به خصوص استفاده از ریسورس‌ها مطالب خوبی بیان شده است:

قسمت بیست و دوم آموزش MVC که مبحث Globalization را دنبال می‌کند.

قسمت اول از شش قسمت مباحث Globalization  که دنباله‌ی آن را می‌توانید در مقاله‌ی خودش دنبال کنید.

مطالب
خواندنی‌های 29 تیر
نظرات مطالب
کاهش تعداد بار تعریف using ها در C# 10.0 و NET 6.0.
یک نکته‌ی تکمیلی: ترفندی برای معرفی Stubs توسط ویژگی global using statements در unit tests

برای نمونه متد زیر را درنظر بگیرید:
public static string CurrentInvariantMonthName()
    {
        var month = DateTimeOffset.UtcNow.Month;
        return CultureInfo
            .InvariantCulture
            .DateTimeFormat
            .GetMonthName(month);
    }
در کل نوشتن آزمون واحد برای متدهایی که با زمان و خواصی مانند UtcNow و یا Now، کار می‌کنند، مشکل است. در آزمون‌های واحد نیاز داریم تا یک خروجی مشخص را با مقداری از پیش معلوم، مقایسه کنیم تا اطمینان حاصل شود که عملیات صورت گرفته، صحیح است. اما UtcNow هر بار متغیر است.
برای حل این مشکل، با استفاده از ویژگی global using statements و compiler directives، می‌توان دو مفهوم متفاوت را برای زمان ارائه داد:
#if MOCK_TIME
global using DateTimeOffset = StubDateTimeOffset; 
#else
global using DateTimeOffset = System.DateTimeOffset;
#endif

public static class StubDateTimeOffset
{
    private static System.DateTimeOffset? value;
    
    public static System.DateTimeOffset UtcNow 
        => value ?? System.DateTimeOffset.UtcNow ;
    
    public static void Set(System.DateTimeOffset dateTimeOffset) {
        value = dateTimeOffset;
    }

    public static void Reset() => value = null;
}
در اینجا یک فایل خالی cs. ایجاد شده و قطعه کد فوق در آن درج می‌شود. در این حالت اگر توسط تنظیمات کامپایلر در فایل csproj برنامه، MOCK_TIME فعال شود، مفهوم DateTimeOffset، دیگر همان System.DateTimeOffset استاندارد نخواهد بود؛ بلکه از یک کلاس بدلی به نام StubDateTimeOffset تامین می‌شود. برای فعالسازی MOCK_TIME هم می‌توان به صورت زیر عمل کرد که این تعریف، در فایل csproj پروژه‌ی آزمایشی قرار می‌گیرد:
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
   <DefineConstants>MOCK_TIME</DefineConstants>
</PropertyGroup>
پس از این تغییر، اینبار کلاس StubDateTimeOffset، تامین کننده‌ی DateTimeOffset در آزمون‌های واحد خواهد بود و در این آزمون‌ها می‌توان با مقدار دهی DateTimeOffset به صورت زیر، هربار آزمایشات را بر اساس یک UtcNow «مشخص» انجام داد:
// set time
StubDateTimeOffset.Set(new(new(2022, 7, 1)));
مطالب
آشنایی با CLR: قسمت دوم
متادیتاهای یک ماژول مدیریت شده Managed Module 

در قسمت قبلی به اصل وجودی CLR پرداختیم. در این قسمت تا حدودی به بررسی ماژول مدیریت شده managed module که از زبان‌های دیگر، کامپایل شده و به زبان میانی تبدیل گشته است صحبت می‌کنیم.

یک ماژول مدیریت شده شامل بخش‌های زیر است:
 نام بخش
توضیح
 هدر PE32 یا PE32+
CLR باید بداند که برنامه‌ی نوشته شده قرار است روی چه پلتفرمی و با چه معماری، اجرا گردد. این برنامه یک برنامه‌ی 32 بیتی است یا 64 بیتی. همچنین این هدر اشاره می‌کند که نوع فایل از چه نوعی است؛ GUI,CUI یا DLL. به علاوه تاریخ ایجاد یا کامپایل فایل هم در آن ذکر شده است. در صورتیکه این فایل شامل کدهای بومی native CPU هم باشد، اطلاعاتی در مورد این نوع کدها نیز در این هدر ذکر می‌شود و اگر ماژول ارائه شده تنها شامل کد IL باشد، قسمت بزرگی از اطلاعات این هدر در نظر گرفته نمی‌شود.
 CLR Header
اطلاعاتی را در مورد CLR ارائه می‌کند. اینکه برای اجرا به چه ورژنی از CLR نیاز دارد. منابع مورد استفاده. آدرس و اندازه جداول و فایل‌های متادیتا و جزئیات دیگر.
 metadata  هر کد یا ماژول مدیریت شده‌ای، شامل جداول متادیتا است که این جداول بر دو نوع هستند. اول جداولی که نوع‌ها و اعضای تعریف شده در کد را توصیف می‌کنند و دومی جداولی که نوع‌ها و اعضایی را که در کد به آن ارجاع شده است، توصیف می‌کنند.
IL Code
اینجا محل قرار گیری کدهای میانی تبدیل شده است که در زمان اجرا، CLR آن‌ها را به کدهای بومی تبدیل می‌کند.


کامپایلرهایی که بر اساس CLR کار می‌کنند، وظیفه دارند جداول متادیتاها را به طور کامل ساخته و داخل فایل نهایی embed کنند. متادیتاها مجموعه‌ی کاملی از فناوری‌های قدیمی چون فایل‌های COM یا Component Object Model و همچنین IDL یا Interface Definition (Description) Language هستند. گفتیم که متادیتا‌ها همیشه داخل فایل IL که ممکن است DLL باشد یا EXE، ترکیب یا Embed شد‌ه‌اند و جدایی آن‌ها غیر ممکن است. در واقع کامپایلر در یک زمان، هم کد IL و هم متادیتاها را تولید کرده و آن‌ها را به صورت یک نتیجه‌ی واحد در می‌آورد.

متادیتاها استفاده‌های زیادی دارند که در زیر به تعدادی از آنان اشاره می‌کنیم:

  • موقع کامپایل نیاز به هدرهای C و ++C از بین می‌رود؛ چرا که فایل نهایی شامل تمامی اطلاعات ارجاع شده می‌باشد. کامپایلرها می‌توانند مستقیما اطلاعات را از داخل متادیتاها بخوانند.
  • ویژوال استودیو از آن‌ها برای کدنویسی راحت‌تر بهره می‌گیرد. با استفاده از قابلیت IntelliSense، متادیتا‌ها به شما خواهند گفت چه متدهایی، چه پراپرتی‌هایی، چه رویدادهایی و ... در دسترس شماست و هر متد انتظار چه پارامترهایی را از شما دارد.
  • CLR Code Verification از متادیتا برای اینکه اطمینان کسب کند که کدها تنها عملیات type Safe را انجام می‌دهند، استفاده می‌کند.
  • متادیتاها به فیلد یک شیء اجازه می‌دهند که خود را به داخل بلوک‌های حافظ انتقال داده و بعد از ارسال به یک ماشین دیگر، همان شیء را با همان وضعیت، ایجاد نماید.
  • متادیتاها به GC اجازه می‌دهند که طول عمر یک شیء را رصد کند. GC برای هر شیء موجود می‌تواند نوع هر شیء را تشخیص داده و از طریق متادیتاها می‌تواند تشخیص دهد که فیلدهای یک شیء به اشیاء دیگری هم متصل هستند.

در آینده بیشتر در مورد متادیتاها صحبت خواهیم کرد. 

نظرات مطالب
پیاده سازی UnitOfWork به وسیله MEF
من کلاسهام به این شکله:
کلاس کانتکس‌های من
 public class VegaContext : DbContext, IUnitOfWork, IDbContext
    {
#region Constructors (2) 

        /// <summary>
        /// Initializes the <see cref="VegaContext" /> class.
        /// </summary>
        static VegaContext()
        {
            Database.SetInitializer<VegaContext>(null);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="VegaContext" /> class.
        /// </summary>
        public VegaContext() : base("LocalSqlServer") { }

#endregion Constructors 

#region Properties (2) 

        /// <summary>
        /// Gets or sets the languages.
        /// </summary>
        /// <value>
        /// The languages.
        /// </value>
        public DbSet<Language> Languages { get; set; }

        /// <summary>
        /// Gets or sets the resources.
        /// </summary>
        /// <value>
        /// The resources.
        /// </value>
        public DbSet<Resource> Resources { get; set; }

#endregion Properties 

#region Methods (2) 

// Public Methods (1) 

        /// <summary>
        /// Setups the specified model builder.
        /// </summary>
        /// <param name="modelBuilder">The model builder.</param>
        public void Setup(DbModelBuilder modelBuilder)
        {
            //todo
            modelBuilder.Configurations.Add(new ResourceMap());
            modelBuilder.Configurations.Add(new LanguageMap());
            modelBuilder.Entity<Resource>().ToTable("Vega_Languages_Resources");
            modelBuilder.Entity<Language>().ToTable("Vega_Languages_Languages");
            //base.OnModelCreating(modelBuilder);
        }
// Protected Methods (1) 

        /// <summary>
        /// This method is called when the model for a derived context has been initialized, but
        /// before the model has been locked down and used to initialize the context.  The default
        /// implementation of this method does nothing, but it can be overridden in a derived class
        /// such that the model can be further configured before it is locked down.
        /// </summary>
        /// <param name="modelBuilder">The builder that defines the model for the context being created.</param>
        /// <remarks>
        /// Typically, this method is called only once when the first instance of a derived context
        /// is created.  The model for that context is then cached and is for all further instances of
        /// the context in the app domain.  This caching can be disabled by setting the ModelCaching
        /// property on the given ModelBuidler, but note that this can seriously degrade performance.
        /// More control over caching is provided through use of the DbModelBuilder and DbContextFactory
        /// classes directly.
        /// </remarks>
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new ResourceMap());
            modelBuilder.Configurations.Add(new LanguageMap());
            modelBuilder.Entity<Resource>().ToTable("Vega_Languages_Resources");
            modelBuilder.Entity<Language>().ToTable("Vega_Languages_Languages");
            base.OnModelCreating(modelBuilder);
        }

#endregion Methods 

        #region IUnitOfWork Members
        /// <summary>
        /// Sets this instance.
        /// </summary>
        /// <typeparam name="TEntity">The type of the entity.</typeparam>
        /// <returns></returns>
        public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
        {
            return base.Set<TEntity>();
        }
        #endregion
    }
در تعاریف کلاسهایی که از IDBContext ارث می‌برن اکسپورت شدن (این یک نمونه از کلاس‌های منه)
در طرف دیگر برای لود کردن کلاس زیر نوشتم
public class LoadContexts
    {
        public LoadContexts()
        {
            var directoryPath = HttpRuntime.BinDirectory;//AppDomain.CurrentDomain.BaseDirectory; //"Dll folder path";

            var directoryCatalog = new DirectoryCatalog(directoryPath, "*.dll");

            var aggregateCatalog = new AggregateCatalog();
            aggregateCatalog.Catalogs.Add(directoryCatalog);

            var container = new CompositionContainer(aggregateCatalog);
            container.ComposeParts(this);
        }

        //[Import]
        //public IPlugin Plugin { get; set; }

        [ImportMany]
        public IEnumerable<IDbContext> Contexts { get; set; }
    }
و در کانتکس اصلی برنامه این پلاگین هارو لود می‌کنم
public class MainContext : DbContext, IUnitOfWork
    {
        public MainContext() : base("LocalSqlServer") { }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            var contextList = new LoadContexts(); //ObjectFactory.GetAllInstances<IDbContext>();
            foreach (var context in contextList.Contexts)
                context.Setup(modelBuilder);

            Database.SetInitializer(new MigrateDatabaseToLatestVersion<MainContext, Configuration>());
            //Database.SetInitializer(new DropCreateDatabaseAlways<MainContext>());
        }

        /// <summary>
        /// Sets this instance.
        /// </summary>
        /// <typeparam name="TEntity">The type of the entity.</typeparam>
        /// <returns></returns>
        public IDbSet<TEntity> Set<TEntity>() where TEntity : class
        {
            return base.Set<TEntity>();
        }
    }
با موفقیت همه پلاگین‌ها لود میشه و مشکلی در عملیات نیست. اما Attribute‌های کلاس هارو نمیشناسه. مثلا پیام خطا تعریف شده در MVC نمایش داده نمیشه چون وجود نداره ولی وقتی کلاس مورد نظر از IValidatableObject  ارث میبره خطای‌های من نمایش داده میشه. می‌خوام از خود متادیتاهای استاندارد استفاده کنم.



مطالب
کاربرد Mixins در Vue.js
وقتی از یک زبان برنامه نویسی شیء گرا مثل سی شارپ استفاده میکنیم، تا جای ممکن سعی خواهیم کرد از نوشتن کدهای تکراری خودداری کنیم (^ , ^) . مثلا یک Super Class داریم که توسط چندین Sub Class مورد استفاده قرار میگیرد و یا از الگوهایی مانند Repository استفاده میکنیم. در Vue.js امکانی فراهم شده تا بتوان کدهایی با قابلیت استفاده‌ی مجدد ایجاد کرد. mixin  میتواند شامل تمام قابلیت‌های یک کامپوننت از قبیل بخش‌هایی مثل توابع، دیتا و ... باشد. وقتی برنامه شما توسعه پیدا میکند، احتمالا کدهای تکراری زیادی را در برنامه پیدا می‌کنید ( data , props, methods , computed , watch و ... ) و یا کامپوننت‌هایی خواهید داشت که در موارد کمی با هم تفاوت دارند؛ مانند توابعی که یک آرایه از اطلاعات را دریافت میکنند و تنها تفاوت این توابع، در آدرس فراخوانی وب سرویس می‌باشد، میتوانیم برای چنین کامپوننت‌هایی با عملکرد مشابه، از mixin استفاده کنیم. 


یک برنامه‌ی Vue.js را ایجاد کنید و سپس یک پوشه را در فولدر src بنام mixins بسازید. در این پوشه یک فایل را با نام دلخواهی از نوع جاوااسکریپت، ایجاد کنید و محتوای زیر را در آن قرار دهید:

export default  {
    data() {
        return {
            title: 'Mixins are cool',
            copyright: 'All rights reserved. Product of super awesome people'
        };
    },
    created: function () {
        this.greetings();
    },
    methods: {
        greetings() {
            console.log('Howdy my good fellow!');
        }
    }
};


برای استفاده از mixin بشکل زیر عمل میکنیم. در واقع کد زیر شامل تمام موارد تعریف شده در myMixin.js میباشد. 

<script>

import myMixin from './mixins/myMixin'

export default {
  name: 'app',
  //را دریافت میکند mixins آرایه ای از 
  mixins:[myMixin]
}
</script>

<style>


نکته: در صورتیکه بین mixin و کامپوننت، داده‌های همنامی وجود داشته باشد، اولویت با داده یا تابعی است که در خود کامپوننت تعریف شده‌است. مثال زیر را در نظر بگیرید:

export default  {
    data() {
        return {
            blogName: 'google.com'
        };
    },
    methods: {
        print() {
            console.log(this.blogName);
        }
    }
};

و در کامپوننتی که از mixin فوق استفاده میکند:

<script>
import myMixin from "./mixins/myMixin";
import duplicateFuncData from "./mixins/duplicateFuncData";

export default {
  name: "app",
  data() {
    return {
      // و کامپوننت جاری تکراری ست mixin نام این متغیر در
      blogName: "microsoft.com"
    };
  },
  methods: {
    // و کامپوننت جاری تکراری ست ولی عملکرد متفاوت دارد mixin نام این تابع در
    print() {
      alert(this.blogName);
    }
  },
  components: {},
  //را دریافت میکند mixins آرایه ای از
  mixins: [myMixin, duplicateFuncData]
};
</script>

و نتیجه‌ی اجرا:


تعریف mixin بصورت سراسری: وقتی یک mixin را بصورت global تعریف میکنیم، تمام نمونه‌های وهله سازی شده از vue، دارای قابلیت‌های تعریف شده‌ی در mixin می‌باشند. کد main.js را بشکل زیر تغییر میدهیم. اکنون با اجرای برنامه، به ازای هر نمونه‌ای از vue که وهله سازی میشود، تابع زیر اجرا می‌گردد.

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false
// بصورت سراسری تعریف شده
Vue.mixin({
  created: function () {
    alert('from global mixin')
  }
})

new Vue({
  render: h => h(App),
}).$mount('#app')


نتیجه‌گیری:

1) استفاده از mixin باعث اجتناب از تکرار کدها و داده‌های تکراری میشود (DRY).

2) از mixin در ساخت پلاگین برای Vue.js استفاده میشود.

// 3. inject some component options
  Vue.mixin({
    created: function () {
      // some logic ...
    }
    ...
  })

3) اگر mixin و کامپوننتی که از mixin استفاده میکند، هر دو توابع  lifecycle را پیاده سازی کرده باشند، اول توابع lifecycle مربوط به mixin اجرا میشود و سپس توابع lifecycle مربوط به کامپوننت.

4) اگر دو کامپوننت، mixin مشترکی را استفاده کنند، داده‌های آنها share نخواهد شد و برای اینکه دو کامپوننتی که از mixin واحدی استفاده میکنند بتوانند از داده‌های یکسانی در mixin استفاده کنند، نیاز به تزریق وابستگی دارند.

5) اگر از ادغام قسمت جاوااسکریپتی و HTML مربوط به کامپوننت‌ها ناراضی هستید، یک راه حل جداسازی، استفاده از mixin به ازای هر کامپوننت است.

6) استفاده از mixin باعث به روزسانی، نگهداری و توسعه‌ی ساده‌تر و همچنین ماژولار بودن برنامه میشود.


کد مثال مقاله‌ی جاری 

نکته: برای اجرای برنامه و دریافت پکیج‌های مورد استفاده در مثال جاری، نیاز است دستور زیر را اجرا کنید:  

npm install