مطالب
یکسان سازی "ی" و "ک" دریافتی در حین استفاده از WCF RIA Services

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

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

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

در مورد ‌مقدمات WCF RIA Services که درSilverlight و ASP.NET کاربرد دارد می‌توانید به این مطلب مراجعه کنید: +

جهت تکمیل این بحث متدی تهیه شده که کار یکسان سازی ی و ک دریافتی از کاربر را حین ثبت توسط امکانات WCF RIA Services انجام می‌دهد (دقیقا پیش از فراخوانی متد SubmitChanges باید بکارگرفته شود):


namespace SilverlightTests.RiaYeKe
{
public static class PersianHelper
{
public static string ApplyUnifiedYeKe(this string data)
{
if (string.IsNullOrEmpty(data)) return data;
return data.Replace("ی", "ی").Replace("ک", "ک");
}
}
}

using System.Linq;
using System.Windows.Controls;
using System.Reflection;
using System.ServiceModel.DomainServices.Client;

namespace SilverlightTests.RiaYeKe
{
public class RIAHelper
{
/// <summary>
/// یک دست سازی ی و ک در عبارات ثبت شده در بانک اطلاعاتی پیش از ورود به آن
/// این متد باید پیش از فراخوانی متد
/// SubmitChanges
/// استفاده شود
/// </summary>
/// <param name="dds"></param>
public static void ApplyCorrectYeKe(DomainDataSource dds)
{
if (dds == null)
return;

if (dds.DataView.TotalItemCount <= 0)
return;

//پیدا کردن موجودیت‌های تغییر کرده
var changedEntities = dds.DomainContext.EntityContainer.GetChanges().Where(
c => c.EntityState == EntityState.Modified ||
c.EntityState == EntityState.New);

foreach (var entity in changedEntities)
{
//یافتن خواص این موجودیت‌ها
var propertyInfos = entity.GetType().GetProperties(
BindingFlags.Public | BindingFlags.Instance
);

foreach (var propertyInfo in propertyInfos)
{
//اگر این خاصیت رشته‌ای است ی و ک آن را استاندارد کن
if (propertyInfo.PropertyType != typeof (string)) continue;
var propName = propertyInfo.Name;
var val = new PropertyReflector().GetValue(entity, propName);
if (val == null) continue;
new PropertyReflector().SetValue(
entity,
propName,
val.ToString().ApplyUnifiedYeKe());
}
}
}
}
}

توضیحات:
از آنجائیکه حین فراخوانی متد SubmitChanges فقط موجودیت‌های تغییر کرده جهت ثبت ارسال می‌شوند، ابتدا این موارد یافت شده و سپس خواص عمومی تک تک این اشیاء توسط عملیات Reflection بررسی می‌گردند. اگر خاصیت مورد بررسی از نوع رشته‌ای بود، یکبار این یک دست سازی اطلاعات ی و ک دریافتی صورت خواهد گرفت (و از آنجائیکه این تعداد همیشه محدود است عملیات Reflection سربار خاصی نخواهد داشت).
اگر در کدهای خود از DomainDataSource استفاده نمی‌کنید باز هم تفاوتی نمی‌کند. متد ApplyCorrectYeKe را از قسمت DomainContext.EntityContainer به بعد دنبال کنید.
اکنون تنها مورد باقیمانده بحث جستجو است که با اعمال متد ApplyUnifiedYeKe به مقدار ورودی متد جستجوی خود، مشکل حل خواهد شد.

کلاس PropertyReflector بکارگرفته شده هم از اینجا به عاریت گرفته شد.
دریافت کدهای این بحث

نظرات مطالب
نحوه استفاده از افزونه Firebug برای دیباگ برنامه‌های ASP.NET مبتنی بر jQuery
همکاری متقابل موتور مرورگر و فایرباگ. فایرباگ درخواست می‌کنه، مرورگر دریافت و ارائه.
چیزی مثل دیباگر در VS.NET. زمانیکه مثلا در کدهای کار با Entity framework روی سطری break point قرار می‌دید و خروجی یک کوئری را بررسی می‌کنید، این دیباگر قابلیت دریافت مقادیر بررسی شده و حتی نشده را هم از بانک اطلاعاتی دارا است (حتی اگر این مقادیر، در کوئری اولیه درخواست نشده باشند؛ نوعی lazy loading در اینجا صورت می‌گیرد)
مطالب
جلوگیری از ورود همزمان کاربران با نام کاربری و رمز عبور یکسان
در اکثر برنامه‌های وب، کاربر قادر است با یک نام کاربری و رمز عبور در چند Session همزمان لاگین کند. ممکن است سیاست برخی مدیران محصول این باشد که جلوی این مورد را بگیرند تا به عنوان مثال کاربران را به جای استفاده‌ی همزمان از یک نام کاربری و رمز عبور، مجبور به خرید مجوز‌های بیشتری کنند. ASP.NET Identity به صورت پیش فرض این مورد را پشتیبانی نمی‌کند؛ اما به کمک استفاده از امکانات درونی آن می‌توان این پشتیبانی را اضافه کرد.
یکی از فیلد‌های جدول AspNetUsers فیلد SecurityStamp می‌باشد. SecurityStamp یک مقدار تصادفی است:


Security Stamp باید با هربار تغییر اطلاعات احراز هویت (مانند رمز عبور) و اختیارات کاربر(Role) تغییر کند. به عنوان مثال کاربری در چند مرورگر لاگین کرده و گزینه‌ی مرا به خاطر داشته باش را انتخاب کرده است. اگر این کاربر رمز عبورش از هر جایی عوض شود، باید لاگین او در همه‌ی Session‌ها غیر معتبر شود. این مورد با تغییر کردن SecurityStamp بعد از تغییر رمز عبور صورت می‌گیرد. ASP.NET مقدار SecurityStamp را در کوکی کاربر نگه می‌دارد و در بازه‌های زمانی، این مقدار را با مقدار درون دیتابیس مقایسه می‌کند و در صورت عدم برابری، کاربر را احراز هویت نمی‌کند. بازه‌ی زمانی این بررسی در متد ConfigureAuth قابل تنظیم است که در ادامه شرح داده خواهد شد.
صورت مساله یافتن راه حلی جهت جلوگیری از ورود همزمان چند کاربر با یک نام کاربری و رمز عبور به سیستم می‌باشد. یکی از راه‌حل هایی که در ابتدا به ذهن می‌آید استفاده از Session و نگهداری کاربران لاگین کرده در حافظه می‌باشد. پیاده سازی این راه حل می‌تواند به کمک یک کلاس Static صورت پذیرد، اما قسمت چالشی این موضوع این است که چه زمانی باید کاربر از لیست حذف گردد؟ اگر اتصال کاربر قطع شود چه عملی باید صورت گیرد؟
راه حل دیگر استفاده از SecurityStamp هست؛ به این صورت که با هربار لاگین کاربر این مقدار تصادفی به‌روز گردد و ASP.NET Identity به گونه‌ای تنظیم شود که با هر درخواست HTTP، صحت SecurityStamp بررسی گردد. مقدار پیش فرض بازه‌ی زمانی بررسی، هر 30 دقیقه یک بار است.
در مثال‌های رسمی ASP.NET Identity لاگین به صورت ذیل پیاده سازی شده است:
   
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
        {
            if (!ModelState.IsValid)
            {
                return View(model);
            }
            // This doesn't count login failures towards account lockout
            // To enable password failures to trigger account lockout, change to shouldLockout: true
            var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
            switch (result)
            {
                case SignInStatus.Success:
                    return RedirectToLocal(returnUrl);
                case SignInStatus.LockedOut:
                    return View("Lockout");
                case SignInStatus.RequiresVerification:
                    return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
                case SignInStatus.Failure:
                default:
                    ModelState.AddModelError("", "Invalid login attempt.");
                    return View(model);
            }
        }
این کد را باید به گونه‌ای تغییر داد که اگر نام کاربری و رمز عبور معتبر بودند، مقدار SeucrityStamp به‌روز گردد. به همین منظور قبل از فراخوانی PasswordSignInAsync کد ذیل اضافه می‌گردد:
       var loggedinUser = await UserManager.FindAsync(model.Email, model.Password);
            if (loggedinUser != null)
            {
                await UserManager.UpdateSecurityStampAsync(loggedinUser.Id);
            }
همانطور که مشاهده می‌شود، جهت بروز رسانی SecurityStamp از سازوکار درونی ASP.NET Identity، در واقع متد UpdateSecurityStampAsync بهره گرفته شده است.
اکنون باید تنظیمات پیش‌فرض بازه‌ی زمانی بررسی صحت SecurityStamp تغییر داده شود. این تنظیمات در فایل Startup.Auth.cs در پوشه‌ی App_Start قرار دارند:
     app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString("/Account/Login"),
                Provider = new CookieAuthenticationProvider
                {
                    // Enables the application to validate the security stamp when the user logs in.
                    // This is a security feature which is used when you change a password or add an external login to your account.  
                    OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                        validateInterval: TimeSpan.FromMinutes(30),
                        regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
                }
            });
در کد بالا OnValidateIdentity باید مقدار ذیل را بگیرد:
OnValidateIdentity =
                        SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                            TimeSpan.FromMinutes(0), // <-- Note the timer is set for zero
                            (manager, user) => user.GenerateUserIdentityAsync(manager))
اکنون Framework موظف است در هر درخواست HTTP، صحت SeucirtyStamp را بررسی کند. بنابراین اگر کاربری در سیستم لاگین باشد و کاربر دیگری با همان نام کاربری و رمز عبور لاگین کند، کاربر اول از سیستم لاگ اوت می‌شود؛ چرا که مقدار SecurityStamp او دیگر معتبر نیست. باید در نظر گرفته شود این عمل در سیستم‌های با تعداد کاربر زیاد باعث افزایش درخواست‌های دیتابیس می‌شود. 
اکنون جهت تست، اگر با مرورگر اول در سیستم لاگین صورت گیرد، سپس با همان اطلاعات در مرورگری دیگر، لاگین صورت گیرد، کاربر اول پس از درخواست بعدی، از سیستم لاگ اوت می‌شود. در مثال انتهای مطلب، صفحه‌ی About به صورت غیر عمومی درآمده که می‌توان بررسی راه حل جاری را در آن صفحه صورت داد. 
اگر بخواهیم لاگ اوت شدن کاربر را آنی کنیم، می‌توان در فواصل زمانی مشخصی، یک درخواست Ajax از سمت کلاینت به سرور ارسال کرد و تصدیق هویت کاربر را بررسی کرد:
        window.setInterval(function() {
            $.ajax({
                url:,
                type: "Post",
                dataType: "json",
                success: function(data) {
                    if (!data) {
                        alert("Someone has logged in using your username and password!");
                        location.reload();
                    }
                }
            });
        }, 60000);
در کد بالا به کمک window.setInterval، هر یک دقیقه یک بار لاگین بودن کاربر بررسی می‌گردد و در صورت لاگین نبودن، پیغام لازم به کاربر نمایش داده می‌شود. در نظر داشته باشید، این کد تنها باید در صفحات غیر عمومی قرار داده شود.
کد اکشن بررسی لاگین بودن به سادگی ذیل است:
        [AllowAnonymous]
        public virtual ActionResult Authenticated()
        {
            return Json(User.Identity.IsAuthenticated);
        }

نکته‌ی مهم این است که خصیصه‌ی AllowAnonymous بالای اکشن قرار گرفته باشد تا در صورتیکه Controller به صورت عمومی در دسترس نیست، اکشن همیشه و حتی وقتی کاربر لاگ اوت شده در دسترس باشد. در مثال انتهای مطلب صفحه‌ی About تنها در اختیار کاربران احراز هویت شده قرار گرفته است، بنابراین اگر دو کاربر با اطلاعات یکسانی به سیستم لاگین کنند، کاربر اول پیغام خطای ذیل را گرفته و به صفحه‌ی لاگین می‌رود. این کد در صفحه‌ی About در مثال انتهای مطلب قرار گرفته است:

مطالب
معرفی OLTP درون حافظه‌ای در SQL Server 2014
OLTP درون حافظه‌ای، مهم‌ترین ویژگی جدید SQL Server 2014 است. موتور بانک اطلاعاتی disk based اس کیوال سرور، حدود 15 تا 20 سال قبل تهیه شد‌ه‌است و موتور جدید درون حافظه‌ای OLTP آن، بزرگترین بازنویسی این سیستم از زمان ارائه‌ی آن می‌باشد و شروع این پروژه به 5 سال قبل بر می‌گردد. علت تهیه‌ی آن نیز به نیازهای بالای پردازش‌های همزمان مصرف کنندگان این محصول در سال‌های اخیر، نسبت به 15 سال قبل مرتبط است. با استفاده از امکانات OLTP درون حافظه‌ای، امکان داشتن جداول معمولی disk based و جداول جدید memory optimized با هم در یک بانک اطلاعاتی میسر است؛ به همراه مهیا بودن تمام زیرساخت‌هایی مانند تهیه بک آپ، بازیابی آن‌ها، امنیت و غیره برای آن‌ها.



آیا جداول بهینه سازی شده‌ی برای حافظه، همان DBCC PINTABLE منسوخ شده هستند؟

در نگارش‌های قدیمی‌تر اس کیوال سرور، دستوری وجود داشت به نام DBCC PINTABLE که سبب ثابت نگه داشتن صفحات جداول مبتنی بر دیسک یک دیتابیس، در حافظه می‌شد. به این ترتیب تمام خواندن‌های مرتبط با آن جدول، از حافظه صورت می‌گرفت. مشکل این روش که سبب منسوخ شدن آن گردید، اثرات جانبی آن بود؛ مانند خوانده شدن صفحات جدیدتر (با توجه به اینکه ساختار پردازشی و موتور بانک اطلاعاتی تغییری نکرده بود) و نیاز به حافظه‌ی بیشتر تا حدی که کل کش بافر سیستم را پر می‌کرد و امکان انجام سایر امور آن مختل می‌شدند. همچنین اولین ارجاعی به یک جدول، سبب قرار گرفتن کل آن در حافظه می‌گشت. به علاوه ساختار این سیستم نیز همانند روش مبتنی بر دیسک، بر اساس همان روش‌های قفل گذاری، ذخیره سازی اطلاعات و تهیه ایندکس‌های متداول بود.
اما جداول بهینه سازی شده‌ی برای حافظه، از یک موتور کاملا جدید استفاده می‌کنند؛ با ساختار جدیدی برای ذخیره سازی اطلاعات و تهیه ایندکس‌ها. دسترسی به اطلاعات آن‌ها شامل قفل گذاری‌های متداول نیست و در آن حداقل زمان دسترسی به اطلاعات درنظر گرفته شده‌است. همچنین در آن‌ها data pages یا index pages و کش بافر نیز وجود ندارد.


نحوه‌ی ذخیره سازی و مدیریت اطلاعات جداول بهینه سازی شده برای حافظه

جداول بهینه سازی شده برای حافظه، فرمت ردیف‌های کاملا جدیدی را نیز به همراه دارند و جهت قرارگرفتن در حافظه ودسترسی سریع به آن‌ها بهینه سازی شده‌اند. برخلاف جداول مبتنی بر دیسک سخت که اطلاعات آن‌ها در یک سری صفحات خاص به نام‌های data or index pages ذخیره می‌شوند، اینگونه جداول، دارای ظروف مبتنی بر صفحه نیستند و از مفهوم چند نگارشی برای ذخیره سازی اطلاعات استفاده می‌کنند؛ به این معنا که ردیف‌ها به ازای هر تغییری، دارای یک نگارش جدید خواهند بود و بلافاصله در همان نگارش اصلی به روز رسانی نمی‌شوند.
در اینجا هر ردیف دارای یک timestamp شروع و یک timestamp پایان است. timestamp شروع بیانگر تراکنشی است که ردیف را ثبت کرده و timestamp پایان برای مشخص سازی تراکنشی بکار می‌رود که ردیف را حذف کرده است. اگر timestamp پایان، دارای مقدار بی‌نهایت باشد، به این معنا است که ردیف متناظر با آن هنوز حذف نشده‌است. به روز رسانی یک ردیف در اینجا، ترکیبی است از حذف یک ردیف موجود و ثبت ردیفی جدید. برای یک عملیات فقط خواندنی، تنها نگارش‌هایی که timestamp معتبری داشته باشند، قابل مشاهده خواهند بود و از مابقی صرفنظر می‌گردد.
در OLTP درون حافظه‌ای که از روش چندنگارشی همزمانی استفاده می‌کند، برای یک ردیف مشخص، ممکن است چندین نگارش وجود داشته باشند؛ بسته به تعداد باری که یک رکورد به روز رسانی شده‌است. در اینجا یک سیستم garbage collection همیشه فعال، نگارش‌هایی را که توسط هیچ تراکنشی مورد استفاده قرار نمی‌گیرند، به صورت خودکار حذف می‌کند؛ تا مشکل کمبود حافظه رخ ندهد.


آیا می‌توان به کارآیی جداول بهینه سازی شده برای حافظه با همان روش متداول مبتنی بر دیسک اما با بکارگیری حافظه‌ی بیشتر و استفاده از یک SSD RAID رسید؟
خیر! حتی اگر کل بانک اطلاعاتی مبتنی بر دیسک را در حافظه قرار دهید به کارآیی روش جداول بهینه سازی شده‌ی برای حافظه نخواهید رسید. زیرا در آن هنوز مفاهیمی مانند data pages و index pages به همراه یک buffer pool پیچیده وجود دارند. در روش‌های مبتنی بر دیسک، ردیف‌ها از طریق page id و row offset آن‌ها قابل دسترسی می‌شوند. اما در جداول بهینه سازی شده‌ی برای حافظه، ردیف‌های جداول با یک B-tree خاص به نام Bw-Tree در دسترس هستند.


میزان حافظه‌ی مورد نیاز برای جداول بهینه سازی شده‌ی برای حافظه

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


چه برنامه‌هایی بهتر است از امکانات OLTP درون حافظه‌ای SQL Server 2014 استفاده کنند؟

- برنامه‌هایی که در آن‌ها تعداد زیادی تراکنش کوتاه مدت وجود دارد به همراه درجه‌ی بالایی از تراکنش‌های همزمان توسط تعداد زیادی کاربر.
- اطلاعاتی که توسط برنامه زیاد مورد استفاده قرار می‌گیرند را نیز می‌توان در جداول بهینه سازی شده جهت حافظه قرار داد.
- زمانیکه نیاز به اعمال دارای write بسیار سریع و با تعداد زیاد است. چون در جداول بهینه سازی شده‌ی برای حافظه، صفحات داده‌ها و ایندکس‌ها وجود ندارند، نسبت به حالت مبتنی بر دیسک، بسیار سریعتر هستند. در روش‌های متداول، برای نوشتن اطلاعات در یک صفحه، مباحث همزمانی و قفل‌گذاری آن‌را باید در نظر داشت. در صورتیکه در روش بهینه سازی شده‌ی برای حافظه، به صورت پیش فرض از حالتی همانند snapshot isolation و همزمانی مبتنی بر نگارش‌های مختلف رکورد استفاده می‌شود.
- تنظیم و بهینه سازی جداولی با تعداد Read بالا. برای مثال، جداول پایه سیستم که اطلاعات تعاریف محصولات در آن قرار دارند. این نوع جداول عموما با تعداد Readهای بالا و تعداد Write کم شناخته می‌شوند. چون طراحی جداول مبتنی بر حافظه از hash tables و اشاره‌گرهایی برای دسترسی به رکوردهای موجود استفاده می‌کند، اعمال Read آن نیز بسیار سریعتر از حالت معمول هستند.
- مناسب جهت کارهای data warehouse و ETL Staging Table. در جداول مبتنی بر حافظه امکان عدم ذخیره سازی اطلاعات بر روی دیسک سخت نیز پیش بینی شده‌است. در این حالت فقط اطلاعات ساختار جدول، ذخیره‌ی نهایی می‌گردد و اگر سرور نیز ری استارت گردد، مجددا می‌تواند اطلاعات خود را از منابع اصلی data warehouse تامین کند.


محدودیت‌های جداول بهینه سازی شده‌ی برای حافظه در SQL Server 2014

- تغیر اسکیما و ساختار جداول بهینه سازی شده‌ی برای حافظه مجاز نیست. به بیان دیگر دستور ALTER TABLE برای اینگونه جداول کاربردی ندارد. این مورد جهت ایندکس‌ها نیز صادق است. همان زمانیکه جدول ایجاد می‌شود، باید ایندکس آن نیز تعریف گردد و پس از آن این امکان وجود ندارد.
تنها راه تغییر اسکیمای اینگونه جداول، Drop و سپس ایجاد مجدد آن‌ها است.
البته باید درنظر داشت که SQL Server 2014، اولین نگارش این فناوری را ارائه داده‌است و در نگارش‌های بعدی آن، بسیاری از این محدودیت‌ها قرار است که برطرف شوند.
- جداول بهینه سازی شده‌ی برای حافظه حتما باید دارای یک ایندکس باشند. البته اگر یک primary key را برای آن‌ها تعریف نمائید، کفایت می‌کند.
- از unique index‌ها پشتیبانی نمی‌کند، مگر اینکه از نوع primary key باشد.
- حداکثر 8 ایندکس را می‌توان بر روی اینگونه جداول تعریف کرد.
- امکان تعریف ستون identity در آن وجود ندارد. اما می‌توان از قابلیت sequence برای رسیدن به آن استفاده کرد.
- DML triggers را پشتیبانی نمی‌کند.
- کلیدهای خارجی و قیود را پشتیبانی نمی‌کند.
- حداکثر اندازه‌ی یک ردیف آن 8060 بایت است. بنابراین از نوع‌های داده‌‌ای max دار و XML پشتیبانی نمی‌کند.
این مورد در حین ایجاد جدول بررسی شده و اگر اندازه‌ی ردیف محاسبه‌ی شده‌ی آن توسط SQL Server 2014 بیش از 8060 بایت باشد، جدول را ایجاد نخواهد کرد.


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

حالت DURABILTY انتخاب شده‌ی در حین ایجاد جدول بهینه سازی شده‌ی برای حافظه، تعیین کننده‌ای این مساله است. اگر SCHEMA_ONLY انتخاب شده باشد، کل اطلاعات شما با ری استارت سرور از دست خواهد رفت؛ البته اطلاعات ساختار جدول حفظ خواهد گردید. اگر حالت SCHEMA_AND_DATA انتخاب شود، اطلاعات شما پس از ری‌استارت سرور نیز در دسترس خواهد بود. این اطلاعات به صورت خودکار از لاگ تراکنش‌ها بازیابی شده و مجددا در حافظه قرار می‌گیرند.
حالت SCHEMA_ONLY برای مصارف برنامه‌های data warehouse بیشتر کاربرد دارد. جایی که اطلاعات قرار است از منابع داده‌ی مختلفی تامین شوند.



برای مطالعه بیشتر
SQL Server 2014: NoSQL Speeds with Relational Capabilities  
SQL Server 2014 In-Memory OLTP Architecture and Data Storage
Overview of Applications, Indexes and Limitations for SQL Server 2014 In-Memory OLTP Tables
Microsoft SQL Server 2014: In-Memory OLTP Overview
SQL Server in Memory OLTP for Database Developers
Exploring In-memory OLTP Engine (Hekaton) in SQL Server 2014 CTP1

نظرات مطالب
Identity و مباحث مربوطه (قسمت دوم) نحوه بدست آوردن مقادیر Identity
- خیر. چندین نوع استراتژی برای تعیین PK وجود دارند که یکی از آن‌ها فیلدهای Identity است و این تنها روش و الزاما بهترین روش نیست.
- مثلا زمانیکه با ORMها کار می‌کنید استفاده از فیلدهای Identity در حین ثبت تعداد بالایی از رکوردها مشکل ساز می‌شوند. چون این فیلدها تحت کنترل دیتابیس هستند و نه برنامه، ORM نیاز دارد پس از هربار Insert یکبار آخرین Id را از بانک اطلاعاتی واکشی کند. همین مساله یعنی افت سرعت در تعداد بالای Insertها (چون یکبار کوئری Insert باید ارسال شود و یکبار هم یک Select اضافی دوم برای دریافت Id تولیدی توسط دیتابیس).
- روش دوم تعیین PK استفاده از نوع Guid است. در این حالت، هم مشکل حذف رکوردها و خالی شدن یک شماره را در این بین ندارید و هم چون عموما تحت کنترل برنامه است، سرعت کار کردن با آن بالاتر است. فقط تنها مشکل آن زیبا نبودنش است در مقایسه با یک عدد ساده فیلدهای Identity.

در مورد فیلدهای Identity، تغییر شماره Id به صلاح نیست چون:
الف) همانطور که عنوان کردید روابط بین جداول را به هم خواهد ریخت.
ب) در یک وب سایت و یا هر برنامه‌ای، کلا آدرس‌ها و ارجاعات قدیمی را از بین می‌برد. مثلا فرض کنید شماره این مطلب 1381 است و شما آن‌را یادداشت کرده‌اید. در روزی بعد، برنامه نویس شماره Id‌ها را کلا ریست کرده. در نتیجه یک هفته بعد شما به شماره 1381 ایی خواهید رسید که تطابقی با مطلب مدنظر شما ندارد (حالا فرض کنید که این عدد شماره پرونده یک شخص بوده یا شماره کاربری او و نتایج و خسارات حاصل را درنظر بگیرید).
ج) این خوب است که در بین اطلاعات یک ردیف خالی وجود دارد. چون بر این اساس می‌توان بررسی کرد که آیا واقعا رکوردی حذف شده یا خیر. گاهی از اوقات کاربران ادعا می‌کنند که اطلاعات ارسالی آن‌ها نیست در حالیکه نبود این رکوردها به دلیل حذف بوده و نه عدم ثبت آن‌ها. با بررسی این Id‌ها می‌شود با کاربران در این مورد بحث کرد و پاسخ مناسبی را ارائه داد.

و اگر شماره‌ای که به کاربر نمایش می‌دهید فقط یک شماره ردیف است (و از این لحاظ می‌خواهید که حتما پشت سرهم باشد)، بهتر است یک View جدید ایجاد کنید تا این Id خود افزاینده را تولید کند (بدون استفاده از pk جدول).


پ.ن.
هدف من از این توضیحات صرفا عنوان این بود که به PK به شکل یک فیلد read only نگاه کنید. این دقیقا برخوردی است که Entity framework با این مفهوم دارد و صحیح است و اصولی. اگر در یک کشور هر روزه عده‌ای به رحمت ایزدی می‌روند به این معنا نیست که سازمان ثبت احوال باید شماره شناسنامه‌ها را هر ماه ریست کند!

مطالب
احراز هویت و اعتبارسنجی کاربران در برنامه‌های Angular - قسمت سوم - ورود به سیستم
پس از ایجاد AuthService در قسمت قبل، اکنون می‌خواهیم از آن برای تکمیل صفحه‌ی ورود به سیستم و همچنین تغییر منوی بالای برنامه یا همان کامپوننت header استفاده کنیم.


ایجاد ماژول Dashboard و تعریف کامپوننت صفحه‌ی محافظت شده

قصد داریم پس از لاگین موفق، کاربر را به یک صفحه‌ی محافظت شده هدایت کنیم. به همین جهت ماژول جدید Dashboard را به همراه کامپوننت یاد شده، به برنامه اضافه می‌کنیم:
>ng g m Dashboard -m app.module --routing
>ng g c Dashboard/ProtectedPage
پس از اجرای این دستورات، ابتدا به فایل app.module.ts مراجعه کرده و تعریف این ماژول را که به صورت خودکار به قسمت imports اضافه شده‌است، به قبل از AppRoutingModule منتقل می‌کنیم تا مسیریابی‌های آن توسط catch all ماژول AppRouting لغو نشوند:
import { DashboardModule } from "./dashboard/dashboard.module";

@NgModule({
  imports: [
    //...
    DashboardModule,
    AppRoutingModule
  ]
})
export class AppModule { }
همچنین به فایل dashboard-routing.module.ts ایجاد شده مراجعه کرده و مسیریابی کامپوننت جدید ProtectedPage را اضافه می‌کنیم:
import { ProtectedPageComponent } from "./protected-page/protected-page.component";

const routes: Routes = [
  { path: "protectedPage", component: ProtectedPageComponent }
];


ایجاد صفحه‌ی ورود به سیستم

در قسمت اول این سری، کارهای «ایجاد ماژول Authentication و تعریف کامپوننت لاگین» انجام شدند. اکنون می‌خواهیم کامپوننت خالی لاگین را به نحو ذیل تکمیل کنیم:
export class LoginComponent implements OnInit {

  model: Credentials = { username: "", password: "", rememberMe: false };
  error = "";
  returnUrl: string;
مدل login را همان اینترفیس Credentials تعریف شده‌ی در قسمت قبل درنظر گرفته‌ایم. به همراه دو خاصیت error جهت نمایش خطاها در ذیل قسمت لاگین و همچنین ذخیره سازی returnUrl در صورت وجود:
  constructor(
    private authService: AuthService,
    private router: Router,
    private route: ActivatedRoute) { }

  ngOnInit() {
    // reset the login status
    this.authService.logout(false);

    // get the return url from route parameters
    this.returnUrl = this.route.snapshot.queryParams["returnUrl"];
  }
AuthService را به سازنده‌ی کامپوننت لاگین تزریق کرده‌ایم تا بتوان از متدهای login و Logout آن استفاده کرد. همچنین از سرویس Router برای استفاده‌ی از متد navigate آن استفاده می‌کنیم و از سرویس ActivatedRoute برای دریافت کوئری استرینگ returnUrl، در صورت وجود، کمک خواهیم گرفت.
اکنون متد ارسال این فرم چنین شکلی را پیدا می‌کند:
  submitForm(form: NgForm) {
    this.error = "";
    this.authService.login(this.model)
      .subscribe(isLoggedIn => {
        if (isLoggedIn) {
          if (this.returnUrl) {
            this.router.navigate([this.returnUrl]);
          } else {
            this.router.navigate(["/protectedPage"]);
          }
        }
      },
      (error: HttpErrorResponse) => {
        console.log("Login error", error);
        if (error.status === 401) {
          this.error = "Invalid User name or Password. Please try again.";
        } else {
          this.error = `${error.statusText}: ${error.message}`;
        }
      });
  }
در اینجا توسط وهله‌ی تزریق شده‌ی this.authService، کار فراخوانی متد login و ارسال شیء Credentials به سمت سرور صورت می‌گیرد. خروجی متد login یک Observable از نوع boolean است. بنابراین در صورت true بودن این مقدار و یا موفقیت آمیز بودن عملیات لاگین، کاربر را به یکی از دو صفحه‌ی protectedPage و یا this.returnUrl (در صورت وجود)، هدایت خواهیم کرد.
صفحه‌ی خالی protectedPage را در ابتدای بحث، در ذیل ماژول Dashboard ایجاد کردیم.
در سمت سرور هم در صورت شکست اعتبارسنجی کاربر، یک return Unauthorized صورت می‌گیرد که معادل error.status === 401 کدهای فوق است و در اینجا در قسمت خطای عملیات بررسی شده‌است.

قالب این کامپوننت نیز به صورت ذیل به model از نوع Credentials آن متصل شده‌است:
<div class="panel panel-default">
  <div class="panel-heading">
    <h2 class="panel-title">Login</h2>
  </div>
  <div class="panel-body">
    <form #form="ngForm" (submit)="submitForm(form)" novalidate>
      <div class="form-group" [class.has-error]="username.invalid && username.touched">
        <label for="username">User name</label>
        <input id="username" type="text" required name="username" [(ngModel)]="model.username"
          #username="ngModel" class="form-control" placeholder="User name">
        <div *ngIf="username.invalid && username.touched">
          <div class="alert alert-danger"  *ngIf="username.errors['required']">
            Name is required.
          </div>
        </div>
      </div>

      <div class="form-group" [class.has-error]="password.invalid && password.touched">
        <label for="password">Password</label>
        <input id="password" type="password" required name="password" [(ngModel)]="model.password"
          #password="ngModel" class="form-control" placeholder="Password">
        <div *ngIf="password.invalid && password.touched">
          <div class="alert alert-danger"  *ngIf="password.errors['required']">
            Password is required.
          </div>
        </div>
      </div>

      <div class="checkbox">
        <label>
          <input type="checkbox" name="rememberMe" [(ngModel)]="model.rememberMe"> Remember me
        </label>
      </div>

      <div class="form-group">
        <button type="submit" class="btn btn-primary" [disabled]="form.invalid ">Login</button>
      </div>

      <div *ngIf="error" class="alert alert-danger " role="alert ">
        {{error}}
      </div>
    </form>
  </div>
</div>
در اینجا خاصیت‌های نام کاربری، کلمه‌ی عبور و همچنین rememberMe مدل، از کاربر دریافت می‌شوند؛ به همراه بررسی اعتبارسنجی سمت کلاینت آن‌ها؛ با این شکل:


برای آزمایش برنامه، نام کاربری Vahid و کلمه‌ی عبور 1234 را وارد کنید.


تکمیل کامپوننت Header برنامه

در ادامه، پس از لاگین موفق شخص، می‌خواهیم صفحه‌ی protectedPage را نمایش دهیم:


در این صفحه، Login از منوی سایت حذف شده‌است و بجای آن Logout به همراه «نام نمایشی کاربر» ظاهر شده‌اند. همچنین توکن decode شده به همراه تاریخ انقضای آن نمایش داده شده‌اند.
برای پیاده سازی این موارد، ابتدا از کامپوننت Header شروع می‌کنیم:
export class HeaderComponent implements OnInit, OnDestroy {

  title = "Angular.Jwt.Core";

  isLoggedIn: boolean;
  subscription: Subscription;
  displayName: string;

  constructor(private authService: AuthService) { }
این کامپوننت وضعیت گزارش شده‌ی ورود شخص را توسط خاصیت isLoggedIn در اختیار قالب خود قرار می‌دهد. برای این منظور به سرویس AuthService تزریق شده‌ی در سازنده‌ی خود نیاز دارد.
اکنون در روال رخ‌دادگردان ngOnInit، مشترک authStatus می‌شود که یک BehaviorSubject است و از آن جهت صدور رخ‌دادهای authService به تمام کامپوننت‌های مشترک به آن استفاده کرده‌ایم:
  ngOnInit() {
    this.subscription = this.authService.authStatus$.subscribe(status => {
      this.isLoggedIn = status;
      if (status) {
        this.displayName = this.authService.getDisplayName();
      }
    });
  }
Status بازگشت داده شده‌ی توسط آن از نوع boolean است و در صورت true بودن، خاصیت isLoggedIn را نیز true می‌کند که از آن در قالب این کامپوننت برای نمایش و یا مخفی کردن لینک‌های login و logout استفاده خواهیم کرد.
همچنین اگر این وضعیت true باشد، مقدار DisplayName کاربر را نیز از سرویس authService دریافت کرده و توسط خاصیت this.displayName در اختیار قالب Header قرار می‌دهیم.
در آخر برای جلوگیری از نشتی حافظه، ضروری است اشتراک به authStatus، در روال رخ‌دادگردان ngOnDestroy لغو شود:
  ngOnDestroy() {
    // prevent memory leak when component is destroyed
    this.subscription.unsubscribe();
  }

همچنین در قالب Header، مدیریت دکمه‌ی Logout را نیز انجام خواهیم داد:
  logout() {
    this.authService.logout(true);
  }

با این مقدمات، قالب Header اکنون به صورت ذیل تغییر می‌کند:
<nav class="navbar navbar-default">
  <div class="container-fluid">
    <div class="navbar-header">
      <a class="navbar-brand" [routerLink]="['/']">{{title}}</a>
    </div>
    <ul class="nav navbar-nav">
      <li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">
        <a class="nav-link" [routerLink]="['/welcome']">Home</a>
      </li>
      <li *ngIf="!isLoggedIn" class="nav-item" routerLinkActive="active">
        <a class="nav-link" queryParamsHandling="merge" [routerLink]="['/login']">Login</a>
      </li>
      <li *ngIf="isLoggedIn" class="nav-item" routerLinkActive="active">
        <a class="nav-link" (click)="logout()">Logoff [{{displayName}}]</a>
      </li>
      <li *ngIf="isLoggedIn" class="nav-item" routerLinkActive="active">
        <a class="nav-link" [routerLink]="['/protectedPage']">Protected Page</a>
      </li>
    </ul>
  </div>
</nav>
در اینجا توسط ngIfها وضعیت خاصیت isLoggedIn این کامپوننت را بررسی می‌کنیم. اگر true باشد، Logoff به همراه نام نمایشی کاربر را در منوی راهبری سایت ظاهر خواهیم کرد و در غیراینصورت لینک به صفحه‌ی Login را نمایش می‌دهیم.


تکمیل کامپوننت صفحه‌ی محافظت شده

در تصویر قبل، نمایش توکن decode شده را نیز مشاهده کردید. این نمایش توسط کامپوننت صفحه‌ی محافظت شده، مدیریت می‌شود:
import { Component, OnInit } from "@angular/core";
import { AuthService } from "../../core/services/auth.service";

@Component({
  selector: "app-protected-page",
  templateUrl: "./protected-page.component.html",
  styleUrls: ["./protected-page.component.css"]
})
export class ProtectedPageComponent implements OnInit {

  decodedAccessToken: any = {};
  accessTokenExpirationDate: Date = null;

  constructor(private authService: AuthService) { }

  ngOnInit() {
    this.decodedAccessToken = this.authService.getDecodedAccessToken();
    this.accessTokenExpirationDate = this.authService.getAccessTokenExpirationDate();
  }
}
در اینجا به کمک سرویس تزریقی AuthService، یکبار با استفاده از متد getDecodedAccessToken آن، مقدار اصلی توکن را و بار دیگر توسط متد getAccessTokenExpirationDate آن، تاریخ انقضای توکن دریافتی از سمت سرور را نمایش می‌دهیم؛ با این قالب:
<h1>
  Decoded Access Token
</h1>

<div class="alert alert-info">
  <label> Access Token Expiration Date:</label> {{accessTokenExpirationDate}}
</div>

<div>
  <pre>{{decodedAccessToken | json}}</pre>
</div>


کدهای کامل این سری را از اینجا می‌توانید دریافت کنید.
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کرده‌اید. سپس از طریق خط فرمان به ریشه‌ی پروژه‌ی ASPNETCore2JwtAuthentication.AngularClient وارد شده و دستور npm install را صادر کنید تا وابستگی‌های آن دریافت و نصب شوند. در آخر با اجرای دستور ng serve -o، برنامه ساخته شده و در مرورگر پیش فرض سیستم نمایش داده خواهد شد (و یا همان اجرای فایل ng-serve.bat). همچنین باید به پوشه‌ی ASPNETCore2JwtAuthentication.WebApp نیز مراجعه کرده و فایل dotnet_run.bat را اجرا کنید، تا توکن سرور برنامه نیز فعال شود.
مطالب
ساخت کلیدهای امنیتی GunPG
یکی از روش‌های ارسال و رمزگذاری اطلاعات، استفاده از کلیدهای امنیتی مورد استفاده‌ی در سیستم یونیکس یا GnuPG است. استفاده از نرم افزار Gnu Privacy Guard یا گارد حفاظتی گنو، به ما این اجازه را می‌دهد که بتوانیم اطلاعاتمان را در بسترهای ارتباطی، با خیالی راحت‌تر ارسال کنیم و تا حد زیادی مطمئن باشیم که تنها فرد هدف توانایی دسترسی به اطلاعات را خواهد داشت. گارد امنیتی گنو زیر مجموعه‌ای از پروژه‌ی گنو است که دولت آلمان پایه ریز اصلی آن بوده است. این نرم افزار از یک روش رمزگذاری ترکیبی استفاده می‌کند که الگوریتم‌های کلیدهای برابر(متقارن) و کلید‌های عمومی (نامتقارن) جهت تبادل آسان کلید را شامل می‌شود. در حال حاضر که نسخه‌ی دو این برنامه ارائه شده است، برای رمزگذاری‌ها از کتابخانه‌ای به اسم libgcrypt استفاده می‌کند. یکی از مشکلات فعلی این پروژه، عدم وجود api‌های مناسبی جهت دسترسی راحت‌تر است و برای حل این مشکل، GPGME که مخفف GnuPG Made Easy ایجاد شد. بسیاری از برنامه‌ها و پلاگین‌های ارسال اطلاعات، امروزه همچون ارسال ایمیل، از این کلیدها بهره می‌برند.

پروژه‌های مرتبط با این قضیه اسم‌های مشابهی دارند که گاها بعضی افراد، هر کدام از اسم‌ها را که دوست دارند، به همه اطلاق می‌کنند؛ ولی تفاوت‌هایی در این بین وجود دارد:

  • OpenPGP: یک برنامه نیست و یک قانون و استانداری برای تهیه‌ی آن است؛ که رعایت اصول آن الزامی است و برنامه‌ی بالا، یک پیاده سازی از این استاندارد است.
  • PGP: یک برنامه، برای رمزگذاری اطلاعات است که مخفف Pretty Good Privacy است.
  • و GnuPG یا GPG که در بالا به آن اشاره شد.
برای ساخت کلید، ما از دستور یا برنامه‌ی GPG که که عمدتا در همه‌ی لینوکس‌ها مثل دبیان و مشتقات آن نصب است، استفاده می‌کنیم و اگر نصب نیست از طریق توزیع آن اقدام نمایید.
در صورتیکه از ویندوز استفاده می‌کنید، نیاز است ابتدا خط فرمان یونیکس را روی آن نصب کنید. برنامه‌ی Cygwin این امکان را به شما می‌دهد تا خط فرمان یونیکس و دستورات پیش فرض آن را داشته باشید. این برنامه در دو حالت ۳۲ بیتی و ۶۴ بیتی ایجاد شده است. از آنجا که گفتیم این برنامه شامل دستورات پیش فرض آن است، برای همین GPG باید به صورت یک بسته‌ی جداگانه نصب شود که در سایت آن می‌توانید بسته‌های مختلف آن‌را برای پلتفرم‌های مختلف را مشاهده کنید.

ساخت کلید


برای ساخت کلید دستور زیر را صادر کنید:
gpg --gen-key
اگر از نسخه‌های جدیدتر GPG استفاده می‌کنید، گزینه‌هایی به شکل زیر ایجاد می‌شوند؛ ولی اگر خیر، ممکن است تعداد و شماره‌ی گزینه‌ها متفاوت باشند که در این مورد دقت کنید. من در اینجا همان حالت پیش فرض، یعنی ۱ را انتخاب می‌کنم. این گزینه نحوه‌ی امضاء و یا رمزگذاری شما با استفاده از الگوریتم‌های RSA و DSA را مشخص می‌کند.
Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
در کل در هر حالتی، استفاده‌ی از RSA پیشنهاد می‌شود. بعد از آن، از شما اندازه‌ی کلید را می‌پرسد که همان مقدار پیش فرض خودش را وارد می‌کنیم:
What keysize do you want? (2048)
البته بسیاری ۱۰۲۴ بایت را نیز کافی می‌دانند.
بعد از آن مدت زمان اعتبار این کلید را از شما جویا می‌شود:
Key is valid for? (0)
هنگام این پرسش نحوه‌ی ورود زمان را به شما خواهد گفت که می‌تواند به شکل‌های زیر باشد:
دو هفته
2w
دو سال
2y
پس از آن هم یک تاییدیه از شما می‌گیرد و تاریخ انقضاء را به طور کامل برای شما می‌نویسد و سپس نیاز است که اطلاعاتی از قبیل نام و ایمیل و توضیح را وارد کنید:
You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
    "Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>"

Real name: ali yeganeh.m
Email address: yeganehaym@gmail.com
Comment: androidbreadcrumb
You selected this USER-ID:
    "ali yeganeh.m (androidbreadcrumb) <yeganehaym@gmail.com>"
بعد از آن از شما می‌خواهد که کل عملیات را تایید و یا کنسل کنید؛ یا اگر اطلاعات بالا را اشتباه وارد کرده‌اید، اصلاح کنید. با زدن کلید O عملیات را تایید کنید. در این حین از شما یک کلید برای رمزگذاری می‌پرسد که باید آن را دو بار بدهید و کارتان در اینجا به پایان می‌رسد و کلید ایجاد می‌شود.
اگر مشکلی در ساخت کلید نباشد با ارسال دستور زیر باید آن را در لیست کلیدها ببینید:
ali@alipc:~$ gpg --list-keys
/home/ali/.gnupg/pubring.gpg
----------------------------
pub   2048R/8708016A 2015-10-23 [expires: 2065-10-10]
uid                  ali yeganeh.m (androidbreadcrumb) <yeganehaym@gmail.com>
sub   2048R/533B7E96 2015-10-23 [expires: 2065-10-10]
در اینجا کلید عمومی در خط pub بعد از  / قرار دارد؛ یعنی عبارت ۸۷۰۸۰۱۶A کلید عمومی ماست که بر روی هر سیستم و هر کلیدی متفاوت است.

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

برای دریافت این کلید متنی باید دستور زیر را صادر کنید:
gpg --output mykey.asc --export -a $GPGKEY
که برای مثال ما می‌شود:
gpg --output mykey.asc --export -a 8708016A
و اگر کلید را با یک ویرایشگر متنی باز کنید، محتوایی شبیه محتوای زیر را خواهید دید:
ali@alipc:~$ cat mykey.asc
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1

mQENBFYqAJABCADcw5xPonh5Vj7nDk1CxDskq/VsO08XOa/i2OLOzatB4oK5x+0x
jxORxXMnIAR83PCK5/WkOBa64jnu3eiP3jKEwAykGGz/Z1bezC9TIP8y+PnsiDhT
aFArluUJx+RT5q7s27aKjqoc3fR/xuwLWopZt9uYzE/DQAPDsHdUoUg+fh4Hevm+
a8/3ncR7q6nM8gc9wk621Urb1HaRrILdmeh7ZpJcl8ZUbc+NObw357fGsjnpfHXO
rdCr7ClvNUq6I+IeGMQG/6040LeeaqhaRxPrUhbFjLA155gkSqzecxl7wQaYc71M
Zdlv+6Pt1B8nPAA3WXq0ypjU8A5bvmAQRD5LABEBAAG0OGFsaSB5ZWdhbmVoLm0g
KGFuZHJvaWRicmVhZGNydW1iKSA8eWVnYW5laGF5bUBnbWFpbC5jb20+iQE+BBMB
AgAoBQJWKgCQAhsDBQld/A8ABgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRDS
Lhq8hwgBanaHB/4reGxUjR6dB08ykfwQOx+raYHGqJlgawisE4qUHTkGaspyQaNy
yxh0vwKkGvg6nNy2VN1XFBc7jlHlrYqPPuPdg2B+1LvEghb30ESDbHUvk8NrJgDJ
C0257gxqWvUQTWvMC3FkSLdw3tyQ8dF7FxmSU79XcxVqGeseaDzMQrEasP0yJHsm
NJf8pvuD6qiWu3KSSoQmI/17Sj8s7eGJMh6o5YRFGHc1Bt9tCD+52bvt579Ju4vZ
tmQvxR4fNQo9sAeMqAJhIpF7IYcuyCEy+CQ847UkzE4f/OCCPxfV3samV/nnBJJ9
Ouu+68lk6Fpx4A0a3nEwqoAmMWxrbSSUFW97uQENBFYqAJABCAC4CzrUOKskE4hK
GVCjaOJKxhbuUdOrep6n3vof0fscs5Dy7h2oVh2vb12WH9X6pijJVPiUpGR4Mpu0
lO2Bu9Rwt38AQ6mRmL/hfzjEXSvKkdX7osk+1CVnnUaSdM9Ek2hWUH8JcN28z/WT
X9Bw8MCdZF7j1HvX/5ojghzMZyYM4elWJLBr1gON6xXAI6HR7DlnRkaVr8L9SYGm
FyAXZ0LzWYwG1Z1AnTyxff6v/Mn3p1/1E3aBA+LkQqBzHg2nBm4jCaFWfeCdiNBf
CHkY9r/Evo9hUPD+CtBNFwsUm1D4maZ0FFtIQ701QhVmupnub+rKoObC0AFj3abK
MCw9uo8TABEBAAGJASUEGAECAA8FAlYqAJACGwwFCV38DwAACgkQ0i4avIcIAWrz
rAf+K1IIMtBq3WlabfZQrgzFHQ62ugVJO/yI1ITkm4l08XHDf+ShqDg4urNuMDEe
oQD35MvB2BhER1jL6VR3qjLkZyZYJ+EQiSxEDWXooav3KvpWjhcqjQy79GFs8waH
E7ssGmWwaugVS/PJAmGQ+s8YWDNa6aCClmp2dJRiwBTyFdewNBLA2V32xzWCYxhI
YtEp+Kg15XuCDTRatOPWSFGSPe/paytmpGZc0XzU/W9sBpabhxVmcL4H6L07uCef
IOn/S5QXo3P9X/3ckmJ9GUb7rjdq1ivYgX53xI75jlePsmN/2f+3fNffUaZgFTTd
Uls+XCun7OVYSBBfjgRfQbTvoA==
=6j7i
-----END PGP PUBLIC KEY BLOCK-----
در صورتی که قصد دارید متن کلید خصوصی را به دست بیاورید، لازم است بعد از export- عبارت secret-key- را نیز اضافه کنی د؛ یعنی:
gpg --output mykey.asc --export-secret-key -a 8708016A


آپلود کلید به سرورهای کلید (Key Servers
)

 یکی از روش‌های به اشتراک گذاری کلید برای کاربران این است که از سرورهای کلید استفاده کنیم. یکبار آپلود روی یکی از این سرورها باعث می‌شود که به بقیه‌ی سرورها هم اضافه شود. یکی از این سرورهای کلید که خودم از آن استفاده می‌کنم، سرور ابونتو است و با استفاده از دستور زیر، همان کلید بالا را برای آن سرور ارسال می‌کنم:
gpg --send-keys --keyserver keyserver.ubuntu.com $GPGKEY

==>
gpg --send-keys --keyserver keyserver.ubuntu.com 8708016A
سپس از طریق کلید متنی، کلید آپلود شده را تایید می‌کنیم. به این آدرس رفته و محتوای کلید متنی خود را به طور کامل به همراه تگ‌های شروع و پایان کپی کنید و حتی می‌توانید کلید خود را از طریق کادر جست و جو پیدا کنید.

رمزگذاری
ابتدا در محیط یونیکس، یک فایل متنی ساده با متن hello ubuntu را ایجاد میکنم. در ادامه قصد دارم این فایل را رمزنگاری کنم:
ali@alipc:~$ cat >ali.txt
hello ubuntu
سپس همین فایل را رمزنگاری می‌کنم:
ali@alipc:~$ gpg --output myali.gpg --encrypt --recipient yeganehaym@gmail.com ali.txt
در این دستور ابتدا گفتیم که نام فایل خروجی ما myali.gpg است و می‌خواهیم آن را رمزگذاری کنیم که توسط کلیدی با ایمیل yeganehaym@gmail.com می‌باشد فایل ali.txt را رمزگذاری می‌کنیم.

رمزگشایی
برای رمزگشایی می‌توانید از طریق دستور زیر اقدام کنید:
gpg --output output.txt --decrypt myali.gpg

You need a passphrase to unlock the secret key for
user: "ali yeganeh.m (androidbreadcrumb) <yeganehaym@gmail.com>"
2048-bit RSA key, ID 533B7E96, created 2015-10-23 (main key ID 8708016A)
در اینجا دستور دادیم محتوای فایل رمزشده‌ی myali.gpg را رمزگشایی کن و محتوای آن را داخل فایلی با نام output.txt قرار بده. بعد از اجرای این دستور از شما عبارت رمزی را که در مرحله‌ی ساخت کلید دوبار از شما پرسید، درخواست می‌کند. در بعضی سیستم‌ها در همان ترمینال می‌پرسد، ولی بعضی سیستم‌ها مثل ابونتو که من از آن استفاده می‌کنم، به صورت گرافیکی یک کادر باز کرده و از شما خواهش می‌کند عبارت رمز را وارد کنید.
عبارت رمز را وارد کنید و حالا فایل output.txt را باز کنید:
ali@alipc:~$ cat output.txt 
hello ubuntu
مطالب
استفاده از Web Fonts در اپلیکیشن های ASP.NET MVC

Typography در طراحی وب

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

به همین دلیل است که فریم ورک‌های HTML/CSS هر روزه محبوب‌تر می‌شوند. فریم ورک هایی مانند Twitter Bootstrap, HTML5 Boilerplate, Foundation و غیره. این فریم ورک‌ها طراحی و ساخت اپلیکیشن‌های وب را ساده‌تر و سریع‌تر می‌کنند، چرا که بسیاری از نیازهای رایج در طراحی وب پیاده سازی شده اند و دیگر نیازی به اختراع مجدد آنها نیست. اما به غیر از طراحی وب سایتی با ظاهری شیک و پسندیده، مقوله Typography شاید آخرین چیزی باشد که توسعه دهندگان به آن فکر می‌کنند.

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

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

طراحان وب طی سالیان متمادی، از فونت هایی استفاده می‌کردند که احتمال وجود آنها روی بازه گسترده ای از کامپیوتر‌ها زیاد بود. اینگونه فونت‌ها با عنوان 'web-safe' یاد می‌شوند:
  • Arial
  • Courier New
  • Helvetica
  • Times New Roman
  • Trebuchet
  • Georgia
  • Verdana
این لیست کوتاه، تنوع بسیار محدودی را در اختیار طراحان قرار می‌داد. برای رفع این محدودیت تکنیک‌های مختلفی استفاده می‌شد. مثلا متون مورد نظر را بصورت تصویر درج می‌کردند، یا با استفاده از افزونه ای در مرورگر از فونت خاصی استفاده می‌شد. مشکل عمده این روش‌ها این بود که متون مورد نظر قابل انتخاب و جستجو نبودند.

Web Fonts
یکی از راه حل‌ها برای رفع این محدودیت، توسعه فونت‌های وب بود. فونت‌های وب روی هر پلفترمی قابل استفاده هستند و توسط یک درخواست HTTP بارگذاری می‌شوند. با استفاده از فونت‌های وب، متون ما قابل انتخاب، جستجو و ترجمه به زبان‌های دیگر هستند. ناگفته نماند که بازه type-face‌‌های بسیار گسترده‌تری هم در دست داریم. از طرف دیگر به دلیل اینکه اکثر مرورگرهای وب امروزی از Web Fonts پشتیبانی می‌کنند، می‌توان اطمینان داشت که خروجی مورد نظر تقریبا روی تمام پلتفرم‌ها یکسان خواهد بود.

استفاده از فونت‌های وب در اپلیکیشن‌های ASP.NET MVC
خوب، چگونه می‌توانیم از فونت‌های وب در اپلیکیشن‌های MVC استفاده کنیم؟ در ادامه یک نمونه را بررسی میکنیم.
1. یک سرویس فونت انتخاب کنید. سرویس دهندگان زیادی وجود دارند که فونت‌های وب رایگان و پولی متنوعی را فراهم می‌کنند. برخی از سرویس دهندگان محبوب:
در مثال جاری از سرویس Typekit استفاده خواهیم کرد. نحوه استفاده از دیگر سرویس‌ها هم تقریبا یکسان است.
2. فونت مورد نظر را انتخاب کنید. سرویس‌های مذکور کتابخانه‌های بزرگی از فونت‌های وب دارند، که توسط رابط کاربری قوی آنها می‌توانید رندر نهایی فونت‌ها را مشاهده کنید. همچنین می‌توانید لیست فونت‌های موجود را بر اساس پارامترهای مختلفی مانند خواص، طبقه بندی، توصیه شده‌ها و غیره فیلتر کنید. برای مثال جاری فونت Bistro Script Web را انتخاب می‌کنیم.

3. کد جاوا اسکریپت خود را تولید کنید (تمام سرویس دهندگان این امکان را پیاده سازی کرده اند).

4. قطعه کد جاوا اسکریپت تولید شده را، در قسمت <head> فایل (Layout.cshtml (Razor_ یا (Site.Master (ASPX کپی کنید.

5. CSS Selector‌‌های لازم برای فونت مورد نظر را تولید کنید.

6. کد css تولید شده را در فایل Site.css کپی کنید. در مثال جاری فونت کل اپلیکیشن را تغییر می‌دهیم. برای اینکار، خانواده فونت "bistro-script-web" را به تگ body اضافه می‌کنیم.

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

همین! حالا می‌توانیم تغییرات اعمال شده را مشاهده کنیم. بصورت پیش فرض قالب پروژه‌ها از فونت "Segoe UI" استفاده می‌کنند، که خروجی زیر را رندر می‌کند.

استفاده از فونت جدید "Bistro Script Web" وب سایت را مانند تصویر زیر رندر خواهد کرد.

همانطور که می‌بینید استفاده از فونت‌های وب بسیار ساده است. اما بهتر است از اینگونه فونت‌ها برای کل سایت استفاده نشود و تنها روی المنت‌های خاصی مانند سر تیتر‌ها (h1,h2,etc) اعمال شوند.

مطالب
#Defensive Code in C - قسمت اول

Defensive Coding به معنی است که شما با انجام یکسری کار‌ها و در نظر گرفتن یکسری زیر ساخت‌ها در توسعه‌ی نرم افزار خود، به اهداف ذیل دست پیدا کنید:

1. Quality (کیفیت)

2. Comprehensible (جامعیت)

3. Predictable  (قابلیت پیش بینی)

دستیابی به هر کدام از این اهداف و روش‌های اعمال آنها بر روی یک پروژه‌ی نرم افزاری، در ادامه بحث خواهند شد. 

1. Clean Code

یکی از اهداف Defensive Coding که در ابتدای مقاله بحث شد جامعیت یا Comprehension بود. برای رسید به این هدف از مفهومی به نام Clean Code  استفاده می‌شود. Clean Code علاوه بر این مسئله، در پی ساده کردن ساختار بندی پشتیبانی و کاهش باگ‌های نرم افزار نیز هست. ویژگی‌های Clean Code در بالا با  توجه به شکل ذیل تشریح می‌شوند: 

· Easy to read

یک کد Clean  قابلیت خوانایی بالایی دارد. بسیاری از برنامه نویسان در سطوح مختلف با اهمیت این مسئله در توسعه نرم افزار آشنایی دارند. ولی بسیاری از همین برنامه نویسان این اصول را رعایت نمی‌کنند و سعی نمی‌کنند با اصول پیاده سازی آن در نرم افزارآشنا شوند.

اگر قابلیت خوانایی یک کد بالا باشد:

§ شما می‌توانید Pattern ‌های موجود در کد خود را که می‌توانید به عنوان نامزدهایی جهت Refactoring  هستند، تشخیص دهید.

§ برنامه نویسان دیگر به راحتی قصد و اهداف ( intent ) شما را از نوشتن یک کد خاص درک خواهند کرد و در طول زمان با خطا‌های زیادی روبرو نمی‌شوند.

§ توسعه‌ی راحت‌تر و در شرایط وجود فشار، ایجاد سریع یک قابلیت جدید در نرم افزار.

· Clear intent

یک کد Clear دارای اهداف روشن و قابل فهمی می‌باشد.

· Simple

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

· Minimal

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

· Thoughtful

یک کد Clean  کدی است که ساختار آن متفکرانه طراحی شده باشد. از نحوه‌ی طراحی یک کلاس گرفته تا layering و Tiering پروژه باید کاملا هوشمندانه و با توجه به پارامتر‌های موجود باشند. همچنین خطا‌های خطرناک و استثناء‌ها باید کاملا هندل شوند. 

همه‌ی ما با دیدن کد بالا سریعا مفهوم اسپاگتی کد به ذهنمان خطور می‌کند. تغییر، توسعه و پشتیبانی نرم افزارهایی که کد آنها به این صورت نوشته شده است، بسیار سخت و پر هزینه می‌باشد. در این حالت تغییر هر یک از اجزاء ممکن است بر سایر قسمت‌های دیگر تاثیرات مختلفی داشته باشد. راه کاری که در این حالت ارائه می‌شود، Refactoring می‌باشد. در این روش کد را به کلاس‌ها و متدهایی بر حسب عملکرد تقسیم خواهیم کرد. در نهایت کد تولید شده دارای کمترین تاثیر بر سایر قسمت‌ها خواهد بود. توجه داشته باشید که با انجام این کار، قدمی به سوی SOC یا Separation Of Concern برداشته‌اید.

1. Testable Code & Unit Test

یکی دیگر از اهداف Defensive Coding افزایش کیفیت یا Quality می‌باشد که برای رسیدن به این هدف از مفهوم Testable Code & Unit Test استفاده می‌شود. بسیاری از ویژگی‌های Testable Code و Clean Code با هم مشابه می‌باشند. برای مثال Refactor کردن هر متد به متد‌های کوچکتر، تست آن را ساده‌تر خواهند کرد. در نتیجه نوشتن کد‌های Testable ، با نوشتن کد‌های clean شروع می‌شود.

در این قسمت اشاره‌ای به Unit Test شده است؛ اما این مفهوم می‌تواند به یک مفهوم گسترده‌تر به نام  Automated Code testing، تعمیم داده شود. به این دلیل که تست فقط به Unit Testing محدود نمی‌شود و می‌تواند شامل سایر انواع تست‌ها مانند  integration test نیز باشد.

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

 


1. Predictability

یکی دیگر از اهداف Defensive Coding، قابلیت پیش بینی یا Predictability می‌باشد. فرآیند تشخیص و پیش بینی خطا‌ها را Predictability می‌گویند. با درنظر گرفتن امکان وقوع خطاهای مختلف و تصمیم گرفتن در مورد اینکه در هنگام رخ دادن این خطا باید چه کاری صورت بگیرد، می‌توان در رسیدن به این هدف قدم بزرگی برداشت. 

برای رسیدن به این هدف باید اصل Trust but Verify را دنبال کنیم. برای مثال این اصل به ما می‌گوید که در هنگام تعریف متد‌های public باید یکسری موارد را در نظر بگیریم. یک متد باید از یکسری قرارداد‌ها پیروی کند. یک متد قرارداد می‌کند که یکسری پارامتر‌ها را با یک data type خاص به عنوان ورودی دریافت کند. قرارداد می‌کند که یک مقدار خاص با یک data type خاص را به عنوان نوع بازگشتی بازگرداند یا اینکه هیچ مقداری را باز نگرداند و در نهایت یک متد متعهد می‌شود که یکسری Exception ‌تعریف شده و پیش بینی شده را صادر کند. اما برای اینکه مطمئن شویم یک application واقعا قابل پیش بینی است و این اصل را به درستی پیاده سازی کرده است، اعتماد می‌کنیم اما Verify را هم انجام می‌دهیم. برای verify کردن باید پارامترها، دیتا‌های متغیر، مقادیر بازگشتی و استثناء‌ها به گونه‌ای بررسی شوند که مطمئن شویم انتظارت ما را برآورده کرده‌اند. 

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


مطالب
نکات استفاده از افزونه‌ی Web Essentials جهت پردازش LESS
در این مطلب، نحوه‌ی استفاده از افزونه‌ی Web Essentials جهت پردازش فایل‌های LESS را بررسی می‌کنیم. پیش‌تر مطالبی را در رابطه با CSS pre-processorها مطالعه کرده‌اید، LESS نیز یک CSS pre-processor است، یا در واقع بهتر است بگوئیم یک زبان جهت پویا کردن CSS می‌باشد که در سال 2009 توسط Alexis Sellier به صورت سورس باز ایجاد شد. یکی از خصوصیات جالب LESS نسبت به دیگر CSS preprocessorها، قابلیت کامپایل فایل‌های CSS به صورت real-time از طریق مرورگر توسط LESS.js می‌باشد.

روش‌های استفاده از LESS در دات نت
1- استفاده از SimpLESS
SimpLESS تمام تغییرات فایل‌های CSS را مشاهده می‌کند و در نهایت به صورت خودکار آنها را به CSS کامپایل و همچنین miniy می‌کند. این روش مستقل از دات نت است و برای هر فایل LESSی جوابگو خواهد بود.

2- استفاده از {}less.

Dotless یک پیاده سازی از کتابخانه جاوا اسکریپتی LESS برای دات نت می‌باشد. پکیج نیوگت DotLess را نیز می‌توانید از اینجا دریافت کنید. بعد از اضافه شدن فایل‌های آن، یک ارجاع به dotless.core به پروژه تان اضافه خواهد شد. همچنین در فایل Web.Config در قسمت HttpHandler خط زیر اضافه خواهد شد:

<add type="dotless.Core.LessCssHttpHandler,dotless.Core" validate="false" path="*.LESS" verb="*" />

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

<section name="dotless" type="dotless.Core.configuration.DotlessConfigurationSectionHandler,dotless.Core" />

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

<dotless minifyCss="false" cache="true" />

3- استفاده از افزونه‌ی Web Essentials

Web Essentials برای کامپایل فایل‌های LESS از کامپایلر node استفاده می‌کند. کار با این افزونه خیلی ساده است. کافی است پسوند فایل CSS موجود در پروژه تان را درون ویژوال استودیو، به less. تغییر دهید. با دوبار کلیک بر روی فایل، ویرایشگر فایل‌های LESS برای شما نمایش داده می‌شود، همزمان نیز فایل یک فایل CSS و یک نسخه از فایل CSS را به صورت فشرده، برایتان تولید می‌کند. خب، هر بار که فایل LESS را تغییر دهید، Web Essentials به صورت خودکار فایل‌های css. و min.css. را برایتان روز رسانی می‌کند.

خوب با کلیک بر روی فایل less، ویرایشگر فایل‌های less نمایش داده می‌شود که با تغییر فایل css می‌توانید پیش نمایش آنرا در سمت راست مشاهده کنید:

تعریف متغیر

با استفاده از syntax زیر می‌توانید متغیرهای خود را تعریف کنید:

@variable-name: variableValue;

یکی از قابلیت‌های جالب در حین مقداردهی متغیرها به خصوص زمانیکه مقدار یک کد رنگی باشد، نمایش کادر انتخاب رنگ است، این کادر بلافاصله بعد از نوشتن علامت # در ابتدای مقدار متغیر نمایش داده می‌شود:

به طور مثال با تعریف متغیر فوق هر جایی می‌توانیم برای تعیین رنگ از آن استفاده کنیم:

@primary-color: #ff6a00;
body
{
    background-color: @primary-color;
}

استفاده از توابع

LESS شامل تعداد زیادی توابع از پیش نوشته شده است که می‌توانید به راحتی از آنها استفاده کنید، توابعی از جمله کار با رنگ ها، اعمال ریاضی و غیره. استفاده از آنها خیلی ساده است. به طور مثال در کد زیر از تابع percentage جهت تبدیل 0.5 به 50% استفاده کرده ایم:

.myClass
{
    width: percentage(0.5);
}

استخراج یک فایل

یکی دیگر از قابلیت‌های Web Essentials استخراج(Extract) یک فایل می‌باشد به طور مثال فایل LESS شما شامل متغیرهای زیر است:

@primary-color: #7BA857;
@primary-color-light: #B6DE8F;
@primary-color-lighter: #D3EFC3;
@primary-color-lightest: #EFFAE6;
@secondary-color: #AE855C;
@text-color-light: #666666;
@text-color-dark: #0444;

به راحتی می‌توانید تعاریف فوق را درون یک فایل LESS دیگر با نام colors.less قرار دهید:

تغییر تنظیمات پیش فرض Web Essentials

افزونه Web Essentials دارای یک قسمت جهت تغییر تنظیمات پیش فرض برای کار با LESS می‌باشد که با مراجعه به منوی Tools در ویژوال استودیو و سپس Options می‌توانید آنها را تغییر دهید:

Auto-compile dependent files on save: توسط این گزینه می‌توانیم تعیین کنیم که فایل‌های که import کرده ایم تنها در صورتی که تغییر کرده و ذخیره شده باشند، در فایل CSS جاری کامپایل شوند.

Compile files on build: توسط این گزینه می‌توانیم تعیین کنیم که فایل‌های less در زمان Build پروژه کامپایل شوند.

Compile files on save: توسط این گزینه می‌توانیم تعیین کنیم که فایل‌های less در زمان ذخیره کردن پروژه کامپایل شوند. 

Create source map files: اگر این گزینه True باشد فایل map. نیز تولید خواهد شد.

Custom output directory: اگر می‌خواهید خروجی در پوشه‌ی موردنظر شما نمایش داده شود می‌توانید آدرس آن را تعیین کنید.

Don't save raw compilation output: با فعال بودن این گزینه فایل CSS عادی ایجاد نخواهد شد.

Process source maps: توسط این گزینه می‌توانید قابلیت‌های ویرایشگر فایل‌های source map را فعال یا غیرفعال کنید.

Strict Math: با فعال بودن این گزینه LESS تمام اعمال ریاضی درون فایل CSS را پردازش خواهد کرد.

Show preview pane: از این گزینه نیز جهت نمایش یا عدم نمایش preview window استفاده می‌شود.