نظرات مطالب
یکپارچه سازی Angular CLI و ASP.NET Core در VS 2017
برای تکرار مجدد: ساختار مدیریت پروژه‌های قدیمی MVC 5 در VS مانند پروژه‌های VSCode یا ASP.NET Core (در همان VS با همان نگارش) نیستند. یعنی هرفایلی که در فایل csproj ارجاعی نداشته باشد، در IDE نمایش داده نمی‌شود (اما در پروژه‌های VSCode و یا پروژه‌های جدید ASP.NET Core، به محض اضافه شدن یک فایل جدید به پوشه‌ی پروژه، این فایل در IDE هم نمایان می‌شود). بنابراین روی دکمه‌ی show all files در solution explorer کلیک کنید و فایل‌های جدید را include کنید (مانند قبل و تمام پروژه‌های دیگری از این دست).


یک نکته: علت اینکه پروژه‌های ASP.NET Core به این صورت پویا عمل می‌کنند، وجود NET Core CLI. هست. این CLI هم شبیه به Angular-CLI یک ابزار خط فرمان است که کار ایجاد یک پروژه‌ی جدید تا ساخت و توزیع برنامه را مدیریت می‌کند و در حقیقت VS فقط این فرامین خط فرمان را در پشت صحنه اجرا می‌کند. بنابراین بهتر است از ساختار پروژه‌ای استفاده کنید که اساسا برای ابزارهای CLI طراحی شده‌است.

مطالب
چگونه نرم افزارهای تحت وب سریعتری داشته باشیم؟ قسمت هشتم
قسمت هفتم 

22. استفاده از  CSS Sprites 
ایده اصلی این تکنیک به این صورت است که تمامی عکس‌های کوچک (دراینجا همه 100 عکس) در قالب یک تصویر بزرگ قرار خواهد گرفت و با استفاده از CSS مختصات هر عکس کوچک را در تصویر بزرگ پیدا کرده و نمایش می‌دهیم. یکی شدن 100 عکس کوچک به یک عکس بزرگ، تاثیر زیادی در پایین آمدن حجم عکس جدید خواهد داشت و مرورگر شما به جای درخواست 100 عکس از سرور، تنها یکی دانلود می‌کند و از این به بعد از کش مرورگر برای بازیابی آن استفاده می‌کند. این موضوع به معنی ترافیک کمتر شبکه و آزاد شدن منابع پر ارزش حافظه، cpu و پهنای باند در سمت سرور و کاربران. برای اطلاع بیشتر از این تکنیک می‌توانید به این مقاله مراجعه نمایید.

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

24. حذف HTTP modules های اضافی
HTTP modules هایی را که در برنامه خود استفاده نمی‌کنید را حذف کنید. این کار یعنی سربار مدیریتی کمتر در ماژول ASP.NET سرور شما. برای اجرای این مورد می‌توانید از کدی مشابه این کد در web.config خود استفاده کنید:
<httpModules>
    <remove name="OutputCache"/>
    <remove name="Session"/>
    <remove name="WindowsAuthentication"/>
    <remove name="FormsAuthentication"/>
    <remove name="PassportAuthentication"/>
    <remove name="RoleManager"/>
    <remove name="UrlAuthorization"/>
    <remove name="FileAuthorization"/>
    <remove name="AnonymousIdentification"/>
    <remove name="Profile"/>
    <remove name="ErrorHandlerModule"/>
    <remove name="ServiceModel"/>
</httpModules>
البته حواستان به این موضوع باشد ماژول‌های مورد استفاده در برنامه خود را حذف نکنید که در این صورت ممکن است این آخرین پروژه شما با صاحب کارتان باشد!
مطالب
افزودن یک صفحه‌ی جدید و دریافت و نمایش اطلاعات از سرور به کمک Ember.js
در قسمت قبل با مقدمات برپایی یک برنامه‌ی تک صفحه‌ای وب مبتنی بر Ember.js آشنا شدیم. مثال انتهای بحث آن نیز یک لیست ساده را نمایش می‌دهد. در ادامه همین برنامه را جهت نمایش لیستی از اشیاء JSON دریافتی از سرور تغییر خواهیم داد. همچنین یک صفحه‌ی about را نیز به آن اضافه خواهیم کرد.


پیشنیازهای سمت سرور

- ابتدا یک پروژه‌ی خالی ASP.NET را ایجاد کنید. نوع آن مهم نیست که Web Forms باشد یا MVC.
- سپس قصد داریم مدل کاربران سیستم را توسط یک ASP.NET Web API Controller در اختیار Ember.js قرار دهیم. مباحث پایه‌ای Web API نیز در وب فرم‌ها و MVC یکی است.
مدل سمت سرور برنامه چنین شکلی را دارد:
namespace EmberJS02.Models
{
    public class User
    {
        public int Id { set; get; }
        public string UserName { set; get; }
        public string Email { set; get; }
    }
}
کنترلر Web API ایی که این اطلاعات را در ختیار کلاینت‌ها قرار می‌دهد، به نحو ذیل تعریف می‌شود:
using System.Collections.Generic;
using System.Web.Http;
using EmberJS02.Models;
 
namespace EmberJS02.Controllers
{
    public class UsersController : ApiController
    {
        // GET api/<controller>
        public IEnumerable<User> Get()
        {
            return UsersDataSource.UsersList;
        }
    }
}
در اینجا UsersDataSource.UsersList صرفا یک لیست جنریک ساده از کلاس User است و کدهای کامل آن‌را می‌توانید از فایل پیوست انتهای بحث دریافت کنید.

همچنین فرض بر این است که مسیریابی سمت سرور ذیل را نیز به فایل global.asax.cs، جهت فعال سازی دسترسی به متدهای کنترلر UsersController تعریف کرده‌اید:
using System;
using System.Web.Http;
using System.Web.Routing;
 
namespace EmberJS02
{
    public class Global : System.Web.HttpApplication
    { 
        protected void Application_Start(object sender, EventArgs e)
        {
            RouteTable.Routes.MapHttpRoute(
               name: "DefaultApi",
               routeTemplate: "api/{controller}/{id}",
               defaults: new { id = RouteParameter.Optional }
               );
        }
    }
}

پیشنیازهای سمت کاربر

پیشنیازهای سمت کاربر این قسمت با قسمت «تهیه‌ی اولین برنامه‌ی Ember.js» دقیقا یکی است.
ابتدا فایل‌های مورد نیاز Ember.js به برنامه اضافه شده‌اند:
 PM> Install-Package EmberJS
سپس یک فایل app.js با محتوای ذیل به پوشه‌ی Scripts اضافه شده‌است:
App = Ember.Application.create();
App.IndexRoute = Ember.Route.extend({
    setupController:function(controller) {
        controller.set('content', ['red', 'yellow', 'blue']);
    }
});
و  در آخر یک فایل index.html با محتوای ذیل کار برپایی اولیه‌ی یک برنامه‌ی مبتنی بر Ember.js را انجام می‌دهد:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script src="Scripts/jquery-2.1.1.js" type="text/javascript"></script>
    <script src="Scripts/handlebars.js" type="text/javascript"></script>
    <script src="Scripts/ember.js" type="text/javascript"></script>
    <script src="Scripts/app.js" type="text/javascript"></script>
</head>
<body>
    <script type="text/x-handlebars" data-template-name="application">
        <h1>Header</h1>
        {{outlet}}
    </script>
    <script type="text/x-handlebars" data-template-name="index">
        Hello,
        <strong>Welcome to Ember.js</strong>!
        <ul>
            {{#each item in content}}
            <li>
                {{item}}
            </li>
            {{/each}}
        </ul>
    </script>
</body>
</html>
تا اینجا را در قسمت قبل مطالعه کرده بودید.
در ادامه قصد داریم به هدر صفحه، دو لینک Home و About را اضافه کنیم؛ به نحوی که لینک Home به مسیریابی index و لینک About به مسیریابی about که صفحه‌ی جدید «درباره‌ی برنامه» را نمایش می‌دهد، اشاره کنند.


تعریف صفحه‌ی جدید About

برنامه‌های Ember.js، برنامه‌های تک صفحه‌ای وب هستند و صفحات جدید در آن‌ها به صورت یک template جدید تعریف می‌شوند که نهایتا متناظر با یک مسیریابی مشخص خواهند بود.
به همین جهت ابتدا در فایل app.js مسیریابی about را اضافه خواهیم کرد:
App.Router.map(function() {
    this.resource('about');
});
به این ترتیب با فراخوانی آدرس about/ در مرورگر توسط کاربر، منابع مرتبط با این آدرس و قالب مخصوص آن، توسط Ember.js پردازش خواهند شد.
بنابراین به صفحه‌ی index.html برنامه مراجعه کرده و صفحه‌ی about را توسط یک قالب جدید تعریف می‌کنیم:
<script type="text/x-handlebars" data-template-name="about">
    <h2>Our about page</h2>
</script>
تنها نکته‌ی مهم در اینجا مقدار data-template-name است که سبب خواهد شد تا به مسیریابی about، به صورت خودکار متصل و مرتبط شود.

در این حالت اگر برنامه را در حالت معمولی اجرا کنید، خروجی خاصی را مشاهده نخواهید کرد. بنابراین نیاز است تا لینکی را جهت اشاره به این مسیر جدید به صفحه اضافه کنیم:
<script type="text/x-handlebars" data-template-name="application">
    <h1>Ember Demo App</h1>
    <ul class="nav">
        <li>{{#linkTo 'index'}}Home{{/linkTo}}</li>
        <li>{{#linkTo 'about'}}About{{/linkTo}}</li>
    </ul>
    {{outlet}}
</script>
اگر از قسمت قبل به خاطر داشته باشید، عنوان شد که قالب ویژه‌ی application به صورت خودکار با وهله سازی Ember.Application.create به صفحه اضافه می‌شود. اگر نیاز به سفارشی سازی آن وجود داشت، خصوصا جهت تعریف عناصری که باید در تمام صفحات حضور داشته باشند (مانند منوها)، می‌توان آن‌را به نحو فوق سفارشی سازی کرد.
در اینجا با استفاده از امکان یا directive ویژه‌ای به نام linkTo، لینک‌هایی به مسیریابی‌های index و about اضافه شده‌اند. به این ترتیب اگر کاربری برای مثال بر روی لینک About کلیک کند، کتابخانه‌ی Ember.js او را به صورت خودکار به مسیریابی about و سپس نمایش قالب مرتبط با آن (قالب about ایی که پیشتر تعریف کردیم) هدایت خواهد کرد؛ مانند تصویر ذیل:


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


دریافت و نمایش اطلاعات از سرور

اکنون که با نحوه‌ی تعریف یک صفحه‌ی جدید و برپایی سیم کشی‌های مرتبط با آن آشنا شدیم، می‌خواهیم صفحه‌ی دیگری را به نام Users به برنامه اضافه کنیم و در آن لیست کاربران ارائه شده توسط کنترلر Web API سمت سرور ابتدای بحث را نمایش دهیم.
بنابراین ابتدا مسیریابی جدید users را به صفحه اضافه می‌کنیم تا لیست کاربران، در آدرس users/ قابل دسترسی شود:
App.Router.map(function() {
    this.resource('about');
    this.resource('users');
});
سپس نیاز است مدلی را توسط فراخوانی Ember.Object.extend ایجاد کرده و به کمک متد reopenClass آن‌را توسعه دهیم:
App.UsersLink = Ember.Object.extend({});
App.UsersLink.reopenClass({
    findAll: function () {
        var users = [];
        $.getJSON('/api/users').then(function(response) {
            response.forEach(function(item) {
                users.pushObject(App.UsersLink.create(item));
            });
        });
        return users;
    }
});
در اینجا متد دلخواهی را به نام findAll اضافه کرده‌ایم که توسط متد getJSON جی‌کوئری، به مسیر /api/users سمت سرور متصل شده و لیست کاربران را از سرور به صورت JSON دریافت می‌کند. در اینجا خروجی دریافتی از سرور به کمک متد pushObject به آرایه کاربران اضافه خواهد شد. همچنین نحوه‌ی فراخوانی متد create مدل UsersLink را نیز در اینجا مشاهده می‌کنید (App.UsersLink.create).

پس از اینکه نحوه‌ی دریافت اطلاعات از سرور مشخص شد، باید اطلاعات این مدل را در اختیار مسیریابی Users قرار داد:
App.UsersRoute = Ember.Route.extend({
    model: function() {
        return App.UsersLink.findAll();
    }
});
 
App.UsersController = Ember.ObjectController.extend({
    customHeader : 'Our Users List'
});
به این ترتیب زمانیکه کاربر به مسیر users/ مراجعه می‌کند، سیستم مسیریابی می‌داند که اطلاعات مدل خود را باید از کجا تهیه نماید.
همچنین در کنترلری که تعریف شده، صرفا یک خاصیت سفارشی و دلخواه جدید، به نام customHeader برای نمایش در ابتدای صفحه تعریف و مقدار دهی گردیده‌است.
اکنون قالبی که قرار است اطلاعات مدل را نمایش دهد، چنین شکلی را خواهد داشت:
<script type="text/x-handlebars" data-template-name="users">
    <h2>{{customHeader}}</h2>
    <ul>
        {{#each item in model}}
        <li>
            {{item.Id}}-{{item.UserName}} ({{item.Email}})
        </li>
        {{/each}}
    </ul>
</script>
با تنظیم data-template-name به users سبب خواهیم شد تا این قالب اطلاعات خودش را از مسیریابی users دریافت کند. سپس یک حلقه نوشته‌ایم تا کلیه عناصر موجود در مدل را خوانده و در صفحه نمایش دهد. همچنین در عنوان قالب نیز از خاصیت سفارشی customHeader استفاده شده‌است:




کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید:
EmberJS02.zip
نظرات مطالب
مقابله با XSS ؛ یکبار برای همیشه!
این ماژول و کدهای اون رو به روز کردم و از اینجا قابل دریافت است:
HtmlCleaner.zip

تفاوت‌ها:
- برای دات نت سه و نیم کامپایل شده. فقط فایل dll رو به پروژه خودتون cs یا vb اضافه کنید.
- متد ToSafeHtml کلاس HtmlSanitizer برای کار با تگ‌های مشخص شده با حروف کوچک و بزرگ بهبود یافته (الان در همین سایت جاری استفاده می‌شود).
- الزامی نیست حتما از AntiXssModule آن استفاده کنید. کلا هرجایی که Allow Html دارید، متد ToSafeHtml را برای پاکسازی اطلاعات فراخوانی کنید (در MVC و یا در وب فرم‌ها).
- کلاس PersianProofWriter هم به آن اضافه شده (جزئی از ToSafeHtml است). یک سری از مسایل مانند نیم فاصله‌ها رو به صورت خودکار اصلاح می‌کند؛ به همراه اصلاح ی و ک فارسی.

به صورت خلاصه:
فقط از متد ToSafeHtml کلاس HtmlSanitizer آن به صورت دستی و در موارد لازم که HTML از کاربر دریافت می‌شود، استفاده کنید.
مطالب
Microsoft SQL Server 2008 Management Objects

یک سرویس کوچک ویندوز ان اتی نوشته‌ام که کارش این است که در پایان هر هفته، تمام دیتابیس‌های اس کیوال سرور موجود را یافته و اسکریپت تمام اشیاء آن‌ها را به صورت خودکار تولید می‌کند (از جداول گرفته تا تریگرها، رویه‌های ذخیره شده و غیره)، سپس کل مجموعه را فشرده کرده و سپس ایمیل می‌زند. این‌کار برای نگهداری تغییرات انجام شده در طول یک هفته لازم است.
برنامه با استفاده از امکانات SMO تهیه شده است و اگر علاقمند بودید که این‌کار را انجام دهید، می‌توانید به مقاله‌های زیر رجوع کنید:

Making a database clone using SMO

Using the SqlServer.Management.Smo

SQL Server: SMO Scripting Basics


با آمدن اس کیوال سرور 2008، اشیاء SMO هم به روز شده‌اند و اگر با این اشیاء برنامه نویسی کرده باشید، برنامه بر روی سروری با اس کیوال سرور 2005 اجرا نخواهد شد و پیغام خطای زیر را دریافت خواهید کرد:

Could not load file or assembly 'Microsoft.SqlServer.Management.Sdk.Sfc, Version=10.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91' or one of its dependencies. The system cannot find the file specified.


خوشبختانه مایکروسافت این کتابخانه‌ها را به صورت مجزا هم برای دریافت قرار داده است و می‌توان آن‌ها را نصب نمود تا برنامه بدون اشکال اجرا شود. به صفحه زیر و قسمت Microsoft SQL Server 2008 Management Objects مراجعه نمائید:
اینجا کلیک نمائید

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

مطالب
مهاجرت از SQL Membership به ASP.NET Identity
در این مقاله مهاجرت یک اپلیکیشن وب که توسط SQL Membership ساخته شده است را به سیستم جدید ASP.NET Identity بررسی می‌کنیم. برای این مقاله از یک قالب اپلیکیشن وب (Web Forms) که توسط Visual Studio 2010 ساخته شده است برای ساختن کاربران و نقش‌ها استفاده می‌کنیم. سپس با استفاده از یک SQL Script دیتابیس موجود را به دیتابیسی که ASP.NET Identity نیاز دارد تبدیل می‌کنیم. در قدم بعدی پکیج‌های مورد نیاز را به پروژه اضافه می‌کنیم و صفحات جدیدی برای مدیریت حساب‌های کاربری خواهیم ساخت. بعنوان یک تست، کاربران قدیمی که توسط SQL Membership ساخته شده بودند باید قادر باشند به سایت وارد شوند. همچنین کاربران جدید باید بتوانند بدون هیچ مشکلی در سیستم ثبت نام کنند. سورس کد کامل این مقاله را می‌توانید از این لینک دریافت کنید.


یک اپلیکیشن با SQL Membership بسازید

برای شروع به اپلیکیشنی نیاز داریم که از SQL Membership استفاده می‌کند و دارای داده هایی از کاربران و نقش‌ها است. برای این مقاله، بگذارید پروژه جدیدی توسط VS 2010 بسازیم.

حال با استفاده از ابزار ASP.NET Configuration دو کاربر جدید بسازید: oldAdminUser و oldUser.

نقش جدیدی با نام Admin بسازید و کاربر oldAdminUser را به آن اضافه کنید.

بخش جدیدی با نام Admin در سایت خود بسازید و فرمی بنام Default.aspx به آن اضافه کنید. همچنین فایل web.config این قسمت را طوری پیکربندی کنید تا تنها کاربرانی که در نقش Admin هستند به آن دسترسی داشته باشند. برای اطلاعات بیشتر به این لینک مراجعه کنید.

پنجره Server Explorer را باز کنید و جداول ساخته شده توسط SQL Membership را بررسی کنید. اطلاعات اصلی کاربران که برای ورود به سایت استفاده می‌شوند، در جداول aspnet_Users و aspnet_Membership ذخیره می‌شوند. داده‌های مربوط به نقش‌ها نیز در جدول aspnet_Roles ذخیره خواهند شد. رابطه بین کاربران و نقش‌ها نیز در جدول aspnet_UsersInRoles ذخیره می‌شود، یعنی اینکه هر کاربری به چه نقش هایی تعلق دارد.

برای مدیریت اساسی سیستم عضویت، مهاجرت جداول ذکر شده به سیستم جدید ASP.NET Identity کفایت می‌کند.

مهاجرت به Visual Studio 2013

  • برای شروع ابتدا Visual Studio Express 2013 for Web یا Visual Studio 2013 را نصب کنید.
  • حال پروژه ایجاد شده را در نسخه جدید ویژوال استودیو باز کنید. اگر نسخه ای از SQL Server Express را روی سیستم خود نصب نکرده باشید، هنگام باز کردن پروژه پیغامی به شما نشان داده می‌شود. دلیل آن وجود رشته اتصالی است که از SQL Server Express استفاده می‌کند. برای رفع این مساله می‌توانید SQL Express را نصب کنید، و یا رشته اتصال را طوری تغییر دهید که از LocalDB استفاده کند.
  • فایل web.config را باز کرده و رشته اتصال را مانند تصویر زیر ویرایش کنید.

  • پنجره Server Explorer را باز کنید و مطمئن شوید که الگوی جداول و داده‌ها قابل رویت هستند.
  • سیستم ASP.NET Identity با نسخه 4.5 دات نت فریم ورک و بالا‌تر سازگار است. پس نسخه فریم ورک پروژه را به آخرین نسخه (4.5.1) تغییر دهید.

پروژه را Build کنید تا مطمئن شوید هیچ خطایی وجود ندارد.

نصب پکیج‌های NuGet

در پنجره Solution Explorer روی نام پروژه خود کلیک راست کرده، و گزینه Manage NuGet Packages را انتخاب کنید. در قسمت جستجوی دیالوگ باز شده، عبارت "Microsoft.AspNet.Identity.EntityFramework" را وارد کنید. این پکیج را در لیست نتایج انتخاب کرده و آن را نصب کنید. نصب این بسته، نیازمندهای موجود را بصورت خودکار دانلود و نصب می‌کند: EntityFramework و ASP.NET Identity Core. حال پکیج‌های زیر را هم نصب کنید (اگر نمی‌خواهید OAuth را فعال کنید، 4 پکیج آخر را نادیده بگیرید).
  • Microsoft.AspNet.Identity.Owin
  • Microsoft.Owin.Host.SystemWeb
  • Microsoft.Owin.Security.Facebook
  • Microsoft.Owin.Security.Google
  • Microsoft.Owin.Security.MicrosoftAccount
  • Microsoft.Owin.Security.Twitter

مهاجرت دیتابیس فعلی به سیستم ASP.NET Identity

قدم بعدی مهاجرت دیتابیس فعلی به الگویی است، که سیستم ASP.NET Identity به آن نیاز دارد. بدین منظور ما یک اسکریپت SQL را اجرا می‌کنیم تا جداول جدیدی بسازد و اطلاعات کاربران را به آنها انتقال دهد. فایل این اسکریپت را می‌توانید از لینک https://github.com/suhasj/SQLMembership-Identity-OWIN دریافت کنید.
این اسکریپت مختص این مقاله است. اگر الگوی استفاده شده برای جداول سیستم عضویت شما ویرایش/سفارشی-سازی شده باید این اسکریپت را هم بر اساس این تغییرات بروز رسانی کنید.
پنجره Server Explorer را باز کنید. گره اتصال ApplicationServices را باز کنید تا جداول را مشاهده کنید. روی گره Tables کلیک راست کرده و گزینه New Query را انتخاب کنید.

در پنجره کوئری باز شده، تمام محتویات فایل Migrations.sql را کپی کنید. سپس اسکریپت را با کلیک کردن دکمه Execute اجرا کنید.

ممکن است با اخطاری مواجه شوید مبنی بر آنکه امکان حذف (drop) بعضی از جداول وجود نداشت. دلیلش آن است که چهار عبارت اولیه در این اسکریپت، تمام جداول مربوط به Identity را در صورت وجود حذف می‌کنند. از آنجا که با اجرای اولیه این اسکریپت چنین جداولی وجود ندارند، می‌توانیم این خطاها را نادیده بگیریم. حال پنجره Server Explorer را تازه (refresh) کنید و خواهید دید که پنج جدول جدید ساخته شده اند.

لیست زیر نحوه Map کردن اطلاعات از جداول SQL Membership به سیستم Identity را نشان می‌دهد.

  • aspnet_Roles --> AspNetRoles
  • aspnet_Users, aspnet_Membership --> AspNetUsers
  • aspnet_UsersInRoles --> AspNetUserRoles

جداول AspNetUserClaims و AspNetUserLogins خالی هستند. فیلد تفکیک کننده (Discriminator) در جدول AspNetUsers باید مطابق نام کلاس مدل باشد، که در مرحله بعدی تعریف خواهد شد. همچنین ستون PasswordHash به فرم 'encrypted password|password salt|password format' می‌باشد. این شما را قادر می‌سازد تا از رمزنگاری برای ذخیره و بازیابی کلمه‌های عبور استفاده کنید. این مورد نیز در ادامه مقاله بررسی شده است.



ساختن مدل‌ها و صفحات عضویت

بصورت پیش فرض سیستم ASP.NET Identity برای دریافت و ذخیره اطلاعات در دیتابیس عضویت از Entity Framework استفاده می‌کند. برای آنکه بتوانیم با جداول موجود کار کنیم، می‌بایست ابتدا مدل هایی که الگوی دیتابیس را نمایندگی می‌کنند ایجاد کنیم. برای این کار مدل‌های ما یا باید اینترفیس‌های موجود در Identity.Core را پیاده سازی کنند، یا می‌توانند پیاده سازی‌های پیش فرض را توسعه دهند. پیاده سازی‌های پیش فرض در Microsoft.AspNet.Identity.EntityFramework وجود دارند.
در نمونه ما، جداول AspNetRoles, AspNetUserClaims, AspNetLogins و AspNetUserRole ستون هایی دارند که شباهت زیادی به پیاده سازی‌های پیش فرض سیستم Identity دارند. در نتیجه می‌توانیم از کلاس‌های موجود، برای Map کردن الگوی جدید استفاده کنیم. جدول AspNetUsers ستون‌های جدیدی نیز دارد. می‌توانیم کلاس جدیدی بسازیم که از IdentityUser ارث بری کند و آن را گسترش دهیم تا این فیلدهای جدید را پوشش دهد.
پوشه ای با نام Models بسازید (در صورتی که وجود ندارد) و کلاسی با نام User به آن اضافه کنید.

کلاس User باید کلاس IdentityUser را که در اسمبلی Microsoft.AspNet.Identity.EntityFramework وجود دارد گسترش دهد. خاصیت هایی را تعریف کنید که نماینده الگوی جدول AspNetUser هستند. خواص ID, Username, PasswordHash و SecurityStamp در کلاس IdentityUser تعریف شده اند، بنابراین این خواص را در لیست زیر نمی‌بینید.

  public class User : IdentityUser
    {
        public User()
        {
            CreateDate = DateTime.Now;
            IsApproved = false;
            LastLoginDate = DateTime.Now;
            LastActivityDate = DateTime.Now;
            LastPasswordChangedDate = DateTime.Now;
            LastLockoutDate = DateTime.Parse("1/1/1754");
            FailedPasswordAnswerAttemptWindowStart = DateTime.Parse("1/1/1754");
            FailedPasswordAttemptWindowStart = DateTime.Parse("1/1/1754");
        }

        public System.Guid ApplicationId { get; set; }
        public string MobileAlias { get; set; }
        public bool IsAnonymous { get; set; }
        public System.DateTime LastActivityDate { get; set; }
        public string MobilePIN { get; set; }
        public string Email { get; set; }
        public string LoweredEmail { get; set; }
        public string LoweredUserName { get; set; }
        public string PasswordQuestion { get; set; }
        public string PasswordAnswer { get; set; }
        public bool IsApproved { get; set; }
        public bool IsLockedOut { get; set; }
        public System.DateTime CreateDate { get; set; }
        public System.DateTime LastLoginDate { get; set; }
        public System.DateTime LastPasswordChangedDate { get; set; }
        public System.DateTime LastLockoutDate { get; set; }
        public int FailedPasswordAttemptCount { get; set; }
        public System.DateTime FailedPasswordAttemptWindowStart { get; set; }
        public int FailedPasswordAnswerAttemptCount { get; set; }
        public System.DateTime FailedPasswordAnswerAttemptWindowStart { get; set; }
        public string Comment { get; set; }
    }

حال برای دسترسی به دیتابیس مورد نظر، نیاز به یک DbContext داریم. اسمبلی Microsoft.AspNet.Identity.EntityFramework کلاسی با نام IdentityDbContext دارد که پیاده سازی پیش فرض برای دسترسی به دیتابیس ASP.NET Identity است. نکته قابل توجه این است که IdentityDbContext آبجکتی از نوع TUser را می‌پذیرد. TUser می‌تواند هر کلاسی باشد که از IdentityUser ارث بری کرده و آن را گسترش می‌دهد.

در پوشه Models کلاس جدیدی با نام ApplicationDbContext بسازید که از IdentityDbContext ارث بری کرده و از کلاس User استفاده می‌کند.

public class ApplicationDbContext : IdentityDbContext<User>
{
        
}

مدیریت کاربران در ASP.NET Identity توسط کلاسی با نام UserManager انجام می‌شود که در اسمبلی Microsoft.AspNet.Identity.EntityFramework قرار دارد. چیزی که ما در این مرحله نیاز داریم، کلاسی است که از UserManager ارث بری می‌کند و آن را طوری توسعه می‌دهد که از کلاس User استفاده کند.

در پوشه Models کلاس جدیدی با نام UserManager بسازید.

public class UserManager : UserManager<User>
{
        
}

کلمه عبور کاربران بصورت رمز نگاری شده در دیتابیس ذخیره می‌شوند. الگوریتم رمز نگاری SQL Membership با سیستم ASP.NET Identity تفاوت دارد. هنگامی که کاربران قدیمی به سایت وارد می‌شوند، کلمه عبورشان را توسط الگوریتم‌های قدیمی SQL Membership رمزگشایی می‌کنیم، اما کاربران جدید از الگوریتم‌های ASP.NET Identity استفاده خواهند کرد.

کلاس UserManager خاصیتی با نام PasswordHasher دارد. این خاصیت نمونه ای از یک کلاس را ذخیره می‌کند، که اینترفیس IPasswordHasher را پیاده سازی کرده است. این کلاس هنگام تراکنش‌های احراز هویت کاربران استفاده می‌شود تا کلمه‌های عبور را رمزنگاری/رمزگشایی شوند. در کلاس UserManager کلاس جدیدی بنام SQLPasswordHasher بسازید. کد کامل را در لیست زیر مشاهده می‌کنید.

public class SQLPasswordHasher : PasswordHasher
{
        public override string HashPassword(string password)
        {
            return base.HashPassword(password);
        }

        public override PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
        {
            string[] passwordProperties = hashedPassword.Split('|');
            if (passwordProperties.Length != 3)
            {
                return base.VerifyHashedPassword(hashedPassword, providedPassword);
            }
            else
            {
                string passwordHash = passwordProperties[0];
                int passwordformat = 1;
                string salt = passwordProperties[2];
                if (String.Equals(EncryptPassword(providedPassword, passwordformat, salt), passwordHash, StringComparison.CurrentCultureIgnoreCase))
                {
                    return PasswordVerificationResult.SuccessRehashNeeded;
                }
                else
                {
                    return PasswordVerificationResult.Failed;
                }
            }
        }

        //This is copied from the existing SQL providers and is provided only for back-compat.
        private string EncryptPassword(string pass, int passwordFormat, string salt)
        {
            if (passwordFormat == 0) // MembershipPasswordFormat.Clear
                return pass;

            byte[] bIn = Encoding.Unicode.GetBytes(pass);
            byte[] bSalt = Convert.FromBase64String(salt);
            byte[] bRet = null;

            if (passwordFormat == 1)
            { // MembershipPasswordFormat.Hashed 
                HashAlgorithm hm = HashAlgorithm.Create("SHA1");
                if (hm is KeyedHashAlgorithm)
                {
                    KeyedHashAlgorithm kha = (KeyedHashAlgorithm)hm;
                    if (kha.Key.Length == bSalt.Length)
                    {
                        kha.Key = bSalt;
                    }
                    else if (kha.Key.Length < bSalt.Length)
                    {
                        byte[] bKey = new byte[kha.Key.Length];
                        Buffer.BlockCopy(bSalt, 0, bKey, 0, bKey.Length);
                        kha.Key = bKey;
                    }
                    else
                    {
                        byte[] bKey = new byte[kha.Key.Length];
                        for (int iter = 0; iter < bKey.Length; )
                        {
                            int len = Math.Min(bSalt.Length, bKey.Length - iter);
                            Buffer.BlockCopy(bSalt, 0, bKey, iter, len);
                            iter += len;
                        }
                        kha.Key = bKey;
                    }
                    bRet = kha.ComputeHash(bIn);
                }
                else
                {
                    byte[] bAll = new byte[bSalt.Length + bIn.Length];
                    Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length);
                    Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length);
                    bRet = hm.ComputeHash(bAll);
                }
            }

            return Convert.ToBase64String(bRet);
    }
}



دقت کنید تا فضاهای نام System.Text و System.Security.Cryptography را وارد کرده باشید.

متد EncodePassword کلمه عبور را بر اساس پیاده سازی پیش فرض SQL Membership رمزنگاری می‌کند. این الگوریتم از System.Web گرفته می‌شود. اگر اپلیکیشن قدیمی شما از الگوریتم خاصی استفاده می‌کرده است، همینجا باید آن را منعکس کنید. دو متد دیگر نیز بنام‌های HashPassword و VerifyHashedPassword نیاز داریم. این متدها از EncodePassword برای رمزنگاری کلمه‌های عبور و تایید آنها در دیتابیس استفاده می‌کنند.

سیستم SQL Membership برای رمزنگاری (Hash) کلمه‌های عبور هنگام ثبت نام و تغییر آنها توسط کاربران، از PasswordHash, PasswordSalt و PasswordFormat استفاده می‌کرد. در روند مهاجرت، این سه فیلد در ستون PasswordHash جدول AspNetUsers ذخیره شده و با کاراکتر '|' جدا شده اند. هنگام ورود کاربری به سایت، اگر کله عبور شامل این فیلدها باشد از الگوریتم SQL Membership برای بررسی آن استفاده می‌کنیم. در غیر اینصورت از پیاده سازی پیش فرض ASP.NET Identity استفاده خواهد شد. با این روش، کاربران قدیمی لازم نیست کلمه‌های عبور خود را صرفا بدلیل مهاجرت اپلیکیشن ما تغییر دهند.

کلاس UserManager را مانند قطعه کد زیر بروز رسانی کنید.

public UserManager()
            : base(new UserStore<User>(new ApplicationDbContext()))
        {
            this.PasswordHasher = new SQLPasswordHasher();
 }

ایجاد صفحات جدید مدیریت کاربران

قدم بعدی ایجاد صفحاتی است که به کاربران اجازه ثبت نام و ورود را می‌دهند. صفحات قدیمی SQL Membership از کنترل هایی استفاده می‌کنند که با ASP.NET Identity سازگار نیستند. برای ساختن این صفحات جدید به این مقاله مراجعه کنید. از آنجا که در این مقاله پروژه جدید را ساخته ایم و پکیج‌های لازم را هم نصب کرده ایم، می‌توانید مستقیما به قسمت Adding Web Forms for registering users to your application بروید.
چند تغییر که باید اعمال شوند:
  • فایل‌های Register.aspx.cs و Login.aspx.cs از کلاس UserManager استفاده می‌کنند. این ارجاعات را با کلاس UserManager جدیدی که در پوشه Models ساختید جایگزین کنید.
  • همچنین ارجاعات استفاده از کلاس IdentityUser را به کلاس User که در پوشه Models ساختید تغییر دهید.
  • لازم است توسعه دهنده مقدار ApplicationId را برای کاربران جدید طوری تنظیم کند که با شناسه اپلیکیشن جاری تطابق داشته باشد. برای این کار می‌توانید پیش از ساختن حساب‌های کاربری جدید در فایل Register.aspx.cs ابتدا شناسه اپلیکیشن را بدست آورید و اطلاعات کاربر را بدرستی تنظیم کنید.
مثال: در فایل Register.aspx.cs متد جدیدی تعریف کنید که جدول aspnet_Applications را بررسی میکند و شناسه اپلیکیشن را بر اساس نام اپلیکیشن بدست می‌آورد.
private Guid GetApplicationID()
{
    using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["ApplicationServices"].ConnectionString))
            {
                string queryString = "SELECT ApplicationId from aspnet_Applications WHERE ApplicationName = '/'"; //Set application name as in database

                SqlCommand command = new SqlCommand(queryString, connection);
                command.Connection.Open();

                var reader = command.ExecuteReader();
                while (reader.Read())
                {
                    return reader.GetGuid(0);
                }

            return Guid.NewGuid();
        }
}


  حال می‌توانید این مقدار را برای آبجکت کاربر تنظیم کنید.
var currentApplicationId = GetApplicationID();

User user = new User() { UserName = Username.Text,
ApplicationId=currentApplicationId, …};
در این مرحله می‌توانید با استفاده از اطلاعات پیشین وارد سایت شوید، یا کاربران جدیدی ثبت کنید. همچنین اطمینان حاصل کنید که کاربران پیشین در نقش‌های مورد نظر وجود دارند.
مهاجرت به ASP.NET Identity مزایا و قابلیت‌های جدیدی را به شما ارائه می‌کند. مثلا کاربران می‌توانند با استفاده از تامین کنندگان ثالثی مثل Facebook, Google, Microsoft, Twitter و غیره به سایت وارد شوند. اگر به سورس کد این مقاله مراجعه کنید خواهید دید که امکانات OAuth نیز فعال شده اند.
در این مقاله انتقال داده‌های پروفایل کاربران بررسی نشد. اما با استفاده از نکات ذکر شده می‌توانید پروفایل کاربران را هم بسادگی منتقل کنید. کافی است مدل‌های لازم را در پروژه خود تعریف کرده و با استفاده از اسکریپت‌های SQL داده‌ها را انتقال دهید.
مطالب
React 16x - قسمت 4 - کامپوننت‌ها - بخش 1 - کار با عبارات JSX
برپایی پروژه‌ی ایجاد اولین کامپوننت React

در اینجا برای بررسی مقدماتی کامپوننت‌ها، یک پروژه‌ی جدید React را ایجاد می‌کنیم.
> create-react-app sample-04
> cd sample-04
> npm start
اکنون در ادامه اولین کاری را که انجام می‌دهیم، نصب توئیتر بوت استرپ 4 است تا بتوانیم توسط امکانات آن، ظاهر بهتری را برای برنامه‌ی تهیه شده تدارک ببینیم. برای این منظور پس از باز کردن پوشه‌ی اصلی برنامه توسط VSCode، دکمه‌های `+ctrl را فشرده (ctrl+back-tick) و دستور زیر را در ترمینال ظاهر شده وارد کنید:
> npm install --save bootstrap
این دستور علاوه بر نصب بوت استرپ 4.3.1 (آخرین نگارش موجود در زمان نگارش این مطلب)، به دلیل ذکر سوئیچ save، مدخل آن‌را نیز به فایل package.json برنامه اضافه می‌کند.
پس از اجرای این دستور، ممکن است پیام‌های اخطاری مانند «requires a peer of jquery@1.9.1 - 3 but none is installed» را نیز مشاهده کنید که مهم نیستند. چون در اینجا صرفا از امکانات CSS ای بوت استرپ استفاده خواهیم کرد و کاری با jQuery نداریم. محل نصب آن نیز پوشه‌ی node_modules\bootstrap برنامه است.

سپس برای افزودن فایل bootstrap.css به پروژه‌ی React خود، ابتدای فایل index.js را به نحو زیر ویرایش خواهیم کرد:
import "bootstrap/dist/css/bootstrap.css";
این import به صورت خودکار توسط webpack ای که در پشت صحنه کار bundling & minification برنامه را انجام می‌دهد، مورد استفاده قرار می‌گیرد.


ایجاد اولین کامپوننت React

در پوشه‌ی src برنامه، پوشه‌ی جدیدی را به نام components ایجاد می‌کنیم و تمام کامپوننت‌های خود را در آن قرار خواهیم داد. سپس داخل این پوشه، یک فایل جدید و خالی را به نام counter.jsx ایجاد می‌کنیم. پسوند این فایل jsx است و نام فایل‌های کامپوننت‌ها را نیز camel case وارد می‌کنیم؛ یعنی اولین حرف اولین واژه‌ی وارد شده، با حروف کوچک و تمام واژه‌های پس از آن با حروف بزرگ شروع خواهند شد مانند coolApp. مزیت استفاده‌ی از پسوند jsx نسبت به js، فراهم شدن امکانات مخصوص React در VSCode است.
در ابتدای فایل counter.jsx، نیاز است وابستگی‌های React را import کنیم. اگر از قسمت اول بخاطر داشته باشید، «simple react snippets» را نیز در VSCode نصب کردیم. به کمک آن می‌تواند این نوع importها را ساده‌تر وارد کرد. برای این منظور imrc را تایپ کرده و سپس دکمه‌ی tab را فشار دهید.  به این ترتیب یک سطر زیر به صورت خودکار تولید می‌شود:
import React, { Component } from 'react';
پس از این سطر، cc را تایپ کرده و سپس دکمه‌ی tab را فشار دهید تا ساختار کلاس یک کامپوننت React تولید شود. همان لحظه‌ای که این ساختار تولید می‌شود، اگر دقت کنید دو کرسر در صفحه ظاهر شده‌اند که با تایپ نام کامپوننت، نام کلاس و نام export آن‌را تکمیل می‌کنند. نام این کامپوننت را Counter که با حروف بزرگ شروع می‌شود، وارد می‌کنیم. اکنون کدهای آن را به نحو زیر ویرایش می‌کنیم:
import React, { Component } from "react";

class Counter extends Component {
  render() {
    return <h1>Hello world!</h1>;
  }
}

export default Counter;
مفهوم ساختار یک چنین کلاس و export ای را در قسمت قبل با معرفی کلاس‌ها، ارث بری، ماژول‌ها و همچنین exportهای آن‌ها بررسی کردیم. البته در قسمت قبل، export default class را مشاهده کردید و در اینجا بجای آن، سطر آخر این ماژول به export default ختم شده‌است که روش دیگری برای تعریف این export است.
خروجی متد render در اینجا، یک رشته‌ی معمولی نیست؛ بلکه یک عبارت jsx است که در قسمت اول معرفی شد. این عبارت در نهایت توسط کامپایلر Babel به معادل React.createElement ترجمه می‌شود. به همین جهت نیاز است تا import React را در ابتدای این ماژول درج کرد؛ هرچند به ظاهر به صورت مستقیم از آن استفاده نمی‌کنیم.

تا اینجا این کامپوننت در UI برنامه نمایش داده نمی‌شود. به همین جهت به فایل index.js مراجعه کرده و آن‌را به صورت زیر تغییر می‌دهیم:
- ابتدا نیاز است تا شیء Counter را در اینجا import کنیم و چون خروجی پیش‌فرض است، نیازی به ذکر {} برای معرفی آن نیست:
import Counter from "./components/counter";
- سپس در سطر ReactDOM.render، بجای رندر کامپوننت App، کامپوننت Counter را ذکر می‌کنیم:
 ReactDOM.render(<Counter />, document.getElementById("root"));
اکنون برنامه هر زمانیکه به المان جدید Counter برسد، بجای آن به متد render کامپوننت متناظر مراجعه کرده و خروجی آن‌را رندر می‌کند. پس از این تغییر اگر به مرورگر مراجعه کنید، خروجی hello world را مشاهده خواهید کرد.


درج چند عنصر در عبارات JSX

می‌خواهیم در کامپوننت Counter، یک دکمه را نیز نمایش دهیم. برای انجام اینکار، به نحو زیر عمل می‌کنیم:
  render() {
    return <h1>Hello world!</h1><button>Increment</button>;
  }
در این حالت هم در VSCode و هم در کنسول توسعه دهندگان مرورگر، خطای «JSX expressions must have one parent element» ظاهر می‌شود.
عبارات JSX در نهایت باید تبدیل به متد React.createElement شوند. اولین پارامتر این متد، نوع المانی است که قرار است ایجاد شود که در اینجا h1 است. اما در اینجا دو المان را داریم. در این حالت Babel نمی‌داند که چگونه باید یک چنین عبارتی را به React.createElement ترجمه کند. یک راه حل این است که کل این عبارت را داخل یک div قرار داد:
  render() {
    return (
      <div>
        <h1>Hello world!</h1>
        <button>Increment</button>
      </div>
    );
در اینجا فرمت چند سطری return، توسط افزونه‌ی Prettier که در قسمت اول معرفی شد، پس از ذخیره‌ی فایل، به صورت خودکار اعمال شده‌است. همچنین اگر دقت کنید یک () جدید را نیز مشاهده می‌کنید. علت آن مقابله با automatic semicolon insertion است (درج ; خودکار). در جاوا اسکریپت اگر یک return را داشته باشید و پس از آن در همان سطر، چیزی درج نشده باشد، یک سمی‌کالن را به صورت خودکار درج/تفسیر می‌کند. به این ترتیب عبارت JSX چند سطری درج شده‌ی در سطرهای بعد از return، دیده نخواهد شد؛ یعنی چیزی شبیه به عبارات زیر تفسیر می‌شود:
return ;
  <div></div>
برای رفع این مشکل باید دقیقا جلوی واژه‌ی کلیدی return، یک پرانتز را باز کرد و آن‌را پس از خاتمه‌ی عبارت JSX، بست (که البته افزونه‌ی Prettier اینکار را به صورت خودکار برای شما انجام می‌دهد):
return (
  <div></div>
);
نکته 1: بدیهی است زمانیکه المان‌های درج شده را درون یک div محصور کردیم، به همین نحو نیز در DOM اصلی ظاهر خواهند شد. اگر علاقمند نیستید که این div در خروجی نهایی رندر شود، می‌توان بجای آن از React.Fragment استفاده کرد که هیچ نوع المان اضافه‌تری را در DOM بجای آن درج نمی‌کند:
    return (
      <React.Fragment>
        <h1>Hello world!</h1>
        <button>Increment</button>
      </React.Fragment>
    );

نکته 2: در VSCode برای ویرایش همزمان ابتدا و انتهای یک تگ (برای مثال ویرایش همزمان عبارت div در اینجا و تبدیل آن به React.Fragment در دو قسمت)، عبارت آن تگ را انتخاب کرده و سپس دکمه‌های ctrl+d را فشار دهید تا بتوانید همزمان هر دو عبارت انتخاب شده را با هم ویرایش کنید. به اینکار multi-cursor editing می‌گویند.


نمایش پویای اطلاعات در عبارات JSX

در ادامه بجای نمایش عبارت ثابت «Hello world»، می‌خواهیم آن‌را به صورت پویا تنظیم کنیم. برای این منظور یک خاصیت جدید را در کلاس جاری، به نام state تعریف کرده و آن‌را با یک شیء، مقدار دهی می‌کنیم. state، یک خاصیت ویژه در کامپوننت‌های React است و بیانگر داده‌هایی است که آن کامپوننت نیاز دارد. این داده‌ها می‌توانند یک key/value ساده باشند و یا حتی value تعریف شده نیز می‌تواند یک شیء پیچیده باشد.
import React, { Component } from "react";

class Counter extends Component {
  state = {
    count: 0
  };

  render() {
    return (
      <React.Fragment>
        <span>{this.state.count}</span>
        <button>Increment</button>
      </React.Fragment>
    );
  }
}

export default Counter;
در اینجا خاصیت state را با شیءای که حاوی key/value مساوی count با مقدار صفر است، مقدار دهی کرده‌ایم. سپس برای نمایش این اطلاعات در عبارت JSX، از یک {} استفاده می‌شود. داخل {}‌ها می‌توان هر نوع عبارت مجاز جاوا اسکریپتی را درج کرد. برای مثال با this شروع می‌کنیم که بیانگر اشاره‌گری به وهله‌ای از شیء جاری است. سپس می‌توان توسط آن به خاصیت state و سپس کلید count شیء منتسب به آن دسترسی یافت. به این ترتیب عدد صفر، در کنار دکمه‌ای با برچسب Increment، در مرورگر ظاهر خواهد شد.

همانطور که عنوان شد در بین {}‌ها می‌توان هر نوع عبارت مجاز جاوا اسکریپتی را ذکر کرد و عبارت چیزی است که مقداری را بازگشت می‌دهد. بنابراین عبارتی مانند {2+2} را نیز می‌توان در اینجا بکار برد و یا حتی در اینجا می‌توان متدی را فراخوانی کرد که مقداری را بازگشت می‌دهد:
import React, { Component } from "react";

class Counter extends Component {
  state = {
    count: 0
  };

  render() {
    return (
      <React.Fragment>
        <span>{this.formatCount()}</span>
        <button>Increment</button>
      </React.Fragment>
    );
  }

  formatCount() {
    const { count } = this.state; // Object Destructuring
    return count === 0 ? "Zero" : count;
  }
}

export default Counter;
در این مثال می‌خواهیم اگر مقدار count مساوی صفر بود، بجای عدد صفر، واژه‌ی Zero را نمایش دهد. به همین جهت این منطق را به یک متد مانند formatCount منتقل کرده و سپس آن‌را به صورت {()this.formatCount}، فراخوانی کرده و نمایش می‌دهیم.
در متد formatCount حتی می‌توان عبارات JSX را نیز بجای یک رشته‌ی ساده، بازگشت داد:
  formatCount() {
    const { count } = this.state; // Object Destructuring
    return count === 0 ? <h1>Zero</h1> : count;
  }


مقدار دهی ویژگی‌های عناصر در عبارات JSX

فرض کنید یک المان img را به عبارت JSX کلاس Counter اضافه کرده‌ایم. اکنون می‌خواهیم ویژگی src آن‌را مقدار دهی کنیم. در اینجا هر چیزی که بین "" قرار گیرد، به صورت یک رشته‌ی ثابت پردازش می‌شود. برای تنظیم آن به یک متغیر، ابتدا خاصیت state را به صورت زیر جهت درج imageUrl، ویرایش می‌کنیم:
  state = {
    count: 0,
    imageUrl: "/logo192.png"
  };
پس از آن عبارت مقدار خاصیت this.state.imageUrl را توسط یک {}، به ویژگی src تصویر نسبت می‌دهیم:
  render() {
    return (
      <React.Fragment>
        <img src={this.state.imageUrl} alt="" />
        <span>{this.formatCount()}</span>
        <button>Increment</button>
      </React.Fragment>
    );
  }

مقدار دهی class و style المان‌ها، نسبت به مقدار دهی attributes که مشاهده کردید، اندکی متفاوت است؛ از این جهت که در نهایت یک عبارت JSX توسط کامپایلر Babel به معادل جاوا اسکریپتی آن ترجمه می‌شود و اگر در اینجا به عنوان مثال از ویژگی class استفاده شود، چون نام class، یک نام و واژه‌ی کلیدی از پیش معین شده‌ی جاوا اسکریپتی است، امکان استفاده‌ی از آن در اینجا وجود ندارد. به همین جهت در React برای تنظیم ویژگی class عناصر، از className استفاده می‌شود:
    return (
      <React.Fragment>
        <img src={this.state.imageUrl} alt="" />
        <span className="badge badge-primary m-2">{this.formatCount()}</span>
        <button className="btn btn-secondary btn-sm">Increment</button>
      </React.Fragment>
    );
در اینجا اعمال یک سری از کلاس‌های بوت استرپ را که در ابتدای مطلب به پروژه اضافه کردیم، به ویژگی‌های className المان‌های span و button مشاهده می‌کنید.
تا اینجا اگر فایل کامپوننت Counter را ذخیره کنید، خروجی ذیل در مرورگر ظاهر خواهد شد:


روش مقدار دهی ویژگی style نیز متفاوت است. در اینجا React انتظار دارد تا شیءای را که به صورت زیر تشکیل می‌شود:
  styles = {
    fontSize: 50,
    fontWeight: "bold"
  };
به صورت {this.styles} به ویژگی style انتساب دهیم:
    return (
      <React.Fragment>
        <img src={this.state.imageUrl} alt="" />
        <span style={this.styles} className="badge badge-primary m-2">
          {this.formatCount()}
        </span>
        <button className="btn btn-secondary btn-sm">Increment</button>
      </React.Fragment>
    );
نحوه‌ی تشکیل خاصیت styles، بر اساس ذکر خواص CSS، به صورت خواصی camel-case است؛ مانند fontSize. در اینجا عدد 50 توسط react به صورت خودکار به 50px تبدیل می‌شود.
اعمال این styles نمونه، یک چنین خروجی را به همراه خواهد داشت:


مزیت تعریف شیء styles به صورت یک خاصیت در کلاس، امکان استفاده‌ی مجدد از آن در سایر المان‌ها است. اگر چنین چیزی مدنظر شما نیست، می‌توان این شیء را به صورت inline هم تعریف کرد:
<button style={{ fontSize: 30 }} className="btn btn-secondary btn-sm">
در اینجا، ابتدا یک {} درج می‌شود تا بیانگر نمایش دریافت یک عبارت معتبر جاوا اسکریپتی باشد. سپس داخل آن یک {} دیگر نیز قرار گرفته‌است که بیانگر تعریف یک شیء جاوا اسکریپتی است و در این حالت باید با نحوه‌ی تشکیل عناصر شیء style مورد نظر React که به صورت caml-case هستند، تطابق داشته باشد.


مقدار دهی پویای ویژگی className عناصر در عبارات JSX

در ادامه می‌خواهیم اگر مقدار count مساوی صفر بود، span ای که هم اکنون با یک badge آبی (با کلاس badge-primary) نمایش داده می‌شود، زرد رنگ (با کلاس badge-warning) شود و در غیراینصورت آبی رنگ. بنابراین می‌خواهیم بر اساس مقدر count، مقدار کلاس‌های انتسابی به className را به صورت پویا تغییر دهیم و این الگویی است که در برنامه‌های واقعی بسیار استفاده می‌شود:
  render() {
    let classes = "badge m-2 badge-";
    classes += this.state.count === 0 ? "warning" : "primary";

    return (
      <React.Fragment>
        <img src={this.state.imageUrl} alt="" />
        <span style={this.styles} className={classes}>
          {this.formatCount()}
        </span>
        <button style={{ fontSize: 30 }} className="btn btn-secondary btn-sm">
          Increment
        </button>
      </React.Fragment>
    );
برای این منظور متغیر classes را تعریف کرده‌ایم که در ابتدا با مقادیری که ثابت هستند، مقدار دهی شده‌اند. سپس بر اساس مقدار this.state.count، مقدار مشخص warning و یا primary به این رشته افزوده می‌شود. در آخر هم از این متغیر به صورت className={classes} استفاده شده‌است؛ با این خروجی:


البته باید دقت داشت که می‌توان منطق تشکیل متغیر classes را به یک متد، جهت خلوت سازی متد render نیز منتقل کرد. برای این کار، دو سطر مرتبط با متغیر classes را در VSCode انتخاب کنید. سپس یک آیکن لامپ مانند ظاهر می‌شود که با کلیک بر روی آن، منوی extract to method نیز قابل انتخاب است:
  render() {
    let classes = this.getBadgeClasses();
    // ...
  }

  getBadgeClasses() {
    let classes = "badge m-2 badge-";
    classes += this.state.count === 0 ? "warning" : "primary";
    return classes;
  }
البته در اینجا می‌توان متغیر classes را نیز حذف کرد و مستقیما متد getBadgeClasses را مورد استفاده قرار داد:
<span style={this.styles} className={this.getBadgeClasses()}>


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: sample-04-part01.zip
مطالب دوره‌ها
تامین مقادیر پارامترها در حین نگاشت‌های AutoMapper
متد Project To را می‌توان به عنوان متد پیش فرض حین کار با ORMها درنظر گرفت؛ با این مزایا:
- جلوگیری از Lazy loading اشتباه
- کاهش تعداد فیلدهای بازگشت داده شده‌ی از دیتابیس و محدود ساختن آن‌ها به خواصی که قرار است نگاشت شوند. در حالت معمولی استفاده‌ی از متد Mapper.Map، تمام فیلدهای مدل بارگذاری شده و سپس در سمت کلاینت توسط AutoMapper نگاشت خواهند شد. اما در حالت استفاده‌ی از متد ویژه‌ی Project To، کوئری SQL ارسالی به بانک اطلاعاتی نیز مطابق نگاشت تعریف شده، تغییر کرده و خلاصه خواهد شد.

در این حالت یک چنین سناریویی را درنظر بگیرید. مدل متناظر با جدول بانک اطلاعاتی ما چنین ساختاری را دارد:
public class UserModel
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
و اطلاعاتی که قرار است در رابط کاربری نمایش داده شوند، به این شکل تعریف شده‌اند:
public class UserViewModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string UserIdentityName { get; set; }
}
در اینجا خاصیت UserIdentityName قرار است در زمان اجرا، برای مثال توسط مقدار User.Identity.Name تامین شود و در حالت کلی، خاصیت یا خاصیت‌های ثابتی را داریم که نیاز است در حین نگاشت انجام شده، در زمان اجرا مقدار ثابت خود را دریافت کنند.


تعریف نگاشت‌های پارامتری

برای حل این مساله، از روش زیر استفاده می‌شود:
 string userIdentityName = null;
this.CreateMap<UserModel, UserViewModel>()
 .ForMember(d => d.UserIdentityName, opt => opt.MapFrom(src => userIdentityName));
ابتدا یک متغیر خالی را تعریف می‌کنیم. از آن جهت تهیه‌ی یک lambda expression صحیح در قسمت MapFrom استفاده خواهیم کرد. کار این متغیر خالی، تهیه‌ی یک عبارت جایگزین شونده‌ی در زمان اجرا است.
اکنون جهت استفاده‌ی از این متغیر با قابلیت جایگزینی، می‌توان به نحو ذیل عمل کرد:
var uiUsers = users.AsQueryable()
                   .Project()
                   .To<UserViewModel>(new { userIdentityName = "User.Identity.Name Value Here" })
                   .ToList();
در اینجا لیست کاربران بانک اطلاعاتی، به لیست UserViewModel‌ها نگاشت شده و همچنین مقدار خاصیت UserIdentityName آن‌ها نیز از پارامتری که به متد Project To ارسال گردیده‌است، تامین خواهد شد.


کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید.