نظرات مطالب
سفارشی سازی صفحه‌ی اول برنامه‌های Angular CLI توسط ASP.NET Core
در پروژه سفارشی سازی DNTIdentity  اومدید برای پیاده سازی دسترسی‌ها و نمایش منو‌ها به کاربر از تگ هلپر  SecurityTrimming   استفاده کردین  توی همون پروژه اگه بخوایم همچین چیزی رو با انگولار پیاده سازی کنیم پیشنهادتون چیه ؟ استفاده کردن تگ هلپر در انگولار مثل این پروژه https://github.com/RobertDyball/A2SPA   ( تو readme  لینکاشو نگاه کنید) یا روش دیگه ای سراغ دارین؟
توی همین پروژه DNTIdentity توی پارشیال ویو _UserMenu.cshtml   با razor اومدید  @inject IUsersPhotoService PhotoService
@{
    var displayName = User.Identity.GetUserDisplayName();
    var photoUrl = PhotoService.GetCurrentUserPhotoUrl();
}
 displayName کاربری که لاگین کرده رو گرفتید برای همچین کاری تو angular چیکار باید کرد؟ ممنون
مطالب
مقایسه بین Proxy و ChannelFactory در WCF
برای ساخت یک WCF Client یا دسترسی به یک سرویس WCF دو راه وجود دارد.
  • استفاده از WCF Proxy
  • استفاده از ChannelFactory

قصد دارم طی یک مقایسه کوتاه این دو روش را بررسی کنیم:

WCF Proxy:

Proxy در واقع یک کلاس CLR است که به عنوان نماینده یک اینترفیس که از نوع  Service Contract است مورد استفاده قرار می‌گیرد. یا به زبان ساده تر، یک Proxy در واقع نماینده Service Contract ای که سمت سرور پیاده سازی شده است در سمت کلاینت خواهد بود. Proxy تمام متد یا Operation Contract‌های سمت سرور را داراست به همراه یک سری متد‌ها و خواص دیگر برای مدیریت چرخه طول عمر سرویس،  هم چنین اطلاعات مربوط به وضعیت سرویس و نحوه اتصال آن به سرور. ساخت Proxy به دو روش امکان پذیر است:

  • با استفاده از امکانات AddServiceReference موجود در Visual Studio. کافیست از پنجره معروف زیر با استفاده از یک URL سرویس مورد نظر را به پروژه سمت کلاینت خود اضافه نمایید

همچنین  می‌توانید از قسمت Advanced نیز برای تنظیمات خاص مورد نظر خود(مثل تولید کد برای متد‌های Async یا تعیین نوع Collection‌ها در هنگام انتقال داده و ...) استفاده نمایید.

  • با استفاده از SvcUtil.exe . کاربرد svcutil.exe در موارد Metadata Export، Service Validtation، XmlSerialization Type Generator و Metadata Download و ... خلاصه می‌شود. با استفاده از Vs.Net Command Promptو svcutil می‌توان به سرویس مورد نظر دسترسی داشت.مثال
    svcutil.exe /language:vb /out:generatedProxy.vb /config:app.config http://localhost:8000/ServiceModelSamples/service

ChannelFactory:

ChannelFactory یک کلاس تعبیه شده در دات نت می‌باشد که به وسیله یک اینترفیس که به عنوان تعاریف سرویس سمت سرور است یک نمونه از سرویس مورد نظر را برای ما خواهد ساخت. اما به خاظر داشته باشید از این روش زمانی می‌توان استفاده کرد که دسترسی کامل به سمت سرور و کلاینت داشته باشید.

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

مثال:

public static TChannel CreateChannel()
        {
            IBookService service;

            var endPointAddress = new EndpointAddress( "http://localhost:7000/service.svc" );

            var httpBinding = new BasicHttpBinding();
            
            ChannelFactory<TChannel> factory = new ChannelFactory<TChannel>( httpBinding, endPointAddress );

            instance= factory.CreateChannel();

            return instance;
        }
همان طور که مشاهده می‌کنید در این روش نیاز به یک EndpointAddress به همراه یک نمونه از نوع Binding مورد نظر دارید. نوع این Binding حتما باید با نوع نمونه ساخته شده در سمت سرور که برای هاست کردن سرویس‌ها مورد استفاده قرار گرفته است یکی باشد. این نوع‌ها می‌تواند شامل NetTcpBidning ،WShttpBinding  BasicHttpBinding ،WSDualHttpBinding، MSMQ Binding و البته چند نوع دیگر نیز باشد.
در نتیجه برای ساخت یک سرویس به روش ChannelFactory باید مراحل زیر را طی نمایید:
  • یک نمونه از WCF Binding بسازید
  • یک نمونه از کلاس EndPointAddress به همراه آدرس سرویس مورد نظر در سمت سرور بسازید(البته می‌توان این مرحله را نادیده گرفت و آدرس سرویس را مستقیما به نمونه ChannelFactory به عنوان پارامتر پاس داد)
  • یک نمونه از کلاس ChannelFactory یا استفاده از EndPointAddress بسازید
  • با استفاده از ChannelFactory یک نمونه از Channel مورد نظر را فراخوانی نمایید(فراخوانی متد CreateChannel)

تفاوت‌های دو روش

Proxy
 ChannelFactory
فقط نیاز به یک URL برای ساخت سرویس مورد نظر دارد. بقیه مراحل توسط ابزار‌های مرتبط انجام خواهد شد  
 شما نیاز به دسترسی مستقیم به اسمبلی حاوی Service Contract پروژه خود دارید.
 استفاده از این روش بسیار آسان و ساده است
 پیاده سازی آن پیچیدگی بیشتر دارد
فهم مفاهیم این روش بسیار راحت است
نیاز به دانش اولیه از مفاهیم WCF برای پیاده سازی دارد
 زمانی که میزان تغییرات در کلاس‌های مدل و Entity‌ها زیاد باشد این روش بسیار موثر است.(مدیریت تغییرات در WCF)
 زمانی که اطمینان دارید که مدل و entity‌ها پروژه زیاد تغییر نخواهند کرد و از طرفی نیاز به کد نویسی کمتر در سمت کلاینت دارید، این روش موثرتر خواهد بود
 فقط به اینترفیس هایی که دارای ServiceContractAttribute هستند دسترسی خواهیم داشت.
به تمام اینترفیس‌های تعریف شده در بخش  Contracts دسترسی داریم.
 فقط به متد‌های که دارای OperationContractAttribute هستند دسترسی خواهیم داشت.    به تمام متد‌های عمومی سرویس دسترسی داریم.  

آیا می‌توان از روش AddServiceReference تعبیه شده در Vs.Net، برای ساخت ChannelFactory استفاده کرد؟

بله! کافیست هنگام ساخت سرویس، در پنجره AddServiceReference از قسمت Advanced وارد برگه تنظیمات شوید. سپس تیک مربوط به قسمت های  Reused Type in referenced assemblies  و Reused Types in specified referenced assemblies را بزنید. بعد از لیست پایین، اسمبلی‌های مربوط به Domain Model و هم چنین Contract‌های سمت سرور را انتخاب نمایید. در این حالت شما از روش Channel Factory برای ساخت سرویس WCF استفاده کرده اید.

مطالب
ASP.NET MVC #2

MVC‌ چیست و اساس کار آن چگونه است؟

الگوی MVC در سال‌های اول دهه 70 میلادی در شرکت زیراکس توسط خالقین زبان اسمال‌تاک که جزو اولین زبان‌های شیءگرا محسوب می‌شود، ارائه گردید. نام MVC از الگوی Model-View-Controller گرفته شده و چندین دهه است که در صنعت تولید نرم افزار مورد استفاده می‌باشد. هدف اصلی آن جدا سازی مسئولیت‌های اجزای تشکیل دهنده «لایه نمایشی» برنامه است.
این الگو در سال 2004 برای اولین بار در سکویی به نام Rails به کمک زبان روبی جهت ساخت یک فریم ورک وب MVC مورد استفاده قرار گرفت و پس از آن به سایر سکوها مانند جاوا، دات نت (در سال 2007)، PHP و غیره راه یافت.
تصاویری را از این تاریخچه در ادامه ملاحظه می‌کنید؛ از دکتر Trygve Reenskaug تا شرکت زیراکس و معرفی آن در Rails به عنوان اولین فریم ورک وب MVC.


C در MVC معادل Controller است. کنترلر قسمتی است که کار دریافت ورودی‌های دنیای خارج را به عهده دارد؛ مانند پردازش یک درخواست HTTP ورودی.


زمانیکه کنترلر این درخواست را دریافت می‌کند، کار وهله سازی Model را عهده دار خواهد شد و حاوی اطلاعاتی است که نهایتا در اختیار کاربر قرار خواهد گرفت تا فرآیند پردازش درخواست رسیده را تکمیل نماید. برای مثال اگر کاربری جهت دریافت آخرین اخبار به سایت شما مراجعه کرده است،‌ در اینجا کار تهیه لیست اخبار بر اساس مدل مرتبط به آن صورت خواهد گرفت. بنابراین کنترلرها، پایه اصلی و مدیر ارکستر الگوی MVC محسوب می‌شوند.
در ادامه، کنترلر یک View را جهت نمایش Model انتخاب خواهد کرد. View در الگوی MVC یک شیء ساده است. به آن می‌توان به شکل یک قالب که اطلاعاتی را از Model دریافت نموده و سپس آن‌ها را در مکان‌های مناسبی در صفحه قرار می‌دهد، نگاه کرد.
نتیجه استفاده از این الگو، ایزوله سازی سه جزء یاد شده از یکدیگر است. برای مثال View نمی‌داند و نیازی ندارد که بداند چگونه باید از لایه دسترسی به اطلاعات کوئری بگیرد. یا برای مثال کنترلر نیازی ندارد بداند که چگونه و در کجا باید خطایی را با رنگی مشخص نمایش دهد. به این ترتیب انجام تغییرات در لایه رابط کاربری برنامه در طول توسعه کلی سیستم، ساده‌تر خواهد شد.
همچنین در اینجا باید اشاره کرد که این الگو مشخص نمی‌کند که از چه نوع فناوری دسترسی به اطلاعاتی باید استفاده شود. می‌توان از بانک‌های اطلاعاتی، وب سرویس‌ها، صف‌ها و یا هر نوع دیگری از اطلاعات استفاده کرد. به علاوه در اینجا در مورد نحوه طراحی Model نیز قیدی قرار داده نشده است. این الگو تنها جهت ساخت بهتر و اصولی «رابط کاربری» طراحی شده است و بس.



تفاوت مهم پردازشی ASP.NET MVC با ASP.NET Web forms

اگر پیشتر با ASP.NET Web forms کار کرده باشید اکنون شاید این سؤال برایتان وجود داشته باشد که این سیستم جدید در مقایسه با نمونه قبلی، چگونه درخواست‌ها را پردازش می‌کند.


همانطور که مشاهده می‌کنید، در وب فرم‌ها زمانیکه درخواستی دریافت می‌شود، این درخواست به یک فایل موجود در سیستم مثلا default.aspx ارسال می‌گردد. سپس ASP.NET یک کلاس وهله سازی شده معرف آن صفحه را ایجاد کرده و آن‌را اجرا می‌کند. در اینجا چرخه طول عمر صفحه مانند page_load و غیره رخ خواهد داد. جهت انجام این وهله سازی، View به فایل code behind خود گره خورده است و جدا سازی خاصی بین این دو وجود ندارد. منطق صفحه به markup آن که معادل است با یک فایل فیزیکی بر روی سیستم، کاملا مقید است. در ادامه، این پردازش صورت گرفته و HTML نهایی تولیدی به مرورگر کاربر ارسال خواهد شد.
در ASP.NET MVC این نحوه پردازش تغییر کرده است. در اینجا ابتدا درخواست رسیده به یک کنترلر هدایت می‌شود و این کنترلر چیزی نیست جز یک کلاس مجزا و مستقل از هر نوع فایل ASPX ایی در سیستم. سپس این کنترلر کار پردازش درخواست رسیده را شروع کرده، اطلاعات مورد نیاز را جمع آوری و سپس به View ایی که انتخاب می‌کند، جهت نمایش نهایی ارسال خواهد کرد. در اینجا View این اطلاعات را دریافت کرده و نهایتا در اختیار کاربر قرار خواهد داد.



آشنایی با قرارداد یافتن کنترلرهای مرتبط

تا اینجا دریافتیم که نحوه پردازش درخواست‌ها در ASP.NET MVC بر مبنای کلاس‌ها و متدها است و نه بر مبنای فایل‌های فیزیکی موجود در سیستم. اگر درخواستی به سیستم ارسال می‌شود، در ابتدا، این درخواست جهت پردازش، به یک متد عمومی موجود در یک کلاس کنترلر هدایت خواهد شد و نه به یک فایل فیزیکی ASPX (برخلاف وب فرم‌ها).


همانطور که در تصویر مشاهده می‌کنید، در ابتدای پردازش یک درخواست، آدرسی به سیستم ارسال خواهد شد. بر مبنای این آدرس، نام کنترلر که در اینجا زیر آن خط قرمز کشیده شده است، استخراج می‌گردد (برای مثال در اینجا نام این کنترلرProducts است). سپس فریم ورک به دنبال کلاس این کنترلر خواهد گشت. اگر آن‌را در اسمبلی پروژه بیابد، از آن خواهد خواست تا درخواست رسیده را پردازش کند؛ در غیراینصورت پیغام 404 یا یافت نشد، به کاربر نمایش داده می‌شود.
اما فریم ورک چگونه این کلاس کنترلر درخواستی را پیدا می‌کند؟
در زمان اجرا، اسمبلی اصلی پروژه به همراه تمام اسمبلی‌هایی که به آن ارجاعی دارند جهت یافتن کلاسی با این مشخصات اسکن خواهند شد:
1- این کلاس باید عمومی باشد.
2- این کلاس نباید abstract باشد (تا بتوان آن‌را به صورت خودکار وهله سازی کرد).
3- این کلاس باید اینترفیس استاندارد IController را پیاده سازی کرده باشد.
4- و نام آن باید مختوم به کلمه Controller باشد (همان مبحث Convention over configuration یا کار کردن با یک سری قرار داد از پیش تعیین شده).

برای مثال در اینجا فریم ورک به دنبال کلاسی به نام ProductsController خواهد گشت.
شاید تعدادی از برنامه نویس‌های ASP.NET MVC تصور ‌کنند که فریم ورک در پوشه‌ی استانداردی به نام Controllers به دنبال این کلاس خواهد گشت؛ اما در عمل زمانیکه برنامه کامپایل می‌شود، پوشه‌ای در این اسمبلی وجود نخواهد داشت و همه چیز از طریق Reflection مدیریت خواهد شد.

نظرات اشتراک‌ها
اطلاعات خود در مورد ارث‌بری را محک بزنید
وقتی که Default Implementation قرار دهیم برای متد‌ها در اینترفیس و موقع نمونه سازی از کلاس مربوطه، object reference آن را در یک متغیر از جنس اینترفیسی که پیاده سازی کرده است قرار دهیم، همان پیاده سازی‌های پیشفرض اجرا میشوند.
در اصل به صورت Explicit Cast کار میکند ( برای مثال فرخوانی یک متد Virtual از کلاس پدر با وجود اینکه همان متد را خودمان یکبار override ) کرده ایم.
اشتراک‌ها
gRPC + ASP.NET Core به عنوان جایگزینی برای WCF در NET Core.

gRPC + ASP.NET Core as a Migration Path for WCFs in .NET Core 

Feature WCF ASP.Net Core + gRPC
Platforms Windows Windows, Linux, MacOS
Protocols LRPC/Named Pipes/HTTP/TCP/MSMQ Binary (GRPC) + HTTP2

(TCP/Named Pipes/LRPC)

.AddProtocol(“ncacn_ip_tcp”, “8080”)

.AddProtocol(“ncacn_np”, @”\pipe\MyService”)

.AddProtocol(“ncalrpc”, “MyService”)

By removing the ASP.NET Core stack and just using .NET Core

Injected Aspects Behaviors ASP.NET Core DI Middleware/ gRPC interceptors
Distributed Transactions *Yes – [TransactionFlow], transactionscopes, and supported bindings *No
Transport Security SSL/TLS SSL/TLS
Message Security Certificates/credentials Certificates/credentials

https://docs.microsoft.com/en-us/aspnet/core/grpc/authn-and-authz?view=aspnetcore-3.0

Windows Authentication Kerberos/NTLM AAD Sync/ASFS + ASP.NET Core middleware
Proxies/Contracts Service Contracts/Data Contracts Protocol Buffers
Proxy-less Communication WCF Channel Factory † Protobuf-Net.GRPC
gRPC + ASP.NET Core به عنوان جایگزینی برای WCF در NET Core.
مسیرراه‌ها
ASP.NET MVC
              مطالب
              استفاده از Fluent Validation در برنامه‌های ASP.NET Core - قسمت اول - معرفی، نصب و تعریف قواعد اعتبارسنجی
              روش مرسوم اعتبارسنجی اطلاعات مدل‌های ASP.NET Core، با استفاده از data annotations توکار آن است که در بسیاری از موارد هم به خوبی کار می‌کند. اما اگر به دنبال ویژگی‌های دیگری مانند نوشتن آزمون‌های واحد برای اعتبارسنجی اطلاعات، جداسازی شرط‌های اعتبارسنجی از تعاریف مدل‌ها، نوشتن اعتبارسنجی‌های پیچیده به همراه تزریق وابستگی‌ها هستید، کتابخانه‌ی FluentValidation می‌تواند جایگزین بهتر و بسیار کاملتری باشد.


              نصب کتابخانه‌ی FluentValidation در پروژه

              فرض کنید پروژه‌ی ما از سه پوشه‌ی FluentValidationSample.Web، FluentValidationSample.Models و FluentValidationSample.Services تشکیل شده‌است که اولی یک پروژه‌ی MVC است و دو مورد دیگر classlib هستند.
              در پروژه‌ی FluentValidationSample.Models، بسته‌ی نیوگت کتابخانه‌ی FluentValidation را به صورت زیر نصب می‌کنیم:
              dotnet add package FluentValidation.AspNetCore


              جایگزین کردن سیستم اعتبارسنجی مبتنی بر DataAnnotations با FluentValidation

              اکنون فرض کنید در پروژه‌ی Models، مدل ثبت‌نام زیر را اضافه کرده‌ایم که از همان data annotations توکار و استاندارد ASP.NET Core برای اعتبارسنجی اطلاعات استفاده می‌کند:
              using System.ComponentModel.DataAnnotations;
              
              namespace FluentValidationSample.Models
              {
                  public class RegisterModel
                  {
                      [Required]
                      [Display(Name = "User name")]
                      public string UserName { get; set; }
              
                      [Required]
                      [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
                      [DataType(DataType.Password)]
                      [Display(Name = "Password")]
                      public string Password { get; set; }
              
                      [DataType(DataType.Password)]
                      [Display(Name = "Confirm password")]
                      [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
                      public string ConfirmPassword { get; set; }
              
                      [DataType(DataType.EmailAddress)]
                      [Display(Name = "Email")]
                      [EmailAddress]
                      public string Email { get; set; }
              
                      [Range(18, 60)]
                      [Display(Name = "Age")]
                      public int Age { get; set; }
                  }
              }
              برای جایگزین کردن data annotations اعتبارسنجی اطلاعات با روش FluentValidation، می‌توان به صورت زیر عمل کرد:
              using FluentValidation;
              
              namespace FluentValidationSample.Models
              {
                  public class RegisterModelValidator : AbstractValidator<RegisterModel>
                  {
                      public RegisterModelValidator()
                      {
                          RuleFor(x => x.UserName).NotNull();
                          RuleFor(x => x.Password).NotNull().Length(6, 100);
                          RuleFor(x => x.ConfirmPassword).Equal(x => x.Password);
                          RuleFor(x => x.Email).EmailAddress();
                          RuleFor(x => x.Age).InclusiveBetween(18, 60);
                      }
                  }
              }
              برای این منظور ابتدا یک کلاس Validator را با ارث بری از AbstractValidator از نوع مدلی که می‌خواهیم قواعد اعتبارسنجی آن‌را مشخص کنیم، ایجاد می‌کنیم. سپس در سازنده‌ی آن، می‌توان به متدهای تعریف شده‌ی در این کلاس پایه دسترسی یافت.
              در اینجا در ابتدا به ازای هر خاصیت کلاس مدل مدنظر، یک RuleFor تعریف می‌شود که با استفاده از static reflection، امکان تعریف strongly typed آن‌ها وجود دارد. سپس ویژگی Required به متد NotNull تبدیل می‌شود و ویژگی StringLength توسط متد Length قابل تعریف خواهد بود و یا ویژگی Compare توسط متد Equal به صورت strongly typed به خاصیت دیگری متصل می‌شود.

              پس از این تعاریف، می‌توان ویژگی‌های اعتبارسنجی اطلاعات را از مدل ثبت نام حذف کرد و تنها ویژگی‌های خاص Viewهای MVC را در صورت نیاز باقی گذاشت:
              using System.ComponentModel.DataAnnotations;
              
              namespace FluentValidationSample.Models
              {
                  public class RegisterModel
                  {
                      [Display(Name = "User name")]
                      public string UserName { get; set; }
              
                      [DataType(DataType.Password)]
                      [Display(Name = "Password")]
                      public string Password { get; set; }
              
                      [DataType(DataType.Password)]
                      [Display(Name = "Confirm password")]
                      public string ConfirmPassword { get; set; }
              
                      [DataType(DataType.EmailAddress)]
                      [Display(Name = "Email")]
                      public string Email { get; set; }
              
                      [Display(Name = "Age")]
                      public int Age { get; set; }
                  }
              }


              تعریف پیام‌های سفارشی اعتبارسنجی

              روش تعریف پیام‌های سفارشی شکست اعتبارسنجی اطلاعات را توسط متد WithMessage در ادامه مشاهده می‌کنید:
              using FluentValidation;
              
              namespace FluentValidationSample.Models
              {
                  public class RegisterModelValidator : AbstractValidator<RegisterModel>
                  {
                      public RegisterModelValidator()
                      {
                          RuleFor(x => x.UserName)
                              .NotNull()
                                  .WithMessage("Your first name is required.")
                              .MaximumLength(20)
                                  .WithMessage("Your first name is too long!")
                              .MinimumLength(3)
                                  .WithMessage(registerModel => $"Your first name `{registerModel.UserName}` is too short!");
              
                          RuleFor(x => x.Password)
                              .NotNull()
                                  .WithMessage("Your password is required.")
                              .Length(6, 100);
              
                          RuleFor(x => x.ConfirmPassword)
                              .NotNull()
                                  .WithMessage("Your confirmation password is required.")
                              .Equal(x => x.Password)
                                  .WithMessage("The password and confirmation password do not match.");
              
                          RuleFor(x => x.Email).EmailAddress();
                          RuleFor(x => x.Age).InclusiveBetween(18, 60);
                      }
                  }
              }
              به ازای هر متد تعریف یک قاعده‌ی اعتبارسنجی جدید، بلافاصله می‌توان از متد WithMessage نیز استفاده کرد. همچنین این متد می‌تواند به اطلاعات اصل model دریافتی نیز همانند پیام سفارشی مرتبط با MinimumLength نام کاربری، دسترسی پیدا کند.


              روش تعریف اعتبارسنجی‌های سفارشی خواص مدل

              فرض کنید می‌خواهیم یک کلمه‌ی عبور وارد شده‌ی معتبر، حتما از جمع حروف کوچک، بزرگ، اعداد و symbols تشکیل شده باشد. برای این منظور می‌توان از متد Must استفاده کرد:
              using System.Text.RegularExpressions;
              using FluentValidation;
              
              namespace FluentValidationSample.Models
              {
                  public class RegisterModelValidator : AbstractValidator<RegisterModel>
                  {
                      public RegisterModelValidator()
                      {
                          RuleFor(x => x.Password)
                              .NotNull()
                                  .WithMessage("Your password is required.")
                              .Length(6, 100)
                              .Must(password => hasValidPassword(password));
                          //...
              
                      }
              
                      private static bool hasValidPassword(string password)
                      {
                          var lowercase = new Regex("[a-z]+");
                          var uppercase = new Regex("[A-Z]+");
                          var digit = new Regex("(\\d)+");
                          var symbol = new Regex("(\\W)+");
                          return lowercase.IsMatch(password) &&
                                  uppercase.IsMatch(password) &&
                                  digit.IsMatch(password) &&
                                  symbol.IsMatch(password);
                      }
                  }
              }
              متد Must، می‌تواند مقدار خاصیت متناظر را نیز در اختیار ما قرار دهد و بر اساس آن مقدار می‌توان خروجی true/false ای را بازگشت داد تا نشان شکست و یا موفقیت آمیز بودن اعتبارسنجی اطلاعات باشد.

              البته lambda expression نوشته شده را می‌توان توسط method groups، به صورت زیر نیز خلاصه نوشت:
              RuleFor(x => x.Password)
                  .NotNull()
                      .WithMessage("Your password is required.")
                  .Length(6, 100)
                  .Must(hasValidPassword);


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

              اگر نیاز به استفاده‌ی از متد hasValidPassword در کلاس‌های دیگری نیز وجود دارد، می‌توان اینگونه اعتبارسنجی‌های سفارشی را به کلاس‌های مجزایی نیز تبدیل کرد. برای مثال فرض کنید که می‌خواهیم ایمیل دریافت شده، فقط از یک دومین خاص قابل قبول باشد.
              using System;
              using FluentValidation;
              using FluentValidation.Validators;
              
              namespace FluentValidationSample.Models
              {
                  public class EmailFromDomainValidator : PropertyValidator
                  {
                      private readonly string _domain;
              
                      public EmailFromDomainValidator(string domain)
                          : base("Email address {PropertyValue} is not from domain {domain}")
                      {
                          _domain = domain;
                      }
              
                      protected override bool IsValid(PropertyValidatorContext context)
                      {
                          if (context.PropertyValue == null) return false;
                          var split = context.PropertyValue.ToString().Split('@');
                          return split.Length == 2 && split[1].Equals(_domain, StringComparison.OrdinalIgnoreCase);
                      }
                  }
              }
              برای این منظور یک کلاس جدید را با ارث‌بری از PropertyValidator تعریف شده‌ی در فضای نام FluentValidation.Validators، ایجاد می‌کنیم. سپس متد IsValid آن‌را بازنویسی می‌کنیم تا برای مثال ایمیل‌ها را صرفا از دومین خاصی بپذیرد.
              PropertyValidatorContext امکان دسترسی به نام و مقدار خاصیت در حال اعتبارسنجی را میسر می‌کند. همچنین مقدار کل model جاری را نیز به صورت یک object در اختیار ما قرار می‌دهد.

              اکنون برای استفاده‌ی از آن می‌توان از متد SetValidator استفاده کرد:
              RuleFor(x => x.Email)
                  .SetValidator(new EmailFromDomainValidator("gmail.com"));
              و یا حتی می‌توان یک متد الحاقی fluent را نیز برای آن طراحی کرد تا SetValidator را به صورت خودکار فراخوانی کند:
                  public static class CustomValidatorExtensions
                  {
                      public static IRuleBuilderOptions<T, string> EmailAddressFromDomain<T>(
                          this IRuleBuilder<T, string> ruleBuilder, string domain)
                      {
                          return ruleBuilder.SetValidator(new EmailFromDomainValidator(domain));
                      }
                  }
              سپس تعریف قاعده‌ی اعتبارسنجی ایمیل‌ها به صورت زیر تغییر می‌کند:
              RuleFor(x => x.Email).EmailAddressFromDomain("gmail.com");


              تعریف قواعد اعتبارسنجی خواص تو در تو و لیستی

              فرض کنید به RegisterModel این قسمت، دو خاصیت آدرس و شماره تلفن‌ها نیز اضافه شده‌است که یکی به شیء آدرس و دیگری به مجموعه‌ای از آدرس‌ها اشاره می‌کند:
                  public class RegisterModel
                  {
                      // ...
              
                      public Address Address { get; set; }
              
                      public ICollection<Phone> Phones { get; set; }
                  }
              
                  public class Phone
                  {
                      public string Number { get; set; }
                      public string Description { get; set; }
                  }
              
                  public class Address
                  {
                      public string Location { get; set; }
                      public string PostalCode { get; set; }
                  }
              در یک چنین حالتی، ابتدا به صورت متداول، قواعد اعتبارسنجی Phone و Address را جداگانه تعریف می‌کنیم:
                  public class PhoneValidator : AbstractValidator<Phone>
                  {
                      public PhoneValidator()
                      {
                          RuleFor(x => x.Number).NotNull();
                      }
                  }
              
                  public class AddressValidator : AbstractValidator<Address>
                  {
                      public AddressValidator()
                      {
                          RuleFor(x => x.PostalCode).NotNull();
                          RuleFor(x => x.Location).NotNull();
                      }
                  }
              سپس برای تعریف اعتبارسنجی دو خاصیت پیچیده‌ی اضافه شده، می‌توان از همان متد SetValidator استفاده کرد که اینبار پارامتر ورودی آن، نمونه‌ای از AbstractValidator‌های هرکدام است. البته برای خاصیت مجموعه‌ای اینبار باید با متد RuleForEach شروع کرد:
                  public class RegisterModelValidator : AbstractValidator<RegisterModel>
                  {
                      public RegisterModelValidator()
                      {
                          // ...
              
                          RuleFor(x => x.Address).SetValidator(new AddressValidator());
              
                          RuleForEach(x => x.Phones).SetValidator(new PhoneValidator());
                      }


              در قسمت بعد، روش‌های مختلف استفاده‌ی از قواعد اعتبارسنجی تعریف شده را در یک برنامه‌ی ASP.NET Core بررسی می‌کنیم.



              برای مطالعه‌ی بیشتر
              - «FluentValidation #1»
              نظرات مطالب
              اعتبارسنجی مبتنی بر JWT در ASP.NET Core 2.0 بدون استفاده از سیستم Identity
              « ... حالا بعد از اعمال روش ارائه شده در این مطلب (ذخیره‌سازی token و refresh token در دیتابیس) چطور می‌توانیم کاربرانی که از توکن قبلی استفاده می‌کنند را مجبور به Sign out کنیم؟  ...»
              همان قسمت «تهیه یک اعتبارسنج توکن سفارشی» مطلب جاری هست که از نتیجه‌ی «پیاده سازی Logout» استفاده می‌کند. یا حتی می‌توانید در قسمت logout یک SerialNumber را هم تغییر دهید که به صورت یک Claim سفارشی در توکن قبلی وجود داشته باشد. عدم انطباق این مقادیر را در این اعتبارسنج سفارشی بررسی کنید.
              مطالب
              بررسی مقدمات کتابخانه‌ی JSON.NET
              چرا JSON.NET؟
              JSON.NET یک کتابخانه‌ی سورس باز کار با اشیاء JSON در دات نت است. تاریخچه‌ی آن به 8 سال قبل بر می‌گردد و توسط یک برنامه نویس نیوزیلندی به نام James Newton King تهیه شده‌است. اولین نگارش آن در سال 2006 ارائه شد؛ مقارن با زمانی که اولین استاندارد JSON نیز ارائه گردید.
              این کتابخانه از آن زمان تا کنون، 6 میلیون بار دانلود شده‌است و به علت کیفیت بالای آن، این روزها پایه اصلی بسیاری از کتابخانه‌ها و فریم ورک‌های دات نتی می‌باشد؛ مانند RavenDB تا ASP.NET Web API و SignalR مایکروسافت و همچنین گوگل نیز از آن جهت تدارک کلاینت‌های کار با API خود استفاده می‌کنند.
              هرچند دات نت برای نمونه در نگارش سوم آن جهت مصارف WCF کلاسی را به نام DataContractJsonSerializer ارائه کرد، اما کار کردن با آن محدود است به فرمت خاص WCF به همراه عدم انعطاف پذیری و سادگی کار با آن. به علاوه باید درنظر داشت که JSON.NET از دات نت 2 به بعد تا مونو، Win8 و ویندوز فون را نیز پشتیبانی می‌کند.

              برای نصب آن نیز کافی است دستور ذیل را در کنسول پاورشل نیوگت اجرا کنید:
               PM> install-package Newtonsoft.Json

              معماری JSON.NET

              کتابخانه‌ی JSON.NET از سه قسمت عمده تشکیل شده‌است:
              الف) JsonSerializer
              ب) LINQ to JSON
              ج) JSON Schema


              الف) JsonSerializer
              کار JsonSerializer تبدیل اشیاء دات نتی به JSON و برعکس است. مزیت مهم آن امکانات قابل توجه تنظیم عملکرد و خروجی آن می‌باشد که این تنظیمات را به شکل ویژگی‌های خواص نیز می‌توان اعمال نمود. به علاوه امکان سفارشی سازی هر کدام نیز توسط کلاسی به نام JsonConverter، پیش بینی شده‌است.
              یک مثال:
               var roles = new List<string>
              {
                 "Admin",
                 "User"
              };
              string json = JsonConvert.SerializeObject(roles, Formatting.Indented);
              در اینجا نحوه‌ی استفاده از JSON.NET را جهت تبدیل یک شیء دات نتی، به معادل JSON آن مشاهده می‌کنید. اعمال تنظیم Formatting.Indented سبب خواهد شد تا خروجی آن دارای Indentation باشد. برای نمونه اگر در برنامه‌ی خود قصد دارید فرمت JSON تو در تویی را به نحو زیبا و خوانایی نمایش دهید یا چاپ کنید، همین تنظیم ساده کافی خواهد بود.
              و یا در مثال ذیل استفاده از یک anonymous object را مشاهده می‌کنید:
               var jsonString = JsonConvert.SerializeObject(new
              {
                 Id =1,
                 Name = "Test"
              }, Formatting.Indented);
              به صورت پیش فرض تنها خواص عمومی کلاس‌ها توسط JSON.NET تبدیل خواهند شد.


              تنظیمات پیشرفته‌تر JSON.NET

              مزیت مهم JSON.NET بر سایر کتابخانه‌ها‌ی موجود مشابه، قابلیت‌های سفارشی سازی قابل توجه آن است. در مثال ذیل نحوه‌ی معرفی JsonSerializerSettings را مشاهده می‌نمائید:
              var jsonData = JsonConvert.SerializeObject(new
              {
                 Id = 1,
                 Name = "Test",
                 DateTime = DateTime.Now
              }, new JsonSerializerSettings
              {
                 Formatting = Formatting.Indented,
                 Converters =
                 {
                    new JavaScriptDateTimeConverter()
                 }
              });
              در اینجا با استفاده از تنظیم JavaScriptDateTimeConverter، می‌توان خروجی DateTime استانداردی را به مصرف کنندگان جاوا اسکریپتی سمت کاربر ارائه داد؛ با خروجی ذیل:
               {
                "Id": 1,
                "Name": "Test",
                "DateTime": new Date(1409821985245)
              }


              نوشتن خروجی JSON در یک استریم

              خروجی متد JsonConvert.SerializeObject یک رشته‌است که در صورت نیاز به سادگی توسط متد File.WriteAllText در یک فایل قابل ذخیره می‌باشد. اما برای رسیدن به حداکثر کارآیی و سرعت می‌توان از استریم‌ها نیز استفاده کرد:
              using (var stream = File.CreateText(@"c:\output.json"))
              {
                  var jsonSerializer = new JsonSerializer
                 {
                    Formatting = Formatting.Indented
                 };
                 jsonSerializer.Serialize(stream, new
                 {
                   Id = 1,
                   Name = "Test",
                   DateTime = DateTime.Now
                 });
              }
              کلاس JsonSerializer و متد Serialize آن یک استریم را نیز جهت نوشتن خروجی می‌پذیرند. برای مثال response.Output برنامه‌های وب نیز یک استریم است و در اینجا نوشتن مستقیم در استریم بسیار سریعتر است از تبدیل شیء به رشته و سپس ارائه خروجی آن؛ زیرا سربار تهیه رشته JSON از آن حذف می‌گردد و نهایتا GC کار کمتری را باید انجام دهد.


              تبدیل JSON رشته‌ای به اشیاء دات نت

              اگر رشته‌ی jsonData ایی را که پیشتر تولید کردیم، بخواهیم تبدیل به نمونه‌ای از شیء User ذیل کنیم:
              public class User
              {
                 public int Id { set; get; }
                 public string Name { set; get; }
                 public DateTime DateTime { set; get; }
              }
              خواهیم داشت:
               var user = JsonConvert.DeserializeObject<User>(jsonData);
              در اینجا از متد DeserializeObject به همراه مشخص سازی صریح نوع شیء نهایی استفاده شده‌است.
              البته در اینجا با توجه به استفاده از JavaScriptDateTimeConverter برای تولید jsonData، نیاز است چنین تنظیمی را نیز در حالت DeserializeObject مشخص کنیم:
              var user = JsonConvert.DeserializeObject<User>(jsonData, new JsonSerializerSettings
              {
                 Converters = {  new JavaScriptDateTimeConverter() }
              });


              مقدار دهی یک نمونه یا وهله‌ی از پیش موجود

              متد JsonConvert.DeserializeObject یک شیء جدید را ایجاد می‌کند. اگر قصد دارید صرفا تعدادی از خواص یک وهله‌ی موجود، توسط JSON.NET مقدار دهی شوند از متد PopulateObject استفاده کنید:
               JsonConvert.PopulateObject(jsonData, user);


              کاهش حجم JSON تولیدی

              زمانیکه از متد JsonConvert.SerializeObject استفاده می‌کنیم، تمام خواص عمومی تبدیل به معادل JSON آن‌ها خواهند شد؛ حتی خواصی که مقدار ندارند. این خواص در خروجی JSON، با مقدار null مشخص می‌شوند. برای حذف این خواص از خروجی JSON نهایی تنها کافی است در تنظیمات JsonSerializerSettings، مقدار NullValueHandling = NullValueHandling.Ignore مشخص گردد.
              var jsonData = JsonConvert.SerializeObject(object, new JsonSerializerSettings
              {
                 NullValueHandling = NullValueHandling.Ignore,
                 Formatting = Formatting.Indented
              });
              مورد دیگری که سبب کاهش حجم خروجی نهایی خواهد شد، تنظیم DefaultValueHandling = DefaultValueHandling.Ignore است. در این حالت کلیه خواصی که دارای مقدار پیش فرض خودشان هستند، در خروجی JSON ظاهر نخواهند شد. مثلا مقدار پیش فرض خاصیت int مساوی صفر است. در این حالت کلیه خواص از نوع int که دارای مقدار صفر می‌باشند، در خروجی قرار نمی‌گیرند.
              به علاوه حذف Formatting = Formatting.Indented نیز توصیه می‌گردد. در این حالت فشرده‌ترین خروجی ممکن حاصل خواهد شد.


              مدیریت ارث بری توسط JSON.NET

              در مثال ذیل کلاس کارمند و کلاس مدیر را که خود نیز در اصل یک کارمند می‌باشد، ملاحظه می‌کنید:
              public class Employee
              {
                  public string Name { set; get; }
              }
              
              public class Manager : Employee
              {
                  public IList<Employee> Reports { set; get; }
              }
              در اینجا هر مدیر لیست کارمندانی را که به او گزارش می‌دهند نیز به همراه دارد. در ادامه نمونه‌ای از مقدار دهی این اشیاء ذکر شده‌اند:
               var employee = new Employee { Name = "User1" };
              var manager1 = new Manager { Name = "User2" };
              var manager2 = new Manager { Name = "User3" };
              manager1.Reports = new[] { employee, manager2 };
              manager2.Reports = new[] { employee };
              با فراخوانی
               var list = JsonConvert.SerializeObject(manager1, Formatting.Indented);
              یک چنین خروجی JSON ایی حاصل می‌شود:
              {
                "Reports": [
                  {
                    "Name": "User1"
                  },
                  {
                    "Reports": [
                      {
                        "Name": "User1"
                      }
                    ],
                    "Name": "User3"
                  }
                ],
                "Name": "User2"
              }
              این خروجی JSON جهت تبدیل به نمونه‌ی معادل دات نتی خود، برای مثال جهت رسیدن به manager1 در کدهای فوق، چندین مشکل را به همراه دارد:
              - در اینجا مشخص نیست که این اشیاء، کارمند هستند یا مدیر. برای مثال مشخص نیست User2 چه نوعی دارد و باید به کدام شیء نگاشت شود.
              - مشکل دوم در مورد کاربر User1 است که در دو قسمت تکرار شده‌است. این شیء JSON اگر به نمونه‌ی معادل دات نتی خود نگاشت شود، به دو وهله از User1 خواهیم رسید و نه یک وهله‌ی اصلی که سبب تولید این خروجی JSON شده‌است.

              برای حل این دو مشکل، تغییرات ذیل را می‌توان به JSON.NET اعمال کرد:
              var list = JsonConvert.SerializeObject(manager1, new JsonSerializerSettings
              {
                 Formatting = Formatting.Indented,
                 TypeNameHandling = TypeNameHandling.Objects,
                 PreserveReferencesHandling = PreserveReferencesHandling.Objects
              });
              با این خروجی:
              {
                "$id": "1",
                "$type": "JsonNetTests.Manager, JsonNetTests",
                "Reports": [
                  {
                    "$id": "2",
                    "$type": "JsonNetTests.Employee, JsonNetTests",
                    "Name": "User1"
                  },
                  {
                    "$id": "3",
                    "$type": "JsonNetTests.Manager, JsonNetTests",
                    "Reports": [
                      {
                        "$ref": "2"
                      }
                    ],
                    "Name": "User3"
                  }
                ],
                "Name": "User2"
              }
              - با تنظیم TypeNameHandling = TypeNameHandling.Objects سبب خواهیم شد تا خاصیت اضافه‌ای به نام $type به خروجی JSON اضافه شود. این نوع، در حین فراخوانی متد JsonConvert.DeserializeObject جهت تشخیص صحیح نگاشت اشیاء بکار گرفته خواهد شد و اینبار مشخص است که کدام شیء، کارمند است و کدامیک مدیر.
              - با تنظیم PreserveReferencesHandling = PreserveReferencesHandling.Objects شماره Id خودکاری نیز به خروجی JSON اضافه می‌گردد. اینبار اگر به گزارش دهنده‌ها با دقت نگاه کنیم، مقدار $ref=2 را خواهیم دید. این مورد سبب می‌شود تا در حین نگاشت نهایی، دو وهله متفاوت از شیء با Id=2 تولید نشود.

              باید دقت داشت که در حین استفاده از JsonConvert.DeserializeObject نیز باید JsonSerializerSettings یاد شده، تنظیم شوند.


              ویژگی‌های قابل تنظیم در JSON.NET

              علاوه بر JsonSerializerSettings که از آن صحبت شد، در JSON.NET امکان تنظیم یک سری از ویژگی‌ها به ازای خواص مختلف نیز وجود دارند.
              - برای نمونه ویژگی JsonIgnore معروفترین آن‌ها است:
              public class User
              {
                 public int Id { set; get; }
              
                 [JsonIgnore]
                 public string Name { set; get; }
              
                 public DateTime DateTime { set; get; }
              }
              JsonIgnore سبب می‌شود تا خاصیتی در خروجی نهایی JSON تولیدی حضور نداشته باشد و از آن صرفنظر شود.

              - با استفاده از ویژگی JsonProperty اغلب مواردی را که پیشتر بحث کردیم مانند NullValueHandling، TypeNameHandling و غیره، می‌توان تنظیم نمود. همچنین گاهی از اوقات کتابخانه‌های جاوا اسکریپتی سمت کاربر، از اسامی خاصی که از روش‌های نامگذاری دات نتی پیروی نمی‌کنند، در طراحی خود استفاده می‌کنند. در اینجا می‌توان نام خاصیت نهایی را که قرار است رندر شود نیز صریحا مشخص کرد. برای مثال:
              [JsonProperty(PropertyName = "m_name", NullValueHandling = NullValueHandling.Ignore)]
              public string Name { set; get; }
              همچنین در اینجا امکان تنظیم Order نیز وجود دارد. برای مثال مشخص کنیم که خاصیت X در ابتدا قرار گیرد و پس از آن خاصیت Y رندر شود.

              - استفاده از ویژگی JsonObject به همراه مقدار OptIn آن به این معنا است که از کلیه خواصی که دارای ویژگی JsonProperty نیستند، صرفنظر شود. حالت پیش فرض آن OptOut است؛ یعنی تمام خواص عمومی در خروجی JSON حضور خواهند داشت منهای مواردی که با JsonIgnore مزین شوند.
              [JsonObject(MemberSerialization.OptIn)]
              public class User
              {
                  public int Id { set; get; }
              
                  [JsonProperty]
                  public string Name { set; get; }
               
                  public DateTime DateTime { set; get; }
              }

              - با استفاده از ویژگی JsonConverter می‌توان نحوه‌ی رندر شدن مقدار خاصیت را سفارشی سازی کرد. برای مثال:
              [JsonConverter(typeof(JavaScriptDateTimeConverter))]
              public DateTime DateTime { set; get; }


              تهیه یک JsonConverter سفارشی

              با استفاده از JsonConverterها می‌توان کنترل کاملی را بر روی اعمال serialization و deserialization مقادیر خواص اعمال کرد. مثال زیر را در نظر بگیرید:
              public class HtmlColor
              {
                 public int Red { set; get; }
                 public int Green { set; get; }
                 public int Blue { set; get; }
              }
              
              var colorJson = JsonConvert.SerializeObject(new HtmlColor
              {
                Red = 255,
                Green = 0,
                Blue = 0
              }, Formatting.Indented);
              در اینجا علاقمندیم، در حین عملیات serialization، بجای اینکه مقادیر اجزای رنگ تهیه شده به صورت int نمایش داده شوند، کل رنگ با فرمت hex رندر شوند. برای اینکار نیاز است یک JsonConverter سفارشی را تدارک دید:
                  public class HtmlColorConverter : JsonConverter
                  {
              
                      public override bool CanConvert(Type objectType)
                      {
                          return objectType == typeof(HtmlColor);
                      }
              
                      public override object ReadJson(JsonReader reader, Type objectType,
                                                      object existingValue, JsonSerializer serializer)
                      {
                          throw new NotSupportedException();
                      }
              
                      public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
                      {
                          var color = value as HtmlColor;
                          if (color == null)
                              return;
              
                          writer.WriteValue("#" + color.Red.ToString("X2")
                              + color.Green.ToString("X2") + color.Blue.ToString("X2"));
                      }
                  }
              کار با ارث بری از کلاس پایه JsonConverter شروع می‌شود. سپس باید تعدادی از متدهای این کلاس پایه را بازنویسی کرد. در متد CanConvert اعلام می‌کنیم که تنها اشیایی از نوع کلاس HtmlColor را قرار است پردازش کنیم. سپس در متد WriteJson منطق سفارشی خود را می‌توان پیاده سازی کرد.
              از آنجائیکه این تبدیلگر صرفا قرار است برای حالت serialization استفاده شود، قسمت ReadJson آن پیاده سازی نشده‌است.

              در آخر برای استفاده از آن خواهیم داشت:
              var colorJson = JsonConvert.SerializeObject(new HtmlColor
              {
                Red = 255,
                Green = 0,
                Blue = 0
              },  new JsonSerializerSettings
                  {
                    Formatting = Formatting.Indented,
                    Converters = { new HtmlColorConverter() }
                  });   
              اشتراک‌ها
              تولید کد اتوماتیک MVVM با Roslyn
              اگر با MVVM کار میکنید و از فریمورک‌های آماده استفاده نمی‌کنید حتما پیاده سازی تکراری INotifyPropertyChanged و RealyCommand و ViewModelBase شما را خسته کرده است. کامپایلر Roslyn به شما اجازه میدهد که به سادگی امکانات Refactoring موجود در Visual Studio را سفارشی سازی کرده و بسته به نیاز خود Refactoring‌های جدید بسازید تا در زمان کدنویسی شما صرفه جویی شود. در این مقاله با نحوه ساخت چند نمونه Refactoring سفارشی آشنا می‌شوید.


              تولید کد اتوماتیک MVVM با Roslyn