مطالب
سطح دوم cache در NHibernate

عموما دو الگوی اصلی caching در برنامه‌ها وجود دارند: cache aside و cache trough .
در الگوی cache trough ، سیستم caching داخل DAL (که در اینجا همان NHibernate است)، تعبیه می‌شود؛ مانند سطح اول caching که پیشتر در مورد آن صحبت شد. در این حالت cache از دید سایر قسمت‌های برنامه مخفی است و DAL به صورت خودکار آن‌را مدیریت می‌کند.
در الگوی cache aside ، کار مدیریت سیستم caching دستی است و خارج از NHibernate قرار می‌گیرد و DAL هیچگونه اطلاعی از وجود آن ندارد. در این حالت لایه caching موظف است تا هنگام به روز شدن بانک اطلاعاتی، اطلاعات خود را نیز به روز نماید. این لایه عموما توسط سایر شرکت‌ها یا گروه‌ها برنامه نویسی تهیه می‌شود.
NHibernate جهت سهولت کار با این نوع cache providers خارجی، نقاط تزریق ویژه‌ای را تدارک دیده است که به second level cache معروف است. هدف از second level cache فراهم آوردن دیدی کش شده از بانک اطلاعاتی است تا فراخوانی‌های کوئری‌ها به سرعت و بدون تماس با بانک اطلاعاتی صورت گیرد.
در حال حاضر (زمان نگارش این مطلب)، entity framework این لایه‌ی دوم caching یا به عبارتی دیگر، امکان تزریق ساده‌تر cache providers خارجی را به صورت توکار ارائه نمی‌دهد.
در NHibernate طول عمر second level cache در سطح session factory (یا به عبارتی طول عمر تمام برنامه) تعریف می‌شود و برخلاف سطح اولیه caching محدود به یک سشن نیست. در این حالت هر زمانیکه یک موجودیت به همراه ID منحصربفرد آن تحت نظر NHibernate قرارگیرد و همچنین سطح دوم caching نیز فعال باشد، این موجودیت در تمام سشن‌های برنامه بدون نیاز به مراجعه به بانک اطلاعاتی در دسترس خواهد بود (بنابراین باید دقت داشت که هدف از این سیستم، کار سریعتر با اطلاعاتی است که سطح دسترسی عمومی دارند).

در ادامه لیستی از cache providers خارجی مهیا جهت استفاده در سطح دوم caching را ملاحظه می‌نمائید:

  • AppFabric Caching Services : بر اساس Microsoft's AppFabric Caching Services که یک پلتفرم caching محسوب می‌شود (+). (این پروژه پیشتر به نام Velocity معروف شده بود و قرار بود تنها برای ASP.NET ارائه شود که سیاست آن به گونه‌ای جامع‌تر تغییر کرده است)
  • MemCache : بر اساس سیستم معروف MemCached تهیه شده است (+).
  • NCache : (+)
  • ScaleOut : (+)
  • Prevalence : (+)
  • SysCache : بر اساس همان روش آشنای متداول در برنامه‌های ASP.NET به کمک System.Web.Caching.Cache کار می‌کند؛ یا به قولی همان IIS caching
  • SysCache2 : همانند SysCache است با این تفاوت که SQL dependencies ویژه SQL Server را نیز پشتیبانی می‌کند.
  • SharedCache : یک سیستم distributed caching نوشته شده برای دات نت است (+).
این موارد و پروایدرها جزو پروژه‌ی nhcontrib در سایت سورس فورج هستند (+).


مطالب تکمیلی:



نظرات مطالب
خلاصه‌ای در مورد روش‌های دریافت فایل از سایت NuGet
شما وقتی این آدرس رو وارد کنید
فقط بسته‌ی مربوطه رو میگیرید و هیچ کدوم از وابستگی‌ها دانلود نمیشن!شما چون توی تنظیماتتون آدرس‌های مربوطه Nuget مثل  
https://www.nuget.org/api/v2/  
رو دارید خود Nuget تمام وابستگی‌ها رو هم براتون دانلود و نصب میکنه.من هم عرض کردم در صورتیکه از داخل VS دسترسی به سایت Nuget ندارید!
اگر دسترسی نداشته باشید بسته‌های پیش نیاز دریافت نخواهند شد , در نتیجه نصب صورت نمیگیره. پس باید تک تک آدرس‌ها رو بسازید و بسته‌های پیش نیاز رو دریافت کنید ولی بوسیله‌ی Nuget.exe میتونید همه‌ی بسته هارو یکجا دریافت کنید. شاید حالت خیلی خاصی باشه ولی بعضی جاها که دسترسی به اینترنت برای سیستمی که VS روش نصبه محدوده و نیاز دارن تا بسته‌ها رو بصورت آفلاین نصب کنن این متود کارآیی زیادی داره.
مطالب
فرم‌های مبتنی بر قالب‌ها در Angular - قسمت پنجم - ارسال اطلاعات به سرور
تا اینجا تنظیمات اصلی فرم ثبت اطلاعات کارمندان را انجام دادیم. اکنون نوبت به ارسال این اطلاعات به سمت سرور است. پیشنیاز آن نیز تدارک مواردی است که در مطلب «یکپارچه سازی Angular CLI و ASP.NET Core در VS 2017» پیشتر بحث شدند. از این مطلب تنها تنظیمات موارد ذیل را نیاز خواهیم داشت و از تکرار آن‌ها در اینجا صرفنظر می‌شود تا هم مطلب کوتاه‌تر شود و هم بتوان بر روی اصل موضوع جاری، تمرکز کرد:
- ایجاد یک پروژه‌ی جدید ASP.NET Core در VS 2017
- تنظیمات یک برنامه‌ی ASP.NET Core خالی برای اجرای یک برنامه‌ی Angular CLI
- تنظیمات فایل آغازین یک برنامه‌ی ASP.NET Core جهت ارائه‌ی برنامه‌های Angular
- ایجاد ساختار اولیه‌ی برنامه‌ی Angular CLI در داخل پروژه‌ی جاری: این مورد را تاکنون انجام داده‌ایم و تکمیل کرده‌ایم. بنابراین تنها کاری که نیاز است انجام شود، cut و paste محتوای پوشه‌ی angular-template-driven-forms-lab (پروژه‌ی این سری) به ریشه‌ی پروژه‌ی ASP.NET Core است.
- تنظیم محل خروجی نهایی Angular CLI به پوشه‌ی wwwroot
- روش اول و یا دوم اجرای برنامه‌های مبتنی بر ASP.NET Core و Angular CLI

البته سورس کامل تمام این تنظیمات را از انتهای بحث نیز می‌توانید دریافت کنید.
ضمن اینکه هیچ نیازی هم به استفاده از VS 2017 نیست و هر دوی برنامه‌ی Angular و ASP.NET Core را می‌توان توسط VSCode به خوبی مدیریت و اجرا کرد.


ایجاد ساختار مقدماتی سرویس ارسال اطلاعات به سرور

در برنامه‌های Angular مرسوم است جهت کاهش مسئولیت‌های یک کلاس و امکان استفاده‌ی مجدد از کدها، منطق ارسال اطلاعات به سرور، به درون کلاس یک سرویس منتقل شود و سپس این سرویس به کلاس‌های کامپوننت‌ها، برای مثال یک فرم ثبت اطلاعات، برای ارسال و یا دریافت اطلاعات، تزریق گردد. به همین جهت، ابتدا ساختار ابتدایی این سرویس و تنظیمات مرتبط با آن‌را انجام می‌دهیم.
ابتدا از طریق خط فرمان به پوشه‌ی ریشه‌ی برنامه وارد شده (جائیکه فایل Startup.cs قرار دارد) و سپس دستور ذیل را اجرا می‌کنیم:
 >ng g s employee/FormPoster -m employee.module
با این خروجی
 installing service
  create src\app\employee\form-poster.service.spec.ts
  create src\app\employee\form-poster.service.ts
  update src\app\employee\employee.module.ts
همانطور که در سطر آخر نیز ملاحظه می‌کنید، فایل employee.module.ts را جهت درج کلاس جدید FormPosterService در قسمت providers ماژول آن به روز رسانی می‌کند؛ تا بتوانیم این سرویس را در کامپوننت‌های این ماژول تزریق کرده و استفاده کنیم.
ساختار ابتدایی این سرویس را نیز به نحو ذیل تغییر می‌دهیم:
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';

import { Employee } from './employee';

@Injectable()
export class FormPosterService {

    constructor(private http:Http) {
    }

    postEmployeeForm(employee: Employee) {
    }
}
در اینجا سرویس Http انگیولار به سازنده‌ی کلاس تزریق شده‌است و این نحوه‌ی تعریف سبب می‌شود تا بتوان به پارامتر http، به صورت یک فیلد خصوصی تعریف شده‌ی در سطح کلاس نیز دسترسی پیدا کنیم.
چون این کلاس از ماژول توکار Http استفاده می‌کند، نیاز است این ماژول را نیز به قسمت imports فایل src\app\app.module.ts اضافه کنیم:
import { HttpModule } from "@angular/http";

@NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    EmployeeModule,
    AppRoutingModule
  ]
اکنون می‌توانیم این سرویس جدید FormPosterService را به سازنده‌ی کامپوننت EmployeeRegisterComponent در فایل src\app\employee\employee-register\employee-register.component.ts تزریق کنیم:
import { FormPosterService } from "../form-poster.service";

export class EmployeeRegisterComponent implements OnInit {

  constructor(private formPoster: FormPosterService) {}

}

در ادامه برای آزمایش برنامه، به ریشه‌ی پروژه وارد شده و دو پنجره‌ی کنسول مجزا را باز کنید. در اولی، دستورات:
>npm install
>ng build --watch
و در دومی، دستورات ذیل را اجرا کنید:
>dotnet restore
>dotnet watch run
دستورات اول کار بازیابی وابستگی‌های سمت کلاینت و سپس ساخت تدریجی برنامه‌ی Angular را دنبال می‌کند. دستورات دوم، وابستگی‌های برنامه‌ی ASP.NET Core را دریافت و نصب کرده و سپس برنامه را در حالت watch ساخته و بر روی پورت 5000 ارائه می‌کند (بدون نیاز به اجرای VS 2017؛ این دستور عمومی است).
به همین جهت برای آزمایش ابتدایی آن، آدرس http://localhost:5000 را در مرورگر باز کنید. برگه‌ی developer tools مرورگر را نیز بررسی کنید تا خطایی در آن ظاهر نشده باشد. برای مثال اگر فراموش کرده باشید تا HttpModule را به app.module اضافه کنید، خطای no provider for HttpModule را مشاهده خواهید کرد.


مدیریت رخداد submit فرم در Angular

تا اینجا کار برپایی تنظیمات اولیه‌ی کار با سرویس Http را انجام دادیم. مرحله‌ی بعد مدیریت رخداد submit فرم است. به همین جهت فایل src\app\employee\employee-register\employee-register.component.html را گشوده و سپس رخدادگردان submit را به فرم آن اضافه کنید:
<form #form="ngForm" (submit)="submitForm(form)" novalidate>
در حین رخدادگردانی submit می‌توان به template reference variable تعریف شده‌ی form# برای دسترسی به وهله‌ای از ngForm نیز کمک گرفت.
export class EmployeeRegisterComponent implements OnInit {
  submitForm(form: NgForm) {
    console.log(this.model);
    console.log(form.value);
  }
}
امضای متد submitForm را در اینجا مشاهده می‌کنید. form دریافتی آن از نوع NgForm است که در ابتدای فایل import شده‌است.
در همین حال اگر بر روی دکمه‌ی ok کلیک کنیم، چنین خروجی را در کنسول developer مروگر می‌توان مشاهده کرد:


اولین مورد، محتوای this.model است و دومی محتوای form.value را گزارش کرده‌است. همانطور که مشاهده می‌کنید، مقدار form.value بسیار شبیه است به وهله‌ای از مدلی که در سطح کلاس تعریف کرده‌ایم و این مقدار همواره توسط Angular نگهداری و مدیریت می‌شود. بنابراین حتما الزامی نیست تا مدلی را جهت کار با فرم‌های مبتنی بر قالب‌ها به صورت جداگانه‌ای تهیه کرد. توسط شیء form نیز می‌توان به تمام اطلاعات فیلدها دسترسی یافت.


تکمیل سرویس ارسال اطلاعات به سرور

در ادامه می‌خواهیم اطلاعات مدل فرم را به سرور ارسال کنیم. برای این منظور سرویس FormPoster را به صورت ذیل تکمیل می‌کنیم:
import { Injectable } from "@angular/core";
import { Http, Response, Headers, RequestOptions } from "@angular/http";

import { Observable } from "rxjs/Observable";
import "rxjs/add/operator/do";
import "rxjs/add/operator/catch";
import "rxjs/add/observable/throw";
import "rxjs/add/operator/map";
import "rxjs/add/observable/of";

import { Employee } from "./employee";

@Injectable()
export class FormPosterService {
  private baseUrl = "api/employee";

  constructor(private http: Http) {}

  private extractData(res: Response) {
    const body = res.json();
    return body.fields || {};
  }

  private handleError(error: Response): Observable<any> {
    console.error("observable error: ", error);
    return Observable.throw(error.statusText);
  }

  postEmployeeForm(employee: Employee): Observable<Employee> {
    const body = JSON.stringify(employee);
    const headers = new Headers({ "Content-Type": "application/json" });
    const options = new RequestOptions({ headers: headers });

    return this.http
      .post(this.baseUrl, body, options)
      .map(this.extractData)
      .catch(this.handleError);
  }
}
برای کار با Observables یا می‌توان نوشت 'import 'rxjs/Rx که تمام بسته‌ی RxJS را import می‌کند، یا همانند این مثال بهتر است تنها اپراتورهایی را که به آن‌ها نیاز پیدا می‌کنیم، import نمائیم. به این ترتیب حجم نهایی ارائه‌ی برنامه نیز کاهش خواهد یافت.
در متد postEmployeeForm، ابتدا توسط JSON.stringify محتوای شیء کارمند encode می‌شود. البته متد post اینکار را به صورت توکار نیز می‌تواند مدیریت کند. سپس ذکر هدر مناسب در اینجا الزامی است تا در سمت سرور بتوانیم اطلاعات دریافتی را به شیء متناظری نگاشت کنیم. در غیراینصورت model binder سمت سرور نمی‌داند که چه نوع فرمتی را دریافت کرده‌است و چه نوع decoding را باید انجام دهد.
در قسمت map، کار بررسی اطلاعات دریافتی از سرور را انجام خواهیم داد و اگر در این بین خطایی وجود داشت، توسط متد handleError در کنسول developer مرورگر نمایش داده می‌شود.
خروجی متد postEmployeeForm یک Observable است. بنابراین تا زمانیکه یک subscriber نداشته باشد، اجرا نخواهد شد. به همین جهت به کلاس EmployeeRegisterComponent مراجعه کرده و متد submitForm را به نحو ذیل تکمیل می‌کنیم:
  submitForm(form: NgForm) {
    console.log(this.model);
    console.log(form.value);

    // validate form
    this.validatePrimaryLanguage(this.model.primaryLanguage);
    if (this.hasPrimaryLanguageError) {
      return;
    }

    this.formPoster
      .postEmployeeForm(this.model)
      .subscribe(
        data => console.log("success: ", data),
        err => console.log("error: ", err)
      );
  }
در اینجا ابتدا اعتبارسنجی سفارشی drop down را که در قسمت قبل بررسی کردیم، قرار داده‌ایم. پس از آن متد postEmployeeForm سرویس formPoster فراخوانی شده‌است و در اینجا کار subscribe به نتیجه‌ی عملیات صورت گرفته‌است که می‌تواند حاوی اطلاعاتی از سمت سرور و یا خطایی در این بین باشد.

یک نکته: اگر علاقمند باشید تا ساختار واقعی شیء NgForm را مشاهده کنید، در ابتدای متد فوق، console.log(form.form) را فراخوانی کنید و سپس شیء حاصل را در کنسول developer مرورگر بررسی نمائید.


تکمیل Web API برنامه‌ی ASP.NET Core جهت دریافت اطلاعات از کلاینت‌ها

در ابتدای سرویس formPoster، یک چنین تعریفی را داریم:
export class FormPosterService {
  private baseUrl = "api/employee";
به همین جهت نیاز است سرویس Web API سمت سرور خود را بر این مبنا تکمیل کنیم.
ابتدا مدل زیر را به پروژه‌ی ASP.NET Core جاری، معادل نمونه‌ی تایپ‌اسکریپتی سمت کلاینت آن اضافه می‌کنیم. البته در اینجا یک Id نیز اضافه شده‌است:
namespace AngularTemplateDrivenFormsLab.Models
{
    public class Employee
    {
        public int Id { set; get; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public bool IsFullTime { get; set; }
        public string PaymentType { get; set; }
        public string PrimaryLanguage { get; set; }
    }
}

سپس کنترلر جدید EmployeeController را با محتوای ذیل اضافه خواهیم کرد:
using Microsoft.AspNetCore.Mvc;
using AngularTemplateDrivenFormsLab.Models;

namespace AngularTemplateDrivenFormsLab.Controllers
{
    [Route("api/[controller]")]
    public class EmployeeController : Controller
    {
        public IActionResult Post([FromBody] Employee model)
        {
            //todo: save model

            model.Id = 100;
            return Created("", new { fields = model });
        }
    }
}
این کنترلر با شیوه‌ی Web API تعریف شده‌است. مسیریابی آن با api شروع می‌شود تا با مسیر baseUrl سرویس formPoster تطابق پیدا کند.
در اینجا پس از ثبت فرضی مدل، Id آن به همراه اطلاعات مدل، به نحوی که ملاحظه می‌کنید، بازگشت داده شده‌است. این نوع خروجی، یک چنین JSON ایی را تولید می‌کند:
{"fields":{"id":100,"firstName":"Vahid","lastName":"N","isFullTime":true,"paymentType":"FullTime","primaryLanguage":"Persian"}}
به همین جهت است که در متد extractData، دسترسی به body.fields را مشاهده می‌کنید. این fields در اینجا دربرگیرنده‌ی اطلاعات بازگشتی از سرور است (نام آن دلخواه است و درصورت تغییر آن در سمت سرو، باید این نام را در متد extractData نیز اصلاح کنید).
  private extractData(res: Response) {
    const body = res.json();
    return body.fields || {};
  }
اکنون اگر برنامه را با دستورات dotnet watch build و ng build --watch اجرا کنیم، بر روی پورت 5000 قابل دسترسی خواهد بود و پس از ارسال فرم به سرور، چنین خروجی را می‌توان در کنسول developer مرورگر مشاهده کرد:


نمایش success به همراه شیءایی که از سمت سرور دریافت شده‌است؛ که حاصل اجرای سطر ذیل در متد submitForm است:
 data => console.log("success: ", data)
همانطور که مشاهده می‌کنید، این شیء به همراه Id نیز هست. بنابراین درصورت نیاز به آن در سمت کلاینت، خاصیت معادل آن‌را به کلاس کارمند اضافه کرده و در همین سطر فوق می‌توان به آن دسترسی یافت.


بارگذاری اطلاعات drop down از سرور

تا اینجا اطلاعات drop down نمایش داده شده از یک آرایه‌ی مشخص سمت کلاینت تامین شدند. در ادامه قصد داریم تا آن‌ها را از سرور دریافت کنیم. به همین جهت اکشن متد ذیل را به کنترلر سمت سرور برنامه اضافه کنید:
[HttpGet("/api/[controller]/[action]")]
public IActionResult Languages()
{
    string[] languages = { "Persian", "English", "Spanish", "Other" };
    return Ok(languages);
}
که برای آزمایش آن می‌توانید مسیر http://localhost:5000/api/employee/languages را جداگانه در مرورگر درخواست کنید.
پس از آن در سمت کلاینت این تغییرات نیاز هستند:
ابتدا به سرویس FormPosterService دو متد ذیل را اضافه می‌کنیم که کار آن‌ها دریافت و پردازش اطلاعات از api/employee/languages سمت سرور هستند:
  private extractLanguages(res: Response) {
    const body = res.json();
    return body || {};
  }

  getLanguages(): Observable<any> {
    return this.http
      .get(`${this.baseUrl}/languages`)
      .map(this.extractLanguages)
      .catch(this.handleError);
  }
اینبار چون خروجی سمت سرور را مانند قبل (متد extractData) داخل فیلدی مانند fields محصور نکردیم، همان body دریافتی بازگشت داده شده‌است.
پس از آن دو تغییر ذیل را نیاز است به EmployeeRegisterComponent اعمال کنیم:
  languages = [];

  ngOnInit() {
    this.formPoster
      .getLanguages()
      .subscribe(
        data => this.languages = data,
        err => console.log("get error: ", err)
      );
  }
ابتدا آرایه‌ی زبان‌ها با یک آرایه‌ی خالی مقدار دهی شده‌است و سپس در متد ngOnInit، کار دریافت اطلاعات آن از سرور، صورت گرفته‌است.

مشکل! ممکن است مدت زمانی طول بکشد تا این اطلاعات از سمت سرور دریافت شوند. در این حالت می‌توان به شکل زیر در فایل employee-register.component.html فرم را تا زمان پر شدن دراپ داون آن مخفی کرد:
<h3 *ngIf="languages.length == 0">Loading...</h3>
<div class="container" *ngIf="languages.length > 0">
در این حالت هر زمانیکه آرایه‌ی زبان‌ها پر شد، loading حذف شده و div نمایان می‌گردد.

کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: angular-template-driven-forms-lab-05.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کرده‌اید. سپس به ریشه‌ی پروژه وارد شده و دو پنجره‌ی کنسول مجزا را باز کنید. در اولی دستورات:
>npm install
>ng build --watch
و در دومی دستورات ذیل را اجرا کنید:
>dotnet restore
>dotnet watch run
اکنون می‌توانید برنامه را در آدرس http://localhost:5000 مشاهده و اجرا کنید.
مطالب
Expression Blend WPF Tutorial

دنبال یک سری ویدیوی آموزشی Expression blend بودم که آدرس زیر را پیدا کردم:


امکان مشاهده‌ی رایگان آن‌ها موجود است، همچنین اگر برنامه‌ی internet download manager را نصب کنید، هنگام گشودن هر صفحه، یک آیکون ذخیره سازی ویدیوی مورد نظر نیز ظاهر می‌شود که به این صورت می‌توان تمام ویدیوها را دانلود کرد.


نظرات مطالب
Embed کردن SQL Server Express 2008 در یک برنامه

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

1- InstallShield

2- InstallAware

3- Advanced Installer

مورد اول ، از قدیمی‌ترین نرم افزارها و  حرفه ای می‌باشد. ولی متاسفانه در تهیه نسخه به روز آن کمی دچار مشکل خواهید شد.

مورد دوم ادعای مقایسه با اینستال شیلد را دارد .از عیوبی که من در استفاده از این نرم افزار دیدم میتوان به حجم بالای نرم افزار اصلی اشاره کرد که بیش از دو گیگابایت است و هر دفعه نسخه جدید اومد شما باید مجدداٌ این حجم را دانلود کنید.

مورد سوم که بهترین گزینه نیز می‌باشد ، بسیار خوش دست و سبک می‌باشد. و به راحتی تمام موارد مورد نیاز جهت اجرای برنامه‌های شما را نصب می‌نماید.

مطالب
کار با نوع داده‌ی HierarchyID توسط Entity framework
نوع داده‌ی HierarchyID به همراه SQL Server 2008 برای کار با داده‌هایی با ساختار درختی ارائه شد. در حال حاضر هیچکدام از ORMهای موجود، پشتیبانی رسمی را از این نوع داده به عمل نمی‌آورند؛ اما با توجه به سورس باز بودن Entity framework، یک Fork مستقل از آن تهیه شده‌است و این نوع داده‌ی جدید به همراه متدهای مرتبط با آن، به این Fork اضافه شده‌اند.
- اصل Fork در اینجا
- تاریخچه‌ی این Fork غیر رسمی در اینجا
- بسته‌ی نیوگت آن در اینجا

چون تیم EF در نگارش فعلی این کتابخانه حاضر به افزودن این نوع جدید نشده‌است، بنابراین بجای بسته‌ی اصلی Entity framework نیاز است بسته‌ی EntityFrameworkWithHierarchyId را نصب کنید.
 PM> install-package EntityFrameworkWithHierarchyId

یک تذکر مهم:
چون امضای دیجیتال این بسته، با امضای دیجیتال بسته‌ی اصلی EF یکی نیست، اگر پروژه‌ی شما صرفا از EF استفاده می‌کند، مشکلی نخواهید داشت. اما اگر برای مثال از ASP.NET Identity کامپایل شده‌ی برای کار با EF اصلی استفاده کنید، پیام یافت نشدن DLL مرتبط را دریافت خواهید کرد.


تعریفی مدلی با خاصیتی از نوع جدید HierarchyId

public class Employee
{
    public int Id { get; set; }
 
    [Required, MaxLength(100)]
    public string Name { get; set; }
 
    [Required]
    public HierarchyId Node { get; set; } // نوع داده جدید
}
در اینجا مدلی را ملاحظه می‌کنید که از نوع داده‌ی جدید HierarchyId استفاده می‌کند. همانطور که عنوان شد این نوع در بسته‌ی EntityFrameworkWithHierarchyId موجود است.


تعریف Context و مقدار دهی اولیه‌ی آن

در این حالت Context برنامه به همراه تنظیمات اولیه‌ی Migrations آن یک چنین شکلی را پیدا خواهد کرد:
public class MyContext : DbContext
{
    public DbSet<Employee> Employees { get; set; }
 
    public MyContext()
        : base("Connection1")
    {
        this.Database.Log = log => Console.WriteLine(log);
    }
}
 
public class Configuration : DbMigrationsConfiguration<MyContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = true;
        AutomaticMigrationDataLossAllowed = true;
    }
 
    protected override void Seed(MyContext context)
    {
        if (context.Employees.Any())
            return;
 
        context.Database.ExecuteSqlCommand(
            "ALTER TABLE [dbo].[Employees] ADD NodePath as Node.ToString() persisted");
        context.Database.ExecuteSqlCommand(
            "ALTER TABLE [dbo].[Employees] ADD Level AS Node.GetLevel() persisted");
        context.Database.ExecuteSqlCommand(
            "ALTER TABLE [dbo].[Employees] ADD ManagerNode as Node.GetAncestor(1) persisted");
        context.Database.ExecuteSqlCommand(
            "ALTER TABLE [dbo].[Employees] ADD ManagerNodePath as Node.GetAncestor(1).ToString() persisted");
 
        context.Database.ExecuteSqlCommand(
            "ALTER TABLE [dbo].[Employees] ADD CONSTRAINT [UK_EmployeeNode] UNIQUE NONCLUSTERED (Node)");
        context.Database.ExecuteSqlCommand(
            "ALTER TABLE [dbo].[Employees]  WITH CHECK ADD CONSTRAINT [EmployeeManagerNodeNodeFK] " +
            "FOREIGN KEY([ManagerNode]) REFERENCES [dbo].[Employees] ([Node])");
 
        context.Employees.Add(new Employee { Name = "Root", Node = new HierarchyId("/") });
        context.Employees.Add(new Employee { Name = "Emp1", Node = new HierarchyId("/1/") });
        context.Employees.Add(new Employee { Name = "Emp2", Node = new HierarchyId("/2/") });
        context.Employees.Add(new Employee { Name = "Emp3", Node = new HierarchyId("/1/1/") });
        context.Employees.Add(new Employee { Name = "Emp4", Node = new HierarchyId("/1/1/1/") });
        context.Employees.Add(new Employee { Name = "Emp5", Node = new HierarchyId("/2/1/") });
        context.Employees.Add(new Employee { Name = "Emp6", Node = new HierarchyId("/1/2/") });
 
        base.Seed(context);
    }
}
در اینجا نحوه‌ی تعریف رکوردهای جدید مبتنی بر HierarchyId را مشاهده می‌کنید که توسط آن‌ها تعدادی کارمند، در یک سازمان فرضی ثبت شده‌اند.
همچنین چند فیلد محاسباتی نیز بر اساس امکانات توکار SQL Server اضافه شده‌اند. متدهایی مانند ToString، GetLevel، GetAncestor و امثال آن جزئی از پیاده سازی توکار SQL Server هستند. همچنین این متدها توسط کتابخانه‌ی EntityFrameworkWithHierarchyId نیز ارائه شده‌اند.


کوئری نویسی

مرتب سازی رکوردها بر اساس HierarchyId آن‌ها

using (var context = new MyContext())
{
    Console.WriteLine("\ngetItems OrderByDescending(employee => employee.Node)");
 
    var employees = context.Employees.OrderByDescending(employee => employee.Node).ToList();
    foreach (var employee in employees)
    {
        Console.WriteLine("{0} {1}", employee.Id, employee.Node);
    }
 }
با این خروجی
SELECT
    [Extent1].[Id] AS [Id],
    [Extent1].[Name] AS [Name],
    [Extent1].[Node] AS [Node]
    FROM [dbo].[Employees] AS [Extent1]
    ORDER BY [Extent1].[Node] DESC


6 /2/1/
3 /2/
7 /1/2/
5 /1/1/1/
4 /1/1/
2 /1/
1 /


یافتن یک HierarchyId خاص و سپس یافتن کلیه‌ی فرزندان آن در یک سطح پایین‌تر

using (var context = new MyContext())
{
    Console.WriteLine("\nGetAncestor(1) of /1/");
 
    var firstItem = context.Employees.Single(employee => employee.Node == new HierarchyId("/1/"));
    foreach (var item in context.Employees.Where(employee => firstItem.Node == employee.Node.GetAncestor(1)))
    {
        Console.WriteLine("{0} {1}", item.Id, item.Name);
    }
}
این کوئری را به این شکل نیز می‌توان عنوان کرد: یافتن یک HierarchyId و سپس یافتن کلیه نودهایی که والدشان (GetAncestor) این HierarchyId است. عدد یک در اینجا مشخص کننده‌ی Level یا سطح است.
با این خروجی:
SELECT TOP (2)
    [Extent1].[Id] AS [Id],
    [Extent1].[Name] AS [Name],
    [Extent1].[Node] AS [Node]
    FROM [dbo].[Employees] AS [Extent1]
    WHERE cast('/1/' as hierarchyid) = [Extent1].[Node]

SELECT
    [Extent1].[Id] AS [Id],
    [Extent1].[Name] AS [Name],
    [Extent1].[Node] AS [Node]
    FROM [dbo].[Employees] AS [Extent1]
    WHERE (@p__linq__0 = ([Extent1].[Node].GetAncestor(1))) OR ((@p__linq__0 IS
NULL) AND ([Extent1].[Node].GetAncestor(1) IS NULL))
-- p__linq__0: '/1/' (Type = Object)

4 Emp3
7 Emp6

کوئری‌های فوق را می‌توان بجای استفاده از متد GetAncestor، با استفاده از متد IsDescendantOf به شکل زیر نیز نوشت:
var list = context.Employees.Where(
          employee => employee.Node.IsDescendantOf(new HierarchyId("/1/")) &&
                              employee.Node.GetLevel() == 2).ToList();
با این خروجی SQL (یک کوئری بجای دو کوئری):
SELECT
    [Extent1].[Id] AS [Id],
    [Extent1].[Name] AS [Name],
    [Extent1].[Node] AS [Node]
    FROM [dbo].[Employees] AS [Extent1]
    WHERE (([Extent1].[Node].IsDescendantOf(cast('/1/' as hierarchyid))) = 1) 
    AND (2 = ([Extent1].[Node].GetLevel()))


جابجا کردن نودها توسط متد GetReparentedValue

در کوئری ذیل، تمامی فرزندان ریشه‌ی /1/ یافت شده و سپس والد آن‌ها به صورت پویا تغییر داده می‌شود:
var items = context.Employees.Where(employee => employee.Node.IsDescendantOf(new HierarchyId("/1/")))
    .Select(employee => new
    {
        Id = employee.Id,
        OrigPath = employee.Node,
        ReparentedValue = employee.Node.GetReparentedValue(new HierarchyId("/1/"), HierarchyId.GetRoot()),
        Level = employee.Node.GetLevel()
    }).ToList();
 
foreach (var item in items)
{
    Console.WriteLine("Id:{0}; OrigPath:{1}; ReparentedValue:{2}; Level:{3}", item.Id, item.OrigPath, item.ReparentedValue, item.Level);
}
با این خروجی
SELECT
    [Extent1].[Id] AS [Id],
    [Extent1].[Node] AS [Node],
    [Extent1].[Node].GetReparentedValue(cast('/1/' as hierarchyid), hierarchyid::GetRoot()) AS [C1],
    [Extent1].[Node].GetLevel() AS [C2]
    FROM [dbo].[Employees] AS [Extent1]
    WHERE ([Extent1].[Node].IsDescendantOf(cast('/1/' as hierarchyid))) = 1


Id:2; OrigPath:/1/; ReparentedValue:/; Level:1
Id:4; OrigPath:/1/1/; ReparentedValue:/1/; Level:2
Id:5; OrigPath:/1/1/1/; ReparentedValue:/1/1/; Level:3
Id:7; OrigPath:/1/2/; ReparentedValue:/2/; Level:2

کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید
HierarcyIdTests.zip
اشتراک‌ها
کنفرانس مجازی blazor day

The blazor day is the online event around Blazor technologies. Originally, this event was organized by three MVP friends, Adrien, Christophe, and Denis. Their objective of this event is to share their passion for .NET and more particularly Blazor. Joined by the famous Charline to upgrade the event to the next level. Blazor is part of the ASP.NET Core product which is offered in Open Source by Microsoft.... 

کنفرانس مجازی blazor day
مطالب
بررسی دقیق‌تر صفحات آبی ویندوز

حدود یک سال قبل کامپیوتری را که داشتم (اینتل پنتیوم 4) به یک AMD دوهسته‌ای ارتقاء دادم و هفته‌ی اول پس از ارتقاء، روزگار من سیاه شد! روزهای اول 2 بار کرش ویندوز و مشاهده صفحه آبی و روزهای بعد تا 7 بار این اتفاق تکرار می‌شد. حتی تا تعویض مادربرد جدید هم پیش رفتم ولی تاثیری نداشت. تست رم و غیره هم انجام شد، مشکلی نبود. خلاصه اینجا بود که از سر ناچاری به این فکر افتادم که آیا این پیغام‌های صفحه‌ی آبی ویندوز را می‌شود تفسیر کرد؟ مشکل دقیقا از کجاست؟ چون در این موارد به هر کسی که مراجعه کنید بر اساس تجربه قبلی یک نسخه برای شما خواهد پیچید. رمت خرابه! بایوست رو ارتقاء بده! (این مورد تاثیر داشت! ولی تعداد کرش‌ها صفر نشد) مادربردت مشکل داره و ...

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



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

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



اکنون بعد از هر کرش و صفحه آبی ویندوز یک فایل دامپ در دایرکتوری C:\WINDOWS\Minidump تشکیل می‌شود. برای آنالیز این فایلها به صورت زیر می‌شود عمل کرد:
ابتدا برنامه زیر را دانلود کنید:
Debugging Tools for Windows

پس از نصب، Debugging Tools for Windows را خواهید داشت که جهت دیباگ کردن سیستم و آنالیز فایلهای دامپ و غیره کاربرد دارد.

سپس مطالعه مقاله زیر در مورد نحوه استفاده از این ابزار بسیار مفید است:
http://support.microsoft.com/kb/315263

به صورت خلاصه :
یک فایل bat درست کنید با محتویات زیر و دقیقا به همین شکل:
c:\windbg\kd -y srv*c:\symbols*http://msdl.microsoft.com/download/symbols -i c:\windows\i386 -z %1


در این دستور سه مورد قابل ملاحظه است:
الف) مسیر فایل kd.exe که توسط پکیج Debugging Tools for Windows نصب می‌شود. (مطابق سیستم خودتان آنرا اصلاح کنید)
ب) مسیر c:\windows\i386 بدین معنا است که دایرکتوری i386 سی دی ویندوز را در این مسیر کپی کرده‌اید یا خواهید کرد (نیاز به یک ویندوز تر و تازه و نصب نشده خواهد بود).
ج) مسیر c:\symbols خودبخود ایجاد خواهد شد و فایلهای مربوطه از سایت مایکروسافت توسط برنامه kd.exe دانلود می‌شود (بنابراین باید دسترسی به اینترنت نیز داشت).

فرض کنید نام این فایل را test.bat گذاشته‌اید.
برای آنالیز فایل Mini102607-07.dmp در دایرکتوری مینی دامپ ویندوز (07 در اینجا یعنی هفتمین کرش روز مربوطه!) دستور زیر را در خط فرمان صادر کنید:
test.bat C:\WINDOWS\Minidump\Mini102607-07.dmp
پس از مدتی این برنامه کار آنالیز را تمام خواهد کرد و گزارشی را ارائه می‌دهد (یک فایل log متنی تشکیل خواهد شد).
نتیجه یک نمونه از این آنالیزهای سیستم من به صورت زیر بود:
BAD_POOL_CALLER (c2)
The current thread is making a bad pool request. Typically this is at a bad IRQL level or double freeing the same allocation, etc.
Arguments:
Arg1: 00000007, Attempt to free pool which was already freed
Arg2: 00000cd4, (reserved)
Arg3: 02060008, Memory contents of the pool block
Arg4: 88b4a118, Address of the block of pool being deallocated

Debugging Details:
------------------

POOL_ADDRESS: 88b4a118

FREED_POOL_TAG: TCPc

BUGCHECK_STR: 0xc2_7_TCPc

CUSTOMER_CRASH_COUNT: 4

DEFAULT_BUCKET_ID: COMMON_SYSTEM_FAULT

PROCESS_NAME: System

LAST_CONTROL_TRANSFER: from 8054a583 to 804f9deb

STACK_TEXT:
ba4f3874 8054a583 000000c2 00000007 00000cd4 nt!KeBugCheckEx+0x1b
ba4f38c4 b043d3ff 88b4a118 00000000 ba4f390c nt!ExFreePoolWithTag+0x2a3
ba4f38d4 b043cca3 883ae760 883ae7f4 883ae7f4 tcpip!TCPClose+0x16
ba4f390c b02f3161 8a74fe20 883ae760 b02f2a6d tcpip!TCPDispatch+0x101
WARNING: Stack unwind information not available. Following frames may be wrong.
ba4f3984 b03e2046 00000001 00000000 ba4f39d8 vsdatant+0x45161
ba4f39d8 b03e921c 00000008 ba4f3aac 00000000 ipnat!NatpRedirectQueryHandler+0x250
ba4f3a70 00000000 8837d8e8 0000000d 000005ee ipnat!NatpDirectPacket+0xd2

STACK_COMMAND: kb

FOLLOWUP_IP:
vsdatant+45161
b02f3161 ?? ???

SYMBOL_STACK_INDEX: 4

SYMBOL_NAME: vsdatant+45161

FOLLOWUP_NAME: MachineOwner

MODULE_NAME: vsdatant

IMAGE_NAME: vsdatant.sys

DEBUG_FLR_IMAGE_TIMESTAMP: 46e0766a

FAILURE_BUCKET_ID: 0xc2_7_TCPc_vsdatant+45161

BUCKET_ID: 0xc2_7_TCPc_vsdatant+45161

Followup: MachineOwner

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

خوب! تا اینجا مشخص شد که دلیل کرش، درایور vsdatant.sys است. با جستجو در اینترنت مشخص شد که این درایور مربوط به فایروال زون آلارم است! (همین عبارت بالا یا نام درایور ذکر شده را مستقیما در گوگل جستجو کنید)
پس از آن زون آلارم را با outpost firewall جایگزین کردم و تا الان کرشی حاصل نشده است (حتی یکبار از سال قبل تا به امروز). جدا زندگی من مختل شده بود. تصور کنید سیستم شما روزی 7 بار کرش کند!! و چه تصورات نامربوطی را نسبت به فروشنده سخت افزار در ذهن خود مرور کرده باشید!

خلاصه‌ی کلام:
صفحات آبی ویندوز قابل تفسیر هستند. پدید آمدن آنها الزاما بدلیل وجود سخت افزار معیوب نیست و به صرف اینکه شخصی به شما گفته "رمت خرابه!" اکتفا نکنید.

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

اشتراک‌ها
نحوه ساخت اپلیکیشنهای Real-time تحت وب با ASP.NET Core SignalR

 show how easy it is to add real-time functionality to your web applications using ASP.NET Core SignalR. They discuss topics such as targeting with clients, SignalR transports, and options for running your SignalR application in the cloud.

Now, you can even leverage the Hub protocol spec is the available on GitHub if you're interested in creating your own SignalR client.

  • [02:10]  - Quick introduction to SignalR
  • [06:06] - Targeting clients
  • [10:51] - Leveraging the Hub protocol
  • [12:18]  - SignalR Transports
  • [13:55] - Hosting SignalR in a background service
  • [16:22] - Azure SignalR Service
  • [18:22] - The SignalR Java client on Android
  • [22:34] - Streaming with SignalR 
نحوه ساخت اپلیکیشنهای Real-time تحت وب با ASP.NET Core SignalR