نظرات مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت ششم - کامپوننت‌های تو در تو
اگر بخواهیم داخل یک کامپوننت والد به متدها و پروپرتی‌های یک کامپوننت دیگر(الزاما کامپوننت دیگر جز فرزندان آن کامپوننت نیست) دسترسی داشته باشیم .مثلا کامپوننت A  روی صفحه نمایش داده شده و با ایونت کلیک کامپوننت B متد CreateDesign متد A صدا زده شده و باعث تغییر UI کامپوننت A شود. روش کار چگونه است؟
مطالب
دستیابی به HttpContext در Blazor Server
در Asp.net core توسط IHttpContextAccessor  می‌توان به‌راحتی به HttpContext در هرجای برنامه دسترسی داشت و اطلاعات مورد نیاز را از قبیل نام کاربری، کوکی‌ها، توکن‌ها و ... به دست آورد. اما در Blazor Server برای استفاده از این قبیل اطلاعات توصیه شده‌است که اکیدا از IHttpContextAccessor در هرجایی از برنامه استفاده نشود؛ زیرا هیچ تضمینی وجود ندارد که در حالت‌های مختلف، مقدار HttpContext معتبر باشد. برای کسب اطلاعات بیشتر در این باب می‌توانید به Blazor and shared state و Accessing HttpContext.Items fails on WebKit browsers مراجعه نمایید. درکل Blazor Server خارج از چارچوب خط لوله ASP.NET Core اجرا می‌شود. اما اگر به هر دلیلی به دنبال اطلاعات خاصی از HttpContext در کامپوننت‌ها باشیم چطور؟ آیا هیچ راهی وجود ندارد؟
برای دستیابی به HttpContext در Blazor Server فقط باید در زمان رندر شدن Host.cshtml_ مقدار(های) مدنظر را از HttpContext دریافت کرده و داخل متغیری ذخیره کنیم و در ادامه‌ی برنامه از آن متغیر در قالب Cascading Parameters در کامپوننت‌ها استفاده نماییم.
روش کار به اینصورت است که ابتدا فایل Host.cshtml_ را به شکل زیر اصلاح می‌کنیم. در اینجا ما به دنبال اطلاعات توکن access_token می‌باشیم.
 @{ 
        var token = await HttpContext.GetTokenAsync("access_token");
    }

 <component type="typeof(App)" render-mode="ServerPrerendered" param-AccessToken="token" />
همانطور که ملاحظه می‌نمایید اطلاعات توکن access_token را داخل متغیر token ریخته و آن‌را به param-AccessToken کامپوننت منتسب می‌کنیم.
سپس به فایل App.razor رفته و متغیر AccessToken را به عنوان یک Cascading Value تعریف می‌کنیم. به شکل زیر:
<CascadingValue Name="AccessToken" Value="AccessToken">
    <CascadingAuthenticationState>
        <Router AppAssembly="@typeof(Program).Assembly">
            <Found Context="routeData">
                <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
            </Found>
            <NotFound>
                <LayoutView Layout="@typeof(MainLayout)">
                    <p>Sorry, there's nothing at this address.</p>
                </LayoutView>
            </NotFound>
        </Router>
    </CascadingAuthenticationState>
</CascadingValue>
@code{
    [Parameter]
     public string AccessToken { get; set; }
}
در پایان، هر کامپوننتی که نیاز به مقدار AccessToken را داشته باشد فقط کافیست آن‌را به عنوان یک Cascade Parameter تعریف کند. برای مثال یک کامپوننت را به نام ShowToken.razor ایجاد می‌کنیم و کدهای آن‌را مانند زیر قرار می‌دهیم.
@page "/showtoken"

<p>This is part of the access token @(AccessToken != null ? AccessToken.Substring(0,30) : "(null)")</p>


@code {
    [CascadingParameter(Name = "AccessToken")] public string AccessToken { get; set; }
}
بدیهی است که اطلاعات دیگری نیز می‌توان از HttpContext استخراج کرد. برای مثال شاید شما بخواهید که در یک کامپوننت، بدون استفاده از <AuthorizeView> نام کاربری شخصی که لاگین کرده است را به دست آورید (برای زمانی که از اعتبارسنجی مبتنی بر کوکی‌ها در ASP.NET Core 2.0 بدون استفاده از سیستم Identity استفاده می‌کنید). تنها کافیست بخش Host.cshtml_ را مانند زیر تغییر دهید.
var userName = await HttpContext.User.Identity.Name;
یا برای مثال اگر به دنبال اطلاعات یک کوکی خاص می‌باشید:
var khasCookie = HttpContext.Request.Cookies["khas"];
مطالب
Blazor 5x - قسمت 22 - احراز هویت و اعتبارسنجی کاربران Blazor Server - بخش 2 - ورود به سیستم و خروج از آن
در قسمت قبل، نحوه‌ی افزودن قالب ابتدایی ASP.NET Core Identity را به یک برنامه‌ی Blazor Server بررسی کردیم. در این مطلب، قسمت‌های ورود و خروج آن‌را به همراه نمایش قسمتی از صفحه، تنها به کاربران اعتبارسنجی شده، بررسی می‌کنیم تا روش دسترسی به اطلاعات ASP.NET Core Identity را در یک برنامه‌ی Blazor Server یکپارچه شده‌ی با آن، مطالعه کنیم.


نمایش قسمتی از صفحه بر اساس وضعیت اعتبارسنجی کاربر

فرض کنید می‌خواهیم در کامپوننت Shared\LoginDisplay.razor که در قسمت قبل آن‌را اضافه کردیم، لینک‌های ثبت نام و لاگین را به کاربران غیر اعتبارسنجی شده (هنوز لاگین نکرده) نمایش دهیم و اگر کاربر، اعتبارسنجی شده بود (لاگین کرده بود)، لینک خروج را به او نمایش دهیم. برای این منظور کامپوننت Shared\LoginDisplay.razor را به صورت زیر تغییر می‌دهیم:
<AuthorizeView>
    <Authorized>
        <a href="Identity/Account/Logout">Logout</a>
    </Authorized>
    <NotAuthorized>
        <a href="Identity/Account/Register">Register</a>
        <a href="Identity/Account/Login">Login</a>
    </NotAuthorized>
</AuthorizeView>
AuthorizeView، یکی از کامپوننت‌های استاندارد Blazor Server است. زمانیکه کاربری به سیستم لاگین کرده باشد، فرگمنت Authorized و در غیر اینصورت قسمت NotAuthorized آن‌را مشاهده خواهد کرد.
البته اگر برنامه را در همین حالت اجرا کنیم، به استثنای زیر خواهیم رسید:
InvalidOperationException: Authorization requires a cascading parameter of type Task<AuthenticationState>.
Consider using CascadingAuthenticationState to supply this.
Microsoft.AspNetCore.Components.Authorization.AuthorizeViewCore.OnParametersSetAsync()
برای رفع این مشکل و ارائه‌ی AuthenticationState به تمام کامپوننت‌های یک برنامه‌ی Blazor Server، نیاز است از کامپوننت CascadingAuthenticationState استفاده کرد. در مورد پارامترهای آبشاری، در قسمت نهم این سری بیشتر بحث شد و هدف از آن، ارائه‌ی یکسری اطلاعات، به تمام زیر کامپوننت‌های یک کامپوننت والد است؛ بدون اینکه نیاز باشد مدام این پارامترها را در هر زیر کامپوننتی، تعریف و تنظیم کنیم. همینقدر که آن‌ها را در بالاترین سطح سلسله مراتب کامپوننت‌های تعریف شده تعریف کردیم، در تمام زیر کامپوننت‌های آن نیز در دسترس خواهند بود.
بنابراین به فایل BlazorServer.App\App.razor که محل تعریف ریشه‌ی مسیریابی برنامه‌است، مراجعه کرده و کامپوننت آن‌را با کامپوننت توکار CascadingAuthenticationState محصور می‌کنیم:
<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
        <Found Context="routeData">
            <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
        </Found>
        <NotFound>
            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
</CascadingAuthenticationState>
اینکار سبب می‌شود تا اطلاعات AuthenticationState، بین تمام کامپوننت‌های یک برنامه‌ی Blazor به اشتراک گذاشته شود.

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



آزمایش نمایش منوی خروج برنامه

برای آزمایش برنامه، نیاز است ابتدا یک کاربر جدید را ثبت کنیم؛ چون هنوز هیچ کاربری در آن ثبت نشده‌است و همچنین کاربر پیش‌فرضی را هم به همراه ندارد. در مورد روش ثبت کاربران پیش‌فرض ASP.NET Core Identity، می‌توانید به مطلب «بازنویسی متد مقدار دهی اولیه‌ی کاربر ادمین در ASP.NET Core Identity توسط متد HasData در EF Core» مراجعه کنید و تمام نکات آن، در اینجا هم صادق است (چون پایه‌ی سیستم Identity مورد استفاده، یکی است و هدف ما در اینجا بیشتر بررسی نکات یکپارچه سازی آن با Blazor Server است و نه مرور تمام نکات ریز Identity).
بنابراین ابتدا از منوی بالای صفحه، گزینه‌ی Register را انتخاب کرده و کاربری را ثبت می‌کنیم. پس از ثبت نام، بلافاصله به منوی جدید زیر می‌رسیم که در آن گزینه‌های ورود و ثبت نام، مخفی شده‌اند و اکنون گزینه‌ی خروج از سیستم را نمایش می‌دهد:



بهبود تجربه‌ی کاربری خروج از سیستم

در همین حال که گزینه‌ی خروج نمایش داده شده‌است، اگر بر روی لینک آن کلیک کنیم، ابتدا ما را به صفحه‌ی مجزای logout هدایت می‌کند. سپس باید در این صفحه، مجددا بر روی لینک logout بالای آن کلیک کنیم. زمانیکه اینکار را انجام دادیم، اکنون صفحه‌ی دیگری را نمایش می‌دهد که به همراه پیام «خروج موفقیت آمیز از سیستم» است! در این پروسه، کاربر احساس می‌کند که کاملا از برنامه‌ی اصلی خارج شده‌است و همچنین مراحل طولانی را نیز باید طی کند.
مدیریت این مراحل توسط دو فایل زیر انجام می‌شوند:
Areas\Identity\Pages\Account\Logout.cshtml
Areas\Identity\Pages\Account\Logout.cshtml.cs

می‌خواهیم کدهای این دو فایل را به نحوی تغییر دهیم که اگر کاربری بر روی لینک logout برنامه‌ی اصلی کلیک کرد، به صورت خودکار logout شده و سپس مجددا به صفحه‌ی اصلی برنامه‌ی Blazor Server هدایت شود و مجبور نباشد تا مراحل طولانی یاد شده را تکرار کند.
به همین جهت ابتدا فایل Logout.cshtml.cs را حذف می‌کنیم؛ چون نیازی به آن نداریم. سپس محتوای فایل Logout.cshtml را به صورت زیر تغییر می‌دهیم:
@page
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager

@functions
{
    public async Task<IActionResult> OnGet()
    {
        if (SignInManager.IsSignedIn(User))
        {
            <p>You have successfully logged out of the application.</p>
            await SignInManager.SignOutAsync();
        }
        return Redirect("~/");
    }
}
با استفاده از سرویس SignInManager در ASP.NET Core Identity می‌توان یک کاربر را logout کرد که نمونه‌ای از آن‌را در اینجا مشاهده می‌کنید. در این حالت بررسی می‌شود که آیا کاربر جاری، به سیستم وارد شده‌است؟ اگر بله، کوکی‌های او حذف شده و سپس به صفحه‌ی اصلی برنامه، Redirect می‌شود. به این ترتیب به تجربه‌ی کاربری خروج بهتری خواهیم رسید.


نمایش User Claims، در یک برنامه‌ی Blazor Server

سیستم ASP.NET Core Identity، بر اساس User Claims کار می‌کند؛ اطلاعات بیشتر. پس از استفاده از CascadingAuthenticationState در بالاترین سطح برنامه، اطلاعات آن در سراسر برنامه‌ی Blazor Server هم قابل دسترسی است. برای مثال در کامپوننت Shared\LoginDisplay.razor، به نحو زیر می‌توان نام کاربر ثبت نام شده را که یکی از User Claims او است، نمایش داد:
<AuthorizeView>
    <Authorized>
        Hello, @context.User.Identity.Name
        <a href="Identity/Account/Logout">Logout</a>
    </Authorized>



محدود کردن دسترسی به صفحات برنامه تنها برای کاربران اعتبارسنجی شده

پس از لاگین موفق به سیستم، اکنون می‌خواهیم دسترسی به صفحات تعریف اتاق‌ها و یا امکانات رفاهی هتل را تنها به کاربران لاگین شده، محدود کنیم. برای اینکار تنها کافی است از ویژگی Authorize استفاده کنیم. برای مثال به کامپوننت Pages\HotelRoom\HotelRoomList.razor مراجعه کرده و یک سطر زیر را به آن اضافه می‌کنیم:
@attribute [Authorize]
دسترسی به کامپوننتی که دارای دایرکتیو فوق باشد، تنها مختص به کاربران اعتبارسنجی شده‌ی سیستم است.

مشکل! با اینکه تمام کامپوننت‌های مثال جاری را به ویژگی Authorize مزین کرده‌ایم، اما ... کار نمی‌کند! و هنوز هم می‌توان بدون لاگین به سیستم، به محتوای آن‌ها دسترسی داشت.
برای رفع این مشکل، مجددا نیاز است کامپوننت BlazorServer.App\App.razor را ویرایش کرد:
<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
        <Found Context="routeData">
            @*<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />*@
            <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
                <NotAuthorized>
                    <p>Sorry, you do not have access to this page</p>
                </NotAuthorized>
            </AuthorizeRouteView>
        </Found>
        <NotFound>
            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
</CascadingAuthenticationState>
در اینجا RouteView پیشین را کامنت کرده و با AuthorizeRouteView، جایگزین کرده‌ایم. کار آن فعالسازی پردازش ویژگی Authorize افزوده شده‌ی به کامپوننت‌های برنامه‌است. همچنین در اینجا محتوای سفارشی را که در صورت درخواست یک چنین کامپوننت‌هایی نمایش داده می‌شود، در فرگمنت NotAuthorized مشاهده می‌کنید؛ که حتی می‌تواند یک کامپوننت مجزا هم باشد:



کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: Blazor-5x-Part-22.zip
مطالب
بهینه سازی برنامه‌های وب ASP.NET برای موتورهای جستجو (SEO)
می‌دانیم بهینه‌سازی موتورهای جستجو (به انگلیسی: ‎Search engine optimization (SEO)‎)‏ که گاهی در فارسی به آن سئو نیز گفته می‌شود، عملیاتی است برای بهبود دید یک وب‌گاه یا یک صفحهٔ وب، در صفحه نتایج موتورهای جستجو که می‌تواند طبیعی و یا الگوریتمی باشد. این عملیات برای وبمسترها یکی از عوامل مهم و حیاتی بدست آوردن کاربران جدید از موتورهای جستجو است.

اگر چک لیست‌های SEO وب سایت ها را مشاهده کنیم، می‌توانیم آن‌ها را در دو دسته‌ی کلی بهینه سازی درونی و برونی وب سایت در نظر بگیریم:
Off-Page Optimization
یا برونی ، که بیشتر بر دوش مشاوران سئو و خود مدیران وب سایت است.(link building ، فعالیت در شبکه اجتماعی و ...)
و اما در حوزه On-Page Optimization یا درونی که بخش‌های مهمی از آن وظیفه‌ی مابرنامه نویس‌ها است.(H1 Tag ، URL Naming ، Meta Tags ، عنوان صفحه و ...)
[البته عامل درونی بهینه سازی محتوا (Content Optimization)  که مهمترین عامل در الگوریتم‌های نسل جدید موتورهای جستجو و همچنین الگوریتم جدید گوگل+) به حساب می‌آید بر عهده مشاوران سئو و خود مدیران وب سایت می‌باشد]


در ادامه به ارائه چند راهکار جهت بهینه سازی برنامه‌های وب ASP.NET مان برای موتورهای جستجو می‌پردازیم:


1.متدی برای ایجاد عنوان سایت
    private const string SeparatorTitle = " - ";
    private const int MaxLenghtTitle = 60;
    public static string GeneratePageTitle(params string[] crumbs)
    {
        var title = "";

        for (int i = 0; i < crumbs.Length; i++)
        {
            title += string.Format
                        (
                            "{0}{1}",
                            crumbs[i],
                            (i < crumbs.Length - 1) ? SeparatorTitle : string.Empty
                        );
        }

        title = title.Substring(0, title.Length <= MaxLenghtTitle ? title.Length : MaxLenghtTitle).Trim();

        return title;
    }
نکته :
  • MaxLenghtTitle پیشنهادی برای عنوان سایت 60 می‌باشد.

2.متدی برای ایجاد متاتگ صفحات سایت
public enum CacheControlType
{
    [Description("public")]
    _public,
    [Description("private")]
    _private,
    [Description("no-cache")]
    _nocache,
    [Description("no-store")]
    _nostore
}  
private const int MaxLenghtTitle = 60; private const int MaxLenghtDescription = 170; private const string FaviconPath = "~/cdn/ui/favicon.ico"; public static string GenerateMetaTag(string title, string description, bool allowIndexPage, bool allowFollowLinks, string author = "", string lastmodified = "", string expires = "never", string language = "fa", CacheControlType cacheControlType = CacheControlType._private) { title = title.Substring(0, title.Length <= MaxLenghtTitle ? title.Length : MaxLenghtTitle).Trim(); description = description.Substring(0, description.Length <= MaxLenghtDescription ? description.Length : MaxLenghtDescription).Trim(); var meta = ""; meta += string.Format("<title>{0}</title>\n", title); meta += string.Format("<link rel=\"shortcut icon\" href=\"{0}\"/>\n", FaviconPath); meta += string.Format("<meta http-equiv=\"content-language\" content=\"{0}\"/>\n", language); meta += string.Format("<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\"/>\n"); meta += string.Format("<meta charset=\"utf-8\"/>\n"); meta += string.Format("<meta name=\"description\" content=\"{0}\"/>\n", description); meta += string.Format("<meta http-equiv=\"Cache-control\" content=\"{0}\"/>\n", EnumExtensions.EnumHelper<CacheControlType>.GetEnumDescription(cacheControlType.ToString())); meta += string.Format("<meta name=\"robots\" content=\"{0}, {1}\" />\n", allowIndexPage ? "index" : "noindex", allowFollowLinks ? "follow" : "nofollow"); meta += string.Format("<meta name=\"expires\" content=\"{0}\"/>\n", expires); if (!string.IsNullOrEmpty(lastmodified)) meta += string.Format("<meta name=\"last-modified\" content=\"{0}\"/>\n", lastmodified); if (!string.IsNullOrEmpty(author)) meta += string.Format("<meta name=\"author\" content=\"{0}\"/>\n", author); //------------------------------------Google & Bing Doesn't Use Meta Keywords ... //meta += string.Format("<meta name=\"keywords\" content=\"{0}\"/>\n", keywords); return meta; }
چند نکته :

3.متدی برای ایجاد Slug ( اسلاگ آدرسی با مفهوم برای بکار بردن در URL ها است که دوست‌دار موتورهای جستجو می‌باشد)
private const int MaxLenghtSlug = 45;
public static string GenerateSlug(string title)
{
        var slug = RemoveAccent(title).ToLower();
        slug = Regex.Replace(slug, @"[^a-z0-9-\u0600-\u06FF]", "-");
        slug = Regex.Replace(slug, @"\s+", "-").Trim();
        slug = Regex.Replace(slug, @"-+", "-");
        slug = slug.Substring(0, slug.Length <= MaxLenghtSlug ? slug.Length : MaxLenghtSlug).Trim();

        return slug;
}
    
private static string RemoveAccent(string text)
{
        var bytes = Encoding.GetEncoding("UTF-8").GetBytes(text);
        return Encoding.UTF8.GetString(bytes);
}
نکته :
  • MaxLenghtSlug پیشنهادی برای عنوان سایت 45 می‌باشد. 

نمونه ای از کاربرد توابع :
   Head.InnerHtml = SEO.GenerateMetaTag
                            (
                                title: SEO.GeneratePageTitle(".NET Tips", "آرشیو مطالب", "ASP.NET MVC #1"),
                                description: "چرا ASP.NET MVC با وجود فریم ورک پخته‌ای به نام ASP.NET web forms، اولین سؤالی که حین سوئیچ به ASP.NET MVC مطرح می‌شود این است: «برای چی؟». بنابراین تا به این سؤال پاسخ داده نشود، هر نوع بحث فنی در این مورد بی فایده است.",
                                allowIndexPage: true,
                                allowFollowLinks: true,
                                author: "وحید نصیری",
                                cacheControlType: SEO.CacheControlType._private
                            );
و خروجی در Page Source :
<title>.NET Tips - آرشیو مطالب - ASP.NET MVC #1</title>
<link rel="shortcut icon" href="../../cdn/images/ui/favicon.ico"/>
<meta http-equiv="content-language" content="fa"/>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<meta charset="utf-8"/>
<meta name="description" content="چرا ASP.NET MVC ؟با وجود فریم ورک پخته‌ای به نام ASP.NET web forms، اولین سؤالی که حین سوئیچ به ASP.NET MVC مطرح می‌شود این است: &#171;برای چی؟&#187;. بن ..."/>
<meta http-equiv="Cache-control" content="private"/>
<meta name="robots" content="index, follow" />
<meta name="expires" content="never"/>
<meta name="author" content="وحید نصیری"/>
موفق باشید
نظرات مطالب
استفاده از GitHub Actions برای Build و توزیع خودکار پروژه‌های NET Core.
یک نکته تکمیلی:
بعد از Build کردن ریپازیتوری یا Pack کردن پکیج خود می‌توانید فایل‌های تولید شده را در جایی به نام "Artifacts" ذخیره کنید تا در زمان دلخواه بتوانید آنها را دانلود کنید
به جهت انجام این کار باید از اکشن upload-artifact استفاده کنید. مثال:
name: Build
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout
      uses: actions/checkout@v2
    - name: Setup .NET Core
      uses: actions/setup-dotnet@v1
    - name: Build (Release)
      run: dotnet build src --configuration Release
    - name: Pack (Release)
      run: dotnet pack src --configuration Release

    - name: Upload Artifact
      uses: actions/upload-artifact@v1.0.0
      with:
        name: Release_Artifacts
        path: src\bin\Release
  • عبارت src به پوشه پروژه اصلی اشاره میکند. (در صورت نیاز آن را تغییر دهید)
  • عبارت "Release_Artifacts" نام فایل Artifacts خروجی شماست که با این نام ذخیره می‌شود.
  • مسیر "src\bin\Release" پوشه ای است که میخواهید محتوای آن را در Artifacts ذخیره کنید.
بعد از اتمام فرایند در صورت موفقیت آمیز بودن فرایند، فایل Artifacts را میتوانید از این قسمت دانلود کنید. 

مطالب
پیاده سازی یک متد الحاقی برای تبدیل آدرس فیزیکی به آدرس مجازی (آدرس سرور)
سناریوی زیر را در نظر بگیرید:
در حال تهیه‌ی یک CMS هستید و طبق سفارش مشتری قسمتی را برای نمایشگاه محصولات در نظر گرفته‌اید. مشتری در نظر دارد در وب سایت خود، محصولات عرضه شده را به صورت یک گالری نمایش دهد و برای اینکار شما از یک فایل آپلودر مثل Kendo Uploader  استفاده کرده‌اید. در این حالت برای ذخیره‌ی فایلها بر روی دیسک، از متد MapPath  به صورت زیر استفاده می‌کنید:
var physicalPath = Path.Combine(Server.MapPath("~/Content/Images"), fileName);
خروجی متد بالا چیزی شبیه زیر است :
C:\\YourProject\\Content\\Images\\1.jpg 
اما برای نمایش عکسها باید بتوانیم مسیر عکس‌های ذخیره شده‌ی در فایل فیزیکی را به آدرس سرور، یا همان آدرس مجازی تبدیل کنیم. برای اینکار می‌توان از یک متد الحاقی به صورت زیر استفاده نمود :
public static class PathConverter
    {
        public static string PhysicalToVirtualPathConverter(this HttpServerUtilityBase utility, string path, HttpRequestBase context)
        {
            return path.Replace(context.ServerVariables["APPL_PHYSICAL_PATH"], "/").Replace(@"\", "/");
        }

    }
و برای استفاده از متد بالا به صورت زیر عمل می‌کنیم :
image.ImagePath = Server.PhysicalToVirtualPathConverter(PhysicalPath, Request);

مطالب
کار با شیوه‌نامه‌های فرم‌ها در بوت استرپ 4
بوت استرپ، به همراه کلاس‌هایی است، برای نمایش زیباتر فرم‌ها، که شامل کلاس‌های اعتبارسنجی و حتی کنترل نحوه‌ی چیدمان و اندازه‌ی آن‌ها نیز می‌شود.


ایجاد فرم‌های مقدماتی، با بوت استرپ 4

بوت استرپ به همراه کلاس‌هایی مانند form-group و form-control است که از آن‌ها می‌توان برای ایجاد یک فرم مقدماتی استفاده کرد. در ابتدا مثال غیر تزئین شده‌ی زیر را در نظر بگیرید:
<body>
    <div class="container">
        <h2>Medical Questionnaire</h2>
        <form>
            <fieldset>
                <legend>Owner Info</legend>
                <div>
                    <label for="ownername">Owner name</label>
                    <input type="text" id="ownername" placeholder="Your Name">
                </div>
                <div>
                    <label for="owneremail">Email address</label>
                    <input type="email" id="owneremail" aria-describedby="emailHelp"
                        placeholder="Enter email">
                    <small id="emailHelp">We'll never share your email</small>
                </div>
            </fieldset>

            <fieldset>
                <legend>Pet Info</legend>
                <div>
                    <label for="petname">Pet name</label>
                    <input type="text" id="petname" placeholder="Your Pet's name">
                </div>
                <div>
                    <label for="pettype">Pet type</label>
                    <select id="pettype">
                        <option>Choose</option>
                        <option value="cat">Dog</option>
                        <option value="cat">Cat</option>
                        <option value="bird">Other</option>
                    </select>
                </div>
                <div>
                    <label for="reasonforvisit">Reason for today's visit</label>
                    <textarea id="reasonforvisit" rows="3"></textarea>
                </div>
                <div>
                    <label>Has your pet been spayed or neutered?</label>
                    <label><input type="radio" name="spayneut" value="yes"
                            checked> Yes</label>
                    <label><input type="radio" name="spayneut" value="no"> No</label>
                </div>
                <div>
                    <label>Has the pet had any of the following in the past 30
                        days</label>
                    <label><input type="checkbox"> Abdominal pain</label>
                    <label><input type="checkbox"> Lack of appetite</label>
                    <label><input type="checkbox"> Weakness</label>
                </div>
            </fieldset>
            <button type="submit">Submit</button>
        </form>

    </div><!-- content container -->
</body>
که چنین خروجی ابتدایی را نیز به همراه دارد:


در ادامه شروع می‌کنیم به تزئین کردن این فرم، با کلاس‌های بوت استرپ 4:
- ابتدا به fieldsetهای تعریف شده، کلاس form-goup را انتساب می‌دهیم. این مورد سبب می‌شود تا اندکی فاصله بین آن‌ها ایجاد شود.
- سپس به تمام divهایی که المان‌ها را در بر گرفته‌اند نیز کلاس form-group را اعمال می‌کنیم.
با اینکار فاصله‌ی مناسبی بین المان‌های تعریف شده‌ی در صفحه ایجاد می‌شود:


- در ادامه به تمام المان‌های input، select و textarea (منهای checkboxها) کلاس form-control را نسبت می‌دهیم:


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

- پس از آن، تمام المان‌های label را انتخاب کرده و کلاس form-control-label را به آن‌ها انتساب می‌دهیم. هرچند با اینکار ظاهر فعلی فرم تغییری نمی‌کند، اما چنین تعریفی برای فعالسازی کلاس‌های اعتبارسنجی ضروری است.
اگر به هر دلیلی نخواستید این برچسب‌ها را نمایش دهید، می‌توانید از کلاس sr-only استفاده کنید که صرفا سبب نمایش آن‌ها به screen readers می‌شود.
- ذیل فیلد ورود ایمیل، متنی وجود دارد. این متن را با کلاس‌های form-text text-muted مزین می‌کنیم:


- به دکمه‌ی پایین صفحه نیز کلاس‌های btn btn-primary را اضافه می‌کنیم که در مطلب «بررسی شیوه‌نامه‌های المان‌های پر کاربرد بوت استرپ 4» بیشتر به آن‌ها پرداختیم.


مزین سازی checkboxها و radio-buttonها در بوت استرپ 4

روش مزین سازی checkboxها و radio-buttonها در بوت استرپ، با سایر المان‌ها اندکی متفاوت است:
<div class="form-check">
    <label class="form-check-label">
        <input class="form-check-input" type="checkbox">
        Lack of appetite
    </label>
</div>
در اینجا تفاوتی نمی‌کند که بخواهیم با checkboxها کار کنیم و یا radio-buttonها، هر دوی این المان‌ها ابتدا داخل یک div با کلاس form-check قرار می‌گیرند. سپس برچسب آن‌ها دارای کلاس form-check-label می‌شود و در آخر به خود این المان‌های input، کلاس form-check-input اضافه خواهد شد.

یک نکته: اگر نیاز است این المان‌ها کنار یکدیگر نمایش داده شوند، می‌توان بر روی div آن‌ها از کلاس‌های form-check form-check-inline استفاده کرد. در این حالت اگر می‌خواهید برچسب برای مثال radio-button تعریف شده، در یک سطر و گزینه‌ها آن در سطری دیگر باشند، از کلاس d-block بر روی این برچسب استفاده کنید:
<div class="form-group">
    <label class="d-block">Has your pet been spayed or
        neutered?</label>
    <div class="form-check form-check-inline">
        <label class="form-check-label">
            <input class="form-check-input" type="radio" name="spayneut"
                   value="yes" checked>
            Yes
        </label>
    </div>
    <div class="form-check form-check-inline">
        <label class="form-check-label">
            <input class="form-check-input" type="radio" name="spayneut"
                   value="no"> No
        </label>
    </div>
</div>
با این خروجی:



کلاس‌های کنترل اندازه و اعتبارسنجی المان‌های فرم‌های بوت استرپ 4

- با استفاده از کلاس form-control-sm می‌توان اندازه‌ی فیلدهای input را با ارتفاع کوچکتری نمایش داد و یا توسط کلاس form-control-lg می‌توان آن‌ها را بزرگتر کرد.
- کلاس form-inline سبب می‌شود تا یک form-group به صورت inline نمایش داده شود. یعنی برچسب و کنترل‌های درون آن، در طی یک سطر نمایش داده خواهند شد. در این حالت، نیاز به کلاس‌های Margin مانند mx-sm-2 خواهد بود تا فاصله‌ی بین کنترل‌ها را بتوان کنترل کرد.
- برای نمایش نتایج اعتبارسنجی کنترل‌ها:
  - اگر کل فرم اعتبارسنجی شده‌است، کلاس was-validated را به المان form اضافه کنید.
  - اگر اعتبارسنجی کنترلی با موفقیت روبرو شود، کلاس is-valid و اگر خیر کلاس is-invalid را به آن نسبت دهید.
  - اگر می‌خواهید پیام خاصی را پس از موفقیت اعتبارسنجی نمایش دهید، آن‌را درون یک div با کلاس valid-feedback قرار دهید و یا برعکس از کلاس invalid-feedback استفاده کنید.
  - برای تغییر رنگ برچسب المان‌ها نیز از کلاس‌های text-color همانند قبل استفاده کنید؛ مانند text-success.

یک مثال:
<div class="form-group">
    <label for="owneremail" class="text-success">Email address</label>
    <input class="form-control is-valid" type="email" id="owneremail"
        aria-describedby="emailHelp" placeholder="Enter email">
    <small class="form-text text-muted" id="emailHelp">We'll
        never share your email</small>
    <div class="valid-feedback">
        Looks good!
    </div>
</div>
با این خروجی:



تغییر نحوه‌ی چیدمان عناصر فرم‌ها در بوت استرپ 4

فرم زیر را در نظر بگیرید:


قصد داریم با استفاده از کلاس‌های ویژه‌ی بوت استرپ 4، آن‌را دو ستونی کنیم؛ به طوریکه برچسب‌ها در یک ستون و فیلدهای ورودی، در ستونی دیگر نمایش داده شوند. همچنین این فرم واکنشگرا نیز باشد؛ به این معنا که این دو ستونی شدن، فقط در اندازه‌های پس از md رخ دهد:
<body>
    <div class="container">
        <h2>Medical Questionnaire</h2>
        <form>
            <fieldset class="form-group">
                <legend>Owner Info</legend>
                <div class="form-group row">
                    <label class="form-control-label col-md-2 col-form-label text-md-right"
                        for="ownername">Owner</label>
                    <div class="col-md-10">
                        <input class="form-control" type="text" id="ownername"
                            placeholder="Your Name">
                    </div>
                </div>
                <div class="form-group row">
                    <label class="form-control-label col-md-2 col-form-label text-md-right"
                        for="owneremail">Address</label>
                    <div class="col-md-10">
                        <input class="form-control" type="text" id="owneremail"
                            placeholder="Address">
                    </div>
                </div>
                <div class="form-group row">
                    <div class="form-group col-6 offset-md-2">
                        <label class="form-control-label sr-only" for="ownercity">City</label>
                        <input class="form-control" type="text" id="ownercity"
                            placeholder="City">
                    </div>
                    <div class="form-group col-md-4 col-6">
                        <label class="form-control-label sr-only" for="ownerzip">Zip</label>
                        <input class="form-control" type="text" id="ownerzip"
                            placeholder="Zip">
                    </div>
                </div>

                <div class="form-group row">
                    <div class="offset-md-2 col-md-10">
                        <button class="btn btn-primary" type="submit">Submit</button>
                    </div>
                </div>
            </fieldset>
        </form>
    </div>
</body>
با این خروجی در اندازه‌ی پس از md:


توضیحات:
برای ستونی کردن فرم‌ها، ابتدا کلاس row، به form-group قرار گرفته‌ی داخل container اصلی اضافه می‌شود:
                <div class="form-group row">
                    <label class="form-control-label col-md-2 col-form-label text-md-right"
                        for="ownername">Owner</label>
                    <div class="col-md-10">
                        <input class="form-control" type="text" id="ownername"
                            placeholder="Your Name">
                    </div>
                </div>
سپس توسط کلاس col-md-2 تعریف شده‌ی بر روی برچسب، سبب خواهیم شد تا در اندازه‌ی صفحه‌ی بیش از md، این برچسب در یک ستون با عرض دو واحد قرار گیرد. در یک چنین حالتی، ذکر col-form-label نیز ضروری است. همچنین اگر مایل باشیم تا این برچسب، در سمت راست این ستون قرار گیرد، می‌توان از کلاس واکنشگرای text-md-right استفاده کرد.
پس از آن نوبت به تعریف ستون فیلد تعریف شده‌است که با ایجاد یک div و تعریف تعداد واحدی را که به خود اختصاص می‌دهد (col-md-10)، انجام می‌شود.

در اینجا برچسب‌های فیلدهای city و zip با کلاس sr-only مشخص شده‌اند. به همین جهت فقط به screen readers نمایش داده می‌شوند.
<div class="form-group row">
   <div class="form-group col-6 offset-md-2">
   <label class="form-control-label sr-only" for="ownercity">City</label>
   <input class="form-control" type="text" id="ownercity"placeholder="City">
</div>
در یک چنین حالتی، برای اینکه این فیلدها در ستون دوم ظاهر شوند، از کلاس offset-md-2 استفاده شده‌است. از این offset برای تراز کردن دکمه، با ستون دوم نیز استفاده کرده‌ایم:
<div class="form-group row">
    <div class="offset-md-2 col-md-10">
        <button class="btn btn-primary" type="submit">Submit</button>
    </div>
</div>

ایجاد گروهی از ورودی‌ها در بوت استرپ 4

برای افزودن آیکن‌هایی به فیلدهای ورودی، از روش ایجاد گروهی از ورودی‌ها در بوت استرپ 4 استفاده می‌شود:
<div class="form-group">
    <label class="form-control-label" for="donationamt">
        Donation Amount
    </label>
    <div class="input-group">
        <div class="input-group-prepend">
            <span class="input-group-text">$</span>
        </div>
        <input type="text" class="form-control" id="donationamt"
            placeholder="Amount">
        <div class="input-group-append">
            <span class="input-group-text">.00</span>
        </div>
    </div>
</div>
در مثال فوق، روش تعریف یک input-group را مشاهده می‌کنید. داخل آن یک input-group-prepend و سپس input-group-text تعریف می‌شود که می‌تواند شامل یک متن و یا آیکن باشد. اگر نیاز به تعریف دکمه‌ای وجود داشت، از این کلاس استفاده نکنید. با این خروجی:


در بوت استرپ 4، کلاس‌های input-group-addon و input-group-btn  بوت استرپ 3 حذف و با کلاس‌های input-group-prepend و input-group-append جایگزین شده‌اند. از prepend برای قرار دادن آیکنی پیش از فیلد ورودی و از append همانند مثال فوق، برای قرار دادن آیکنی اختیاری پس از فیلد ورودی استفاده می‌شود.

نمونه‌ی متداول دیگر آن، نحوه‌ی تعریف ویژه‌ی فیلد جستجوی سایت، در منوی راهبری آن است:
    <nav class="navbar bg-dark navbar-dark navbar-expand-sm">
        <div class="container">
            <div class="navbar-brand d-none d-sm-inline-block">
                Wisdom Pet Medicine
            </div>
            <div class="navbar-nav mr-auto">
                <a class="nav-item nav-link active" href="#">Home</a>
                <a class="nav-item nav-link" href="#">Mission</a>
                <a class="nav-item nav-link" href="#">Services</a>
                <a class="nav-item nav-link" href="#">Staff</a>
                <a class="nav-item nav-link" href="#">Testimonials</a>
            </div>
            <form class="form-inline d-none d-md-inline-block">
                <div class="input-group">
                    <label for="search" class="form-control-label sr-only"></label>
                    <input type="text" id="search" class="form-control"
                        placeholder="Search ...">
                    <div class="input-group-append">
                        <button class="btn btn-outline-light" type="submit">Go</button>
                    </div>
                </div>
            </form>
        </div>
    </nav>
با این خروجی که در آن دکمه، توسط کلاس input-group-append، با فیلد ورودی کنار آن، یکپارچه به نظر می‌رسد:




کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: Bootstrap4_10.zip
مطالب
درخت‌ها و گراف‌ها قسمت پنجم

در ،قسمت قبلی پیاده سازی درخت‌ها را بررسی کردیم و در این قسمت مبحث گراف‌ها را آغاز می‌کنیم .

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

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

درخت زیر را در نظر بگیرید؛ این درخت هم مانند سایر درخت‌ها با گره‌های شماره دار، نامگذاری شده است که تشخیص آن‌ها را برای ما آسان‌تر می‌سازد. در گراف، به گره‌ها راس یا vertice    هم می‌گویند. هر چند نام گره هم برای آن‌ها به کار برده می‌شود. به فلش‌هایی که به این رئوس اشاره می‌کنند، لبه‌های جهت دار directed edges  گفته می‌شود که در ویکی پدیا و کتب آموزشی فارسی، به آن‌ها یال اطلاق می‌شود . به یال هایی که از هر راس بیرون می‌آیند Predecessor گفته می‌شود که به معنی آغاز کننده است و به راسی که اشاره می‌کند، Successor گویند که به معنی ارث برنده یا جایگزین شناخته می‌شود. در شکل زیر عدد راس 19 آغاز کننده راس 1 است و 1 هم جایگزین یا ارث برنده 19 و اگر با دقت به شکل نگاه کنید می‌بینید که یک راس می‌تواند از چند راس ارث برنده باشد؛ مثل راس 21 .




برای نمایش گراف، ما از عبارت (V,E) استفاده می‌کنیم که V مجموعه‌ای از راس‌ها و E مجموعه‌ای از لبه‌هاست. هر لبه (که با e کوچک نمایش داده می‌شود) و در مجموعه E قرار دارد، پیوند دو راس v و u را نشان می‌دهد یا به عبارتی به صورت ریاضی می‌شود (e=(u,v .
برای اینکه مطالب را بهتر درک کنیم بهتر است که هر راس را یک شهر و هر لبه را یک جاده‌ی یک طرفه برای ارتباط با این راس‌ها فرض کنیم. مثلا اگر یکی از راس‌ها را تهران تصور کنیم و دیگری را کاشان، لبه یا جاده‌ی یک طرفه‌ای که این دو شهر را به هم متصل می‌کند، می‌شود جاده یا لبه‌ی تهران کاشان.

در بعضی مواقع از لبه‌های بدون جهت استفاده می‌شود که این لبه‌ها را لبه‌های دو طرفه می‌گویند؛ مثل جاده‌ی دو طرفه. گاهی هم از دو لبه‌ی جهت دار به جای یک لبه‌ی بدون جهت استفاده می‌کنند که نمونه‌ی آن را در شکل زیر می‌بینید.

دو راسی که به وسیله‌ی یک یال به یکدیگر متصل می‌شوند را همسایه Neighbor می‌نامند. هر یال می‌تواند یک عدد برای خود داشته باشد که به این عدد وزن یال یا لبه می‌گویندWeight (Cost)   و در مثال بالا می‌توان گفت وزن هر یال می‌شود مسافت آن جاده؛ مسافتی که بین دو شهر همسایه باید طی شود. تصویر زیر یک گراف را نشان می‌دهد که وزن یال‌های آن در کنار هر یال نوشته شده است.


مسیر Path در گراف همانند درخت‌ها، طی کردن مسیری است که از یک راس به راس دیگر می‌رسد. در مثال بالا باید گفت که برای رسیدن از شهر مبدا به شهر مقصد، باید از چه شهرهایی عبور کرد. در شکل بالا مسیر 1 - 12 - 19 - 21 - 7 - 21 و 1 مسیر نیستند؛ چرا که راس 21 هیچ لبه‌ی آغاز کننده‌ای ندارد و بیشتر ارث برنده است.

طول Length هر مسیر هم تعداد یال‌هایی است که در طول مسیر قرار دارد یا تعداد راس‌ها منهای یک؛ به این مثال دقت کنید:

مسیر 1 -12-19-21 مسیری است که طول آن سه می‌باشد.

وزن مسیر هم از جمع وزن یال‌هایی که در طول مسیر طی می‌شود به دست می‌آید.

حلقه Loop مسیری است که راس اولیه با راس نهایی یکی باشد. نمونه‌ی آن مسیر 1-12-19-1 می‌باشد. ولی مسیر 1-7-21 حلقه‌ای تشکیل نمی‌دهد.

لبه‌ی حلقه ای Looping Edge لبه‌ای است که مبداء یا آغاز کننده‌ی آن با مقصد یا ارث برنده‌ی آن یکی باشد. یعنی به راسی وصل شود که از همان، آغاز شده است. مثل لبه‌ی متصل به راس 14.


یک کلاس گراف به چه مواردی نیاز دارد:

عملیات ایجاد گراف

افزودن و حذف یک راس یا لبه

بررسی اینکه بین دو راس لبه ای وجود دارد یا خیر

جست و جوی جانشین‌های یک راس


در قسمت آینده کد آن را در سی شارپ پیاده سازی خواهیم کرد.

مطالب
یافتن اکشن متد‌های Post ایی در ASP.NET MVC که فیلتر CSRF ندارند
روش مرسوم مقابله با حملات CSRF در ASP.NET MVC، استفاده از فیلتر امنیتی ValidateAntiForgeryToken بر روی اکشن متدهایی از نوع Post است و سپس فراخوانی Html.AntiForgeryToken در View متناظر.
با بالا رفتن تعداد اکشن متدهای یک پروژه، ممکن است استفاده از ValidateAntiForgeryToken فراموش شود. در ادامه مثالی را ملاحظه می‌کنید که یک پروژه‌ی ASP.NET MVC را جهت یافتن اکشن متدهای Post ایی که فیلتر ValidateAntiForgeryToken ندارند، اسکن می‌کند:

using System;
using System.Linq;
using System.Reflection;
// Add a ref. to \Program Files\Microsoft ASP.NET\ASP.NET MVC 4\Assemblies\System.Web.Mvc.dll
using System.Web.Mvc;
// Add a ref. to System.Web
using System.Web.UI;

namespace FindOutputCaches
{
    class Program
    {
        static void Main(string[] args)
        {
            var path = @"D:\path\bin\site.dll";
            var asmTarget = Assembly.LoadFrom(path);

            checkCsrfTokens(asmTarget);

            Console.WriteLine("Press a key...");
            Console.Read();
        }


        private static void checkCsrfTokens(Assembly asmTarget)
        {
            // یافتن کلیه کنترلرها
            var controllers = asmTarget.GetTypes()
                                       .Where(type => typeof(IController).IsAssignableFrom(type) &&
                                                     !type.Name.StartsWith("T4MVC"))
                                       .ToList();

            foreach (var controller in controllers)
            {
                // یافتن کلیه اکشن متدهای کنترلر جاری
                var actionMethods = controller.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
                                              .Where(method => typeof(ActionResult).IsAssignableFrom(method.ReturnType))
                                              .ToList();

                foreach (var method in actionMethods)
                {                    
                    var httpPostAttributes = method.GetCustomAttributes(typeof(HttpPostAttribute), true);
                    if (httpPostAttributes == null || !httpPostAttributes.Any())
                        continue;

                    var csrfTokens = method.GetCustomAttributes(typeof(ValidateAntiForgeryTokenAttribute), true);
                    if (csrfTokens == null || !csrfTokens.Any())
                    {
                        Console.WriteLine("Detected [HttpPost] without [ValidateAntiForgeryToken] in:\n {0}-->{1}",
                                               controller.FullName, method.Name);
                    }
                }
            }
        }
    }
}
ابتدا مسیر اسمبلی کامپایل شده پروژه ASP.NET MVC که حاوی کنترلرهای برنامه است، باید مشخص گردد.
سپس در این اسمبلی، کلیه نوع‌های تعریف شده، یافت گردیده و آن‌هایی که پیاده سازی کننده IController هستند (یعنی کلاس‌های کنترلر واقعی برنامه)، جدا خواهند شد.
در ادامه در این کنترلرها، متدهایی را بررسی خواهیم کرد که دارای خروجی از نوع ActionResult باشند (فقط اکشن متدها مدنظر هستند). اگر این اکشن متد یافت شده دارای ویژگی HttpPost بود و همچنین ValidateAntiForgeryToken نداشت، یعنی یک مشکل امنیتی که باید برطرف شود.
مطالب
رمزنگاری Connection String از طریق خط فرمان

برای رمز نگاری Connection String ابتدا Command Prompt  را از مسیر زیر باز کنید 


Start Menu\Programs\Microsoft Visual Studio 2010\Visual Studio Tools\Visual Studio Command Prompt
و سپس دستور زیر را برای رمزنگاری Connection String وارد کنید :
aspnet_regiis.exe -pef "connectionStrings" "sulotion path"
در اینجا پارامتر اول (pef) مشخص می‌کند که Application ما از نوع File System Website است،پارامتر دوم(connectionStrings) نام کلید موردنظری است که می‌خواهیم Encrypt شود،در اینجا می‌توانیم هر کلیدی را Encrypt  کنیم به عنوان مثال فرض کنید تنظیمات SMTP را در Web.Config بدین صورت داریم : 
<system.net> 
    <mailSettings> 
      <smtp deliveryMethod="Network"> 
        <network host="smtp.gmail.com" port="587" userName="username@gmail.com" password="password" /> 
      </smtp> 
    </mailSettings> 
  </system.net> 
برای رمزنگاری بدین صورت عمل می‌کنیم :
aspnet_regiis.exe -pef "system.net/mailSettings/smtp" "sulotion path"
و بعد از Encrypt کردن توسط دستور فوق بدین صورت در Web.Config نمایش داده می‌شود :
  <system.net>
    <mailSettings>
      <smtp configProtectionProvider="RsaProtectedConfigurationProvider">
        <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
          xmlns="http://www.w3.org/2001/04/xmlenc#">
          <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
          <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
            <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
              <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
              <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
                <KeyName>Rsa Key</KeyName>
              </KeyInfo>
              <CipherData>
                <CipherValue>QuOFQrT6XwxDhQjFnM3EByleyWqYY6lA1cGK1Dzli/hrDOYSj35ADk4MB3PeLOMVYh76kB8vch0/iZKAZaJlNUPKi/iZjEzE755B3sILKGLxfkH3j3qKHB0x1WN65L6zBXgzufphCVaNRobQXOl5J3E0Df8VCf/bERZu741HLPs=</CipherValue>
              </CipherData>
            </EncryptedKey>
          </KeyInfo>
          <CipherData>
            <CipherValue>NdwBe0mWZ+Yg/DEzNuiDfXlGpicoH1ZMn54FTrLuVsY3rawS/k6KPID3bZvOWB/XYseTYFGhqs7FUEqIYMvWjJYYmDAzk6dd4iv9y6ch3ZcXWQ/R5TkQLWoLQPYgdwGI3uJNs22t28xUISm1wS0uDbizCM2Io+DzSQe8N4Ih2MP9mb2NCbZ4BZEBCPvCevpSpdEjGN9v7hk=</CipherValue>
          </CipherData>
        </EncryptedData>
      </smtp>
    </mailSettings>
  </system.net>
در اینجا Encryption از نوع RSAProtectedConfigurationProvider انتخاب شده است که می‌توانیم آنرا به DataProtectionConfgurationProvider تغییر دهیم که مورد اول از روش رمز نگاری کلید عمومی RSA استفاده می‌کند.
برای Decrypt کردن هم فقط کافی است پارامتر اول دستور aspnet_regiis را به pdf تغییر دهیم :
aspnet_regiis.exe -pdf "system.net/mailSettings/smtp" "sulotion path"