نظرات مطالب
EF Code First #14
- آزمایش کردی یکبار؟ (من این رو در یک برنامه WPF استفاده کردم؛ با یک Context در سطح ViewModel که کار تحت نظر قرار دادن اطلاعات رو داره. حتی دکمه undo هم میشه طراحی کرد با استفاده از متد RejectChanges و در WPF با سیستم Binding خوبی که داره بلافاصله UI به صورت خودکار به روز میشه)
- Added مربوط به زمانی است که اطلاعات به سیستم ردیابی (context در اینجا) اضافه شده و نه به بانک اطلاعاتی. Modified مربوط به حالتی است که اطلاعات تحت نظر سیستم ردیابی مثلا یک خاصیت آن تغییر کرده است؛ پیش از ذخیره سازی در بانک اطلاعاتی. EF بر همین اساس هست که تشخیص می‌ده چه کوئری را باید صادر کند برای ذخیره یا به روز رسانی نهایی اطلاعات.
نظرات مطالب
ModelBinder سفارشی در ASP.NET MVC
ضمن تشکر از مقاله خوبتان
در قسمت آخر فرمودید ، باید به کنترلر اطلاع دهیم که میخواهیم از چه نوع Binding استفاده کنیم  ولی اگر اشتباه نکنم این مورد اجباری نیست یعنی اکشن ما می‌تواند به شکل زیر هم باشد
public ActionResult Create(CustomerInfo customerInfo)
اشتراک‌ها
ساخت یک برنامه اعلان وضعیت (Notification) توسط برنامه های UWP بخش اول

f you are a developer and building apps or games for Windows 10, targetting Universal Windows Platform (aka. UWP), you might want to show toast notifications to the user of your app/game. You might also want to provide some additional content like textbox, button etc. to get specific inputs. 

ساخت یک برنامه اعلان وضعیت (Notification) توسط برنامه های UWP بخش اول
مطالب
صدور رخدادها از سرویس‌ها به کامپوننت‌ها در برنامه‌های Angular
در طراحی برنامه‌های Angular توصیه شده‌است تا هرگونه منطقی که مستقیما به View یک کامپوننت مرتبط نیست، به یک کلاس سرویس منتقل شود. در این بین ممکن است نیاز به صدور رخدادی از یک سرویس به خارج از آن باشد؛ چیزی مانند EventEmitter. اما EventEmitter برای سرویس‌ها طراحی نشده‌است و کاربرد صحیح آن صرفا محدود به کامپوننت‌ها است. برای حل این مساله، API سرویس ما باید یک Observable را در معرض دید استفاده کننده قرار دهد تا توسط آن بتوان رخ‌دادهایی را به کامپوننت‌های مشترک شده‌ی به آن، صادر کرد.


چگونه می‌توان رخ‌دادهایی از نوع Observable را ایجاد کرد؟

کلاس Subject پاسخی است به این پرسش. Subjectها Observableهایی هستند که می‌توانند چندین مشترک داشته باشند و رخ‌دادهایی را به مشترکین خود صادر کنند. برای کار با آن‌ها باید یک private Subject را در سرویس خود ایجاد کرد و سپس جریان منتقل شده‌ی توسط آن‌را توسط یک public Observable در اختیار مصرف کنندگان قرار داد. با فراخوانی متد next یک Subject، رخ‌دادی به مشترکین آن منتقل می‌شود.
import { Subject } from “rxjs/Subject”;

public countdown: number = 0;

private countdownEndSource = new Subject<void>();
public countdownEnd$ = this.countdownEndSource.asObservable();
مرسوم است نام Observableهایی را که قرار است رخ‌دادی را صادر کنند به $ ختم می‌کنند.
استفاده کنندگان نیز مشترک این $countdownEnd شده و هر بار که در طرف سرویس، متد next آن فراخوانی می‌شود، از به روز رسانی آن مطلع خواهند شد.


چرا مستقیما از مقدار countdown استفاده نکنیم؟

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


یک مشکل! Subject تعریف شده، مقادیر را تنها در زمان فراخوانی متد next ارائه می‌دهد و نه به صورت دیگری.

پیشتر با دسترسی مستقیم به خاصیت countdown، همواره به مقادیر آن هم دسترسی داشتیم. اما با استفاده از یک Subject، تنها زمانیکه متد next آن فراخوانی شود می‌توان به این مقدار دسترسی یافت. برای رفع این مشکل یک Subject ویژه به نام BehaviorSubject طراحی شده‌است که به محض مشترک شدن به آن، اولین و یا آخرین مقدار آن‌را می‌توان دریافت کرد.


تفاوت Subject با BehaviorSubject

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

و مهم‌ترین مزیت آن نسبت به Subject، همان مورد دوم است. اگر مشترک یک Subject شویم، تا متد next آن فراخوانی نشود، مقداری را دریافت نمی‌کنیم. اما همان لحظه که مشترک BehaviorSubject می‌شویم، آخرین مقدار موجود در آن‌را دریافت خواهیم کرد.
برای مثال فرض کنید کامپوننتی را دارید که به خاصیت isLoggedIn از نوع Observable یک Subject گوش فرا می‌دهد. اما اشتراک آن پس از فراخوانی متد next در این سرویس بوده‌است. از این رو این کامپوننت هیچگاه متوجه تغییر و یا مقدار نهایی isLoggedIn نخواهد شد. به همین جهت است که به BehaviorSubject نیاز داریم. در این بین مهم نیست که چه زمانی مشترک آن می‌شویم؛ همواره در زمان اشتراک، آخرین و یا اولین مقدار موجود در آن‌را دریافت خواهیم کرد.


یک مثال: بررسی عملکرد BehaviorSubject

در ادامه یک ماژول را به همراه 4 کامپوننت و یک سرویس سفارشی ایجاد می‌کنیم:
ng g m ServiceComponentCommunication -m app.module --routing
ng g c ServiceComponentCommunication/First
ng g c ServiceComponentCommunication/Second
ng g c ServiceComponentCommunication/Third
ng g c ServiceComponentCommunication/Final
ng g s ServiceComponentCommunication/Sample


هدف این است که سه کامپوننت اول، دوم و سوم را در کامپوننت final، همانند تصویر فوق نمایش دهیم.
در این بین یک سرویس انتشار اطلاعات نیز طراحی شده‌است:
import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs/BehaviorSubject";

@Injectable()
export class SampleService {

  private msgSource = new BehaviorSubject<string>("default service value");

  telecast$ = this.msgSource.asObservable();

  constructor() { }

  editMsg(newMsg: string) {
    this.msgSource.next(newMsg);
  }

}
کار این سرویس ارائه یک پیام از نوع BehaviorSubject از طریق خاصیت عمومی $telecast آن است که به صورت Observable در معرض دید کامپوننت‌های مشترک به آن قرار خواهد گرفت. هدف این است که کامپوننت‌ها مدام تغییرات msg را بررسی نکنند و فقط به آخرین تغییر صادر شده‌ی توسط کامپوننت که از طریق فراخوانی متد next در متد editMsg صورت می‌گیرد، واکنش نشان دهند.

در کامپوننت اول، نحوه‌ی اشتراک به این سرویس را مشاهده می‌کنید:
import { SampleService } from "./../sample.service";
import { Component, OnInit, OnDestroy } from "@angular/core";
import { Subscription } from "rxjs/Subscription";

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

  editedMsg: string;
  sampleSubscription: Subscription;

  constructor(private sampleService: SampleService) { }

  ngOnInit() {
    this.sampleSubscription = this.sampleService.telecast$.subscribe(message => {
      this.editedMsg = message;
    });
  }

  editMsg() {
    this.sampleService.editMsg(this.editedMsg);
  }

  ngOnDestroy() {
    this.sampleSubscription.unsubscribe();
  }
}
کار اشتراک در این کامپوننت در متد ngOnInit انجام شده‌است. بسیار مهم است جهت عدم بروز نشتی حافظه، در متد ngOnDestroy کار unsubscribe بر روی این اشتراک نیز صورت گیرد.
در اینجا هر زمانیکه متد next در سرویس فراخوانی شود، this.editedMsg مقدار جدیدی را دریافت می‌کند.
با این قالب:
<div class="panel panel-default">
  <div class="panel-heading">
    <h2 class="panel-title">First Component</h2>
  </div>
  <div class="panel-body">
    <p> {{editedMsg}}</p>
    <input class="form-control" type="text" [(ngModel)]="editedMsg">
    <button (click)="editMsg()" class="btn btn-primary">Change</button>
  </div>
</div>


اما اگر به تصویر دقت کنید، this.editedMsg هم اکنون دارای مقدار است (در اولین بار اجرای این کامپوننت). علت آن به داشتن مقدار اولیه‌ای در BehaviorSubject تعریف شده بر می‌گردد که در اولین بار اشتراک به آن، در اختیار مشترک قرار خواهد گرفت. این مورد، مهم‌ترین تفاوت BehaviorSubject با Subject است.
در این کامپوننت اگر کاربر مقداری را در textbox وارد کند و سپس بر روی دکمه‌ی Change کلیک نماید، این تغییر از طریق سرویس، به تمام مشترکین آن صادر خواهد شد.

کامپوننت دوم نیز مانند کامپوننت اول است، فقط یک textbox ورود اطلاعات را به همراه ندارد.


همانطور که ملاحظه می‌کنید، این کامپوننت نیز دارای مقدار اولیه‌ی BehaviorSubject است.

کامپوننت سوم، اندکی متفاوت است:
import { SampleService } from "./../sample.service";
import { Component, OnInit, OnDestroy } from "@angular/core";
import { Subscription } from "rxjs/Subscription";

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

  message: string;
  sampleSubscription: Subscription;

  constructor(private sampleService: SampleService) { }

  ngOnInit() {
  }

  subscribe() {
    this.sampleSubscription = this.sampleService.telecast$.subscribe(message => {
      this.message = message;
    });
  }

  ngOnDestroy() {
    if (this.sampleSubscription) {
      this.sampleSubscription.unsubscribe();
    }
  }
}
در اینجا کار اشتراک در متد subscribe فراخوانی شده‌ی توسط قالب آن صورت می‌گیرد:
<div class="panel panel-default">
  <div class="panel-heading">
    <h2 class="panel-title">Third Component</h2>
  </div>
  <div class="panel-body">
    <p>{{message}}</p>
    <button (click)="subscribe()" class="btn btn-success">Subscribe</button>
  </div>
</div>


و چون این متد پس از ngOnInit قرار است توسط کاربر فراخوانی شود، مقدار message این کامپوننت هنوز خالی است.
اکنون اگر بر روی دکمه‌ی Subscribe آن کلیک کنیم، بلافاصله در لحظه‌ی اشتراک، اولین/آخرین مقدار موجود در BehaviorSubject را دریافت خواهیم کرد:


کامپوننت Final نیز تمام کامپوننت‌ها را در صفحه نمایش می‌دهد:
<div class="row">
  <div class="col-md-4">
    <app-first></app-first>
  </div>
  <div class="col-md-4">
    <app-second></app-second>
  </div>
  <div class="col-md-4">
    <app-third></app-third>
  </div>
</div>

و اگر در textbox کامپوننت اول، مقدار Test را وارد کنیم و سپس بر روی دکمه‌ی Change آن کلیک نمائیم، این مقدار به تمام کامپوننت‌های مشترک به BehaviorSubject سرویس برنامه، منتشر خواهد شد:


کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید.
نظرات مطالب
Blazor 5x - قسمت یازدهم - مبانی Blazor - بخش 8 - کار با جاوا اسکریپت
یک نکته‌ی تکمیلی: امکان فراخوانی کدهای #C از طریق کدهای جاوااسکریپت، در برنامه‌های Blazor

در مطلب جاری، روش فراخوانی توابع جاوااسکریپتی را از طریق کدهای #C برنامه‌های Blazor بررسی کردیم؛ عکس آن نیز میسر است و یکی از کاربردهای آن، ارسال نتایج کتابخانه‌های جاوااسکریپتی، به کدهای یک کامپوننت است. برای مثال کاربری در یک کامپوننت تقویم باز شده، روزی را انتخاب می‌کند. می‌خواهیم نتیجه‌ی این انتخاب او را که در سمت کدهای جاوااسکریپتی رخ‌داده، به نحوی به کدهای #C یک کامپوننت منتقل کنیم و یا حتی محاسباتی را در سمت کدهای #C انجام دهیم و به کدهای جاوااسکریپتی منتقل کنیم.

الف) فراخوانی متدهای استاتیک #C از طریق کدهای جاوااسکریپتی
فرض کنید متد استاتیک HelpMessage را می‌خواهیم از طریق کدهای جاوااسکریپتی فراخوانی کنیم. برای این منظور، یک چنین تابعی باید به ویژگی JSInvokable مزین شود:
@page "/js-sample"


<button class="btn btn-primary" onclick="JsFunctionHelper.invokeDotnetStaticFunction()">Invoke Static Method</button>

@code
{
    [JSInvokable]
    public static Task<string> HelpMessage()
    {
        return Task.FromResult("Help text from C# static function");
    }
}
در اینجا یک دکمه را هم مشاهده می‌کنید که از ویژگی onclick استاندارد HTML استفاده کرده‌است. یعنی متدی را که فراخوانی می‌کند، در حقیقت یک کد جاوا اسکریپتی است و نه یک متد #C واقع در کامپوننت جاری.
سپس در سمت در فایل Client\wwwroot\main.js برای فراخوانی متد HelpMessage خواهیم داشت:
window.JsFunctionHelper = {
  invokeDotnetStaticFunction: function () {
    DotNet.invokeMethodAsync("BlazorRazorSample.Client", "HelpMessage").then(
      (data) => {
        console.log(data);
      }
    );
  }
};
در اینجا تابع سراسری جدیدی به نام invokeDotnetStaticFunction تعریف شده‌است (همان تابعی که توسط دکمه‌ی قرار گرفته در کامپوننت فراخوانی می‌شود). این تابع با استفاده از متد DotNet.invokeMethodAsync استاندارد Blazor، کار فراخوانی متد استاتیک HelpMessage واقع در فضای نام BlazorRazorSample.Client را انجام می‌دهد. چون این فراخوانی async است، نتیجه‌ی نهایی را از طریق یک callback دریافت کرده و لاگ می‌کند.

ب) فراخوانی متدهای غیر استاتیک #C از طریق کدهای جاوااسکریپتی
فراخوانی instance methodهای کامپوننت‌ها از طریق کدهای #C، کمی پیچیده‌تر است:
@page "/js-sample"
@implements IDisposable

@inject IJSRuntime jSRuntime

<button class="btn btn-primary" @onclick="CallInstanceMethod">Invoke Instance Method</button>

@code
{
    private DotNetObjectReference<JsSample> objectReference;

    [JSInvokable]
    public string GetAddress()
    {
        return "123 Main Street";
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if(firstRender)
        {
            objectReference = DotNetObjectReference.Create(this);
        }
    }

    private async Task CallInstanceMethod()
    {
        await jSRuntime.InvokeVoidAsync("JsFunctionHelper.invokeDotnetInstanceFunction", objectReference);
    }

    public void Dispose()
    {
        objectReference?.Dispose();
    }
}
- در این حالت نیاز است ارجاعی از وهله‌ی کامپوننت جاری را به متد جاوااسکریپتی ارسال کرد. به همین جهت در ابتدا توسط متد DotNetObjectReference.Create، این ارجاع را ایجاد کرده و سپس توسط متد jSRuntime.InvokeVoidAsync آن‌را به سمت کدهای جاوا اسکریپتی ارسال می‌کنیم. در مثال فوق، JsSample همان نام کامپوننت جاری است.
- همچنین در اینجا onclick تعریف شده، به متدی داخل همین کامپوننت اشاره می‌کند.
- این ارجاع نیز باید در پایان کار کامپوننت، Dispose شود. به همین جهت implements IDisposable@ را مشاهده می‌کنید.

اکنون کدهای جاوا اسکریپتی که از این وهله‌ی دریافتی استفاده می‌کند، به صورت زیر خواهد بود. در این کدها addressProvider همان objectReference دریافتی است که توسط آن می‌توان متد غیراستاتیک GetAddress کامپوننت را فراخوانی کرد:
window.JsFunctionHelper = {
  invokeDotnetInstanceFunction: function (addressProvider) {
    addressProvider.invokeMethodAsync("GetAddress").then((data) => {
      console.log(data);
    });
  }
};
نظرات مطالب
ASP.NET MVC #11
من یک مثال جهت استفاده از model و viewmodel می‌خواستم که چگونه قابل اجرا است
مطالب
تزریق وابستگی‌های Automapper به کمک Autofac
در این مقاله قصد دارم به وسیله Autofac تزریق وابستگی‌های Automapper و همچنین Register کردن فایل‌های Profile Mapper را توضیح دهم.
حتما مقالات مقالات متعدد در رابطه با تزریق وابستگی را که در این سایت وجود دارند، مطالعه کرده‌اید. در این بخش قصد دارم از Autofac (بجای StructureMap) برای تزریق Automapper استفاده کنم.
1. ابتدا ساختار پروژه را بررسی می‌کنیم. بدین منظور یک پروژه جدید را با عنوان AufacDI ایجاد میکنیم. 
2. در این مرحله یک پروژه از نوع Class Library را با عنوان AufacDI.DomainClasses، برای شبیه سازی مدل ایجاد میکنیم. 
3. سپس یک پروژه از نوع Class Library را با عنوان AufacDI.IocConfig برای تعریف تنظیمات تزریق وابستگی ایجاد میکنیم.
4. در ادامه، پروژه‌ای را از نوع Class Library با عنوان AufacDI.MapperProfile برای معرفی Profile‌های Mapper ایجاد میکنیم.
5. همچنین پروژه‌ای را برای ViewModel‌ها تعریف میکنیم؛ با عنوان AufacDI.ViewModel. 
6. و در آخر ایجاد پروژه‌ای برای بخش UI با عنوانAufacDI.WebApplication

در ابتدا نیاز است که بسته‌های زیر را از Nuget دریافت و  نصب کنیم:
PM>Install-Package Autofac
PM>Install-Package Autofac.Mvc5
PM>Install-Package AutoMapper
بسته Autofac را در لایه AufacDI.IocConfig و AufacDI.ConsoleApplication نصب می‌کنیم.
بسته Install-Package Autofac.Mvc5  را برای تزریق وابستگی‌ها در لایه UI استفاده میکنیم.
و بسته AutoMapper را در لایه AufacDI.MapperProfile , AufacDI.IocConfig و  AufacDI.WebApplication  نصب میکنیم (به دلیل اینکه این پروژه برای مثال، Automapper به لایه UI اضافه شده است وگرنه باید در لایه Service ارجاع داده شود).

حال در این بخش به تعاریف داخلی پروژه می‌پردازیم:
لازم است ابتدا یک Domain Class را تعریف کنیم؛ به صورت زیر:
namespace AufacDI.DomainClasses
{
    public class Category
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
}
سپس ViewModel متناظر با آن را تعریف میکنیم:
namespace AufacDI.ViewModel
{
    public class CategoryViewModel
    {
        public int Id { get; set; }
        public int Name { get; set; }
    }
}
سپس یک  Profile را برای مدل نمونه تعریف میکینم. (ارجاعات لازم به DomainClasses و ViewModel داده شود)
using AufacDI.DomainClasses;
using AufacDI.ViewModel;
using AutoMapper;

namespace AufacDI.MapperProfile
{
    public class CategoryProfile : Profile
    {
        public CategoryProfile()
        {
            CreateMap<Category, CategoryViewModel>();
            CreateMap<CategoryViewModel, Category>();
        }
    }
}

حال به بخش اصلی میرسیم؛ یعنی تکمیل بخش IocConfig: (ارجاعات لازم به MapperProfile داده شود)
using AufacDI.MapperProfile;
using Autofac;
using AutoMapper;
using System;
using System.Linq;

namespace AufacDI.IocConfig
{
    public static class IoCContainer
    {
       public static void Register(ContainerBuilder builder)
        {
            // شناسایی پروفایل‌ها براساس نمونه از کلاس پر.وفایل 
            var profiles = from types in typeof(CategoryProfile).Assembly.GetTypes()
                           where typeof(Profile).IsAssignableFrom(types)
                           select (Profile)Activator.CreateInstance(types);

            // رجیستر کردن کلاس‌های پروفایل در اتومپر
            builder.Register(ctx => new MapperConfiguration(cfg =>
            {
                foreach (var profile in profiles)
                    cfg.AddProfile(profile);
            })).SingleInstance().AutoActivate().AsSelf();

            // رجیستر کردن کلاس  MapperConfiguration و ایجاد آن براساس IMapper
            builder.Register(ctx => ctx.Resolve<MapperConfiguration>().CreateMapper()).As<IMapper>().InstancePerRequest();
        }
    }
}

در ادامه با یک مثال، روند کلی را توضیح میدهیم:
            var builder = new ContainerBuilder();

            // تزریق کنترلرها برای تزریف سایر المان‌ها در سازنده
            builder.RegisterControllers(typeof(MvcApplication).Assembly).InstancePerDependency();

            // فراخوانی متد رجیستر برای تزریق وابستگی مپر و کلاس‌های پروفایل آن
            IoCContainer.Register(builder);

            // ایجاد نمونه از سازنده
            var container = builder.Build();
            DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
این بخش، معرفی و تعریف نگاشت‌های تزریق وابستگی می‌باشد.
نمونه‌ای از پیاده سازی در سطح کنترلر
namespace AufacDI.WebApplication.Controllers
{
    public class HomeController : Controller
    {
        private readonly IMapper _mapepr;
        public HomeController(IMapper mapepr)
        {
            _mapepr = mapepr;
        }

        public ActionResult Index()
        {
            // مپ کردن یک کلاس به یک کلاس
            var categoryViewModel = new CategoryViewModel { Id = 1, Name = "News" };
            var categoryModel = _mapepr.Map<CategoryViewModel, Category>(categoryViewModel);

            // مپ کردن لیست از کلاس به لیستی از کلاس
            var categoryListModel = new List<Category>();
            categoryListModel.Add(new Category { Id = 1, Name = "A" });
            categoryListModel.Add(new Category { Id = 2, Name = "B" });
            categoryListModel.Add(new Category { Id = 3, Name = "C" });
            categoryListModel.Add(new Category { Id = 4, Name = "D" });
            categoryListModel.Add(new Category { Id = 5, Name = "E" });

            var categoryListViewModel = categoryListModel.AsQueryable().ProjectTo<CategoryViewModel>(_mapepr.ConfigurationProvider).ToList(); ;

            return View();
        }
    }
}
نکته: برای مپ کردن یک آبجکت به آبجکتی دیگر، از متد Map استفاده می‌شود و برای مپ کردن لیستی از آبجکت‌ها از ProjectTo استفاده می‌شود.
نمونه ای از مثال AufacDI.rar
مطالب
یکپارچه سازی سیستم اعتبارسنجی ASP.NET MVC با Kendo UI validator
روش پیش فرض اعتبارسنجی برنامه‌های ASP.NET MVC، استفاده از دو افزونه‌ی jquery.validate و jquery.validate.unobtrusive است.
    <script src="~/Scripts/jquery.validate.min.js" type="text/javascript"></script>
    <script src="~/Scripts/jquery.validate.unobtrusive.min.js" type="text/javascript"></script>
کار اصلی اعتبارسنجی، توسط افزونه‌ی jquery.validate انجام می‌شود و فایل jquery.validate.unobtrusive صرفا یک وفق دهنده و مترجم ویژگی‌های خاص ASP.NET MVC به jquery.validate است.


عدم سازگاری پیش فرض jquery.validate با بعضی از ویجت‌های Kendo UI

در حالت استفاده از Kendo UI، این سیستم هنوز هم کار می‌کند؛ اما با یک مشکل. اگر برای مثال از kendoComboBox استفاده کنید، اعتبارسنجی‌های تعریف شده در برنامه، توسط jquery.validate دیده نخواهند شد. برای مثال فرض کنید یک چنین مدلی در اختیار View برنامه قرار گرفته است:
    public class OrderDetailViewModel
    {
        [StringLength(15)]
        [Required]
        public string Destination { get; set; }
    }
با این View که در آن به فیلد Destination، یک kendoComboBox متصل شده‌است:
@model Mvc4TestViewModel.Models.OrderDetailViewModel

@using (Ajax.BeginForm(actionName: "Index", controllerName: "Home",
                       ajaxOptions:new AjaxOptions(),
                       htmlAttributes: new { id = "Form1", name ="Form1" }, routeValues: new { }
                       ))
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>OrderDetail</legend>
        <div class="editor-label">
            @Html.LabelFor(model => model.Destination)
        </div>
        <div class="editor-field">
            @Html.TextBoxFor(model => model.Destination, new { @class = "k-textbox" })
            @Html.ValidationMessageFor(model => model.Destination)
        </div>

        <p>
            <button class="k-button" type="submit" title="Sumbit">
                Sumbit
            </button>
        </p>
    </fieldset>
}

@section JavaScript
{
    <script type="text/javascript">
        $(function () {
            $("#Destination").kendoComboBox({
                dataSource: [
                    "loc 1",
                    "loc 2"
                ]
            });
    </script>
}
اگر برنامه را اجرا کنید و بر روی دکمه‌ی submit کلیک نمائید، ویژگی Required عمل نخواهد کرد و عملا در سمت کاربر اعتبارسنجی رخ نمی‌دهد.


همانطور که در تصویر مشاهده می‌کنید، با اتصال kendoComboBox به یک فیلد، این فیلد در حالت مخفی قرار می‌گیرد و ویجت کندو یو آی بجای آن نمایش داده خواهد شد. در این حالت چون در فایل jquery.validate.js چنین تنظیمی وجود دارد:
$.extend( $.validator, {
    defaults: {
     //…
     ignore: ":hidden",
به صورت پیش فرض از اعتبارسنجی فیلدهای مخفی صرفنظر می‌شود.
راه حل آن نیز ساده‌است. تنها باید خاصیت ignore را بازنویسی کرد و تغییر داد:
    <script type="text/javascript">
        $(function () {
            var form = $('#Form1');
            form.data('validator').settings.ignore = ''; // default is ":hidden".
        });
    </script>
در اینجا صرفا خاصیت ignore فرم یک، جهت درنظر گرفتن فیلدهای مخفی تغییر کرده‌است. اگر می‌خواهید این تنظیم را به تمام فرم‌ها اعمال کنید، می‌توان از دستور ذیل استفاده کرد:
<script type="text/javascript">
    $.validator.setDefaults({
        ignore: ""
    });
</script>


یکپارچه کردن سیستم اعتبارسنجی Kendo UI با سیستم اعتبارسنجی ASP.NET MVC

در مطلب «اعتبار سنجی ورودی‌های کاربر در Kendo UI» با زیرساخت اعتبارسنجی Kendo UI آشنا شدید. برای اینکه بتوان این سیستم را با ASP.NET MVC یکپارچه کرد، نیاز است دو کار صورت گیرد:
الف) تعریف فایل kendo.aspnetmvc.js به صفحه اضافه شود:
 <script src="~/Scripts/kendo.aspnetmvc.js" type="text/javascript"></script>
ب) همانند قبل، متد kendoValidator بر روی فرم فراخوانی شود تا سیستم اعتبارسنجی Kendo UI در این ناحیه فعال گردد:
    <script type="text/javascript">
        $(function () {
            $("form").kendoValidator();
        });
    </script>
پس از آن خواهیم داشت:


فایل kendo.aspnetmvc.js که در بسته‌ی مخصوص Kendo UI تهیه شده برای ASP.NET MVC موجود است (در پوشه‌ی js آن)، عملکردی مشابه فایل jquery.validate.unobtrusive مایکروسافت دارد. کار آن وفق دادن و ترجمه‌ی اعتبارسنجی unobtrusive به روش Kendo UI است.

این فایل را از اینجا می‌توانید دریافت کنید:
kendo.mvc.zip


البته باید دقت داشت که در حال حاضر فقط ویژگی‌های ذیل از ASP.NET MVC توسط kendo.aspnetmvc.js پشتیبانی می‌شوند:
 Required
StringLength
Range
RegularExpression
برای تکمیل آن می‌توان از یک پروژه‌ی سورس باز به نام Moon.Validation for KendoUI Validator استفاده کرد. برای مثال remote validation مخصوص Kendo UI را اضافه کرده‌است.