نظرات مطالب
اعمال تزریق وابستگی‌ها به مثال رسمی ASP.NET Identity
طبق فرمایش شما  از کد زیر استفاده کردم اما claims  به نام emailaddress  یافت نمی‌شود.  
   public async Task<ClaimsIdentity> GenerateUserIdentityAsync(User applicationUser)
        {
            // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await CreateIdentityAsync(applicationUser, DefaultAuthenticationTypes.ApplicationCookie);
            // Add custom user claims here
            userIdentity.AddClaim(new Claim("emailaddress", applicationUser.Email));
            return userIdentity;
        }

و زمانی که کاربر وارد سایت می‌شود GenerateUserIdentityAsync  را فراخوانی می‌کنم.
  public virtual async Task<ActionResult> Login(LoginViewModel viewModel, string returnUrl)
  {

       // ... more code
        var user =  await this._userManager.FindByNameAsync(viewModel.UserName);
        await this._userManager.GenerateUserIdentityAsync(user);

            return this.View(viewModel);
  }

و در Container :
 _.For<IPrincipal>().Use(() => HttpContext.Current.User);
نحوه استفاده :
var useremail = this._principal.GetClaimValue("emailaddress");

مطالب
آشنایی با CLR: قسمت یازدهم
انتشار نوع‌ها  (Types) به یک ماژول
در این قسمت به نحوه‌ی تبدیل سورس به یک فایل قابل انتشار می‌پردازیم. کد زیر را به عنوان مثال در نظر بگیرید:
public sealed class Program {
  public static void Main() {
    System.Console.WriteLine("Hi");
  }
}
این کد یک ارجاع به نام کنسول دارد که این ارجاع، داخل فایلی به نام mscorlib.dll قرار دارد. پس برنامه‌ی ما نوعی را دارد، که آن نوع توسط شرکت دیگری پیاده سازی شده است. برای ساخت برنامه‌ی کد بالا، کدها را داخل فایلی با نام program.cs قرار داده و با دستور زیر در خط فرمان آن را کامپایل می‌کنیم:
csc.exe /out:Program.exe /t:exe /r:MSCorLib.dll Program.cs
کد بالا با سوئیچ اول می‌گوید که فایلی را با نام program.exe درست کن و با سوئیچ دوم می‌گوید که این برنامه از نوع کنسول هست.
موقعیکه کامپایلر فایل سورس را مورد بررسی قرار می‌دهد، متوجه متد writerline می‌گردد؛ ولی از آنجاکه این نوع توسط شما ایجاد نشده است و یک نوع خارجی است، شما باید یک مجموعه از ارجاعات را به کمپایلر داده تا آن نوع را در آن‌ها بیابد. ارائه این ارجاعات به کامپایلر توسط سوئیچ r/ که در خط بالا استفاده شده است، صورت می‌گیرد.


mscorlib یک فایل سورس است که شامل همه‌ی نوع‌ها، از قبیل int,string,byte و خیلی از نوع‌های دیگر می‌شود. از آنجائیکه استفاده‌ی از این نوع‌ها به طور مکرر توسط برنامه نویس‌ها صورت می‌گیرد، کمپایلر به طور خودکار این کتابخانه را به لیست ارجاعات اضافه می‌کند. به بیان دیگر خط بالا به شکل زیر هم قابل اجراست:
csc.exe /out:Program.exe /t:exe Program.cs

به علاوه از آنجایی که بقیه‌ی سوئیچ‌ها هم مقدار پیش فرضی را دارند، خط زیر هم معادل خطوط بالاست:
csc.exe Program.cs

اگر به هر دلیلی دوست ندارید که سمت mscorlib ارجاعی صورت بگیرید، می‌توانید از دستور زیر استفاده کنید:
/nostdlib

مایکروسافت موقعی از این سوئیچ بالا استفاده کرده است که خواسته است خود mscorlib را بسازد. با اضافه کردن این سوئیچ، کد مثال که حاوی شیء یا نوع کنسول است به خطا برخواهد خورد چون تعریف آن در mscorlib صورت گرفته و شما با سوئیچ بالا دسترسی به آن را ممنوع اعلام کرد‌ه‌اید و از آنجاکه این نوع تعریف نشده، برنامه ازکامپایل بازخواهد ماند.
csc.exe /out:Program.exe /t:exe /nostdlib Program.cs

بیایید نگاه دقیقتری به فایل program.exe ساخته شده بیندازیم؛ دقیقا این فایل چه نوع فایلی است؟ برای بسیاری از مبتدیان، این یک فایل اجرایی است که در هر دو ماشین 32 و 64 بیتی قابل اجراست. ویندوز از سه نوع برنامه پشتیبانی می‌کند: CUI یا برنامه‌های تحت کنسول، برنامه‌هایی با رابط گرافیکی GUI و برنامه‌های مخصوص windows store که سوئیچ‌های آن به شرح زیر است:
//CUI
/t:exe

//GUI
/t:winexe

//Winsows store App
/t:appcontainerexe

قبل از اینکه بحث را در مورد سوئیچ‌ها به پایان برسانیم، اجازه دهید در مورد فایل‌های پاسخگو یا response file صحبت کنیم. یک فایل پاسخگو، فایلی است که شامل مجموعه‌ای از سوئیچ‌های خط فرمان می‌شود. موقعی‌که csc.exe اجرا می‌شود، به فایل پاسخگویی که شما به آن معرفی کرده‌اید مراجعه کرده و فرمان را با سوئیچ‌های داخل آن اجرا می‌کند. معرفی یک فایل پاسخگو به کامپایلر توسط علامت @ و سپس نام فایل صورت می‌گیرد و در این فایل هر خط، شامل یک سوئیچ است. مثلا فایل پاسخگوی response.rsp شامل سوئیچ‌های زیر است:
/out:MyProject.exe
/target:winexe

و برای در نظر گرفتن این سوئیچ‌ها فایل پاسخگو را به کامپایلر معرفی می‌کنیم:
csc.exe @MyProject.rsp CodeFile1.cs CodeFile2.cs

این فایل خیلی کار شما را راحت می‌کند و نمی‌گذارد در هر بار کامپایل، مرتب سوئیچ‌های آن را وارد کنید و کیفیت کار را بالا می‌برد. همچنین می‌توانید چندین فایل پاسخگو داشته باشید و هر کدام شامل سوئیچ‌های مختلفی تا اگر خواستید تنظیمات کامپایل را تغییر دهید، به راحتی تنها نام فایل پاسخگو را تغییر دهید. همچنین کامپایلر سی شارپ از چندین فایل پاسخگو هم پشتیبانی می‌کند و می‌توانید هر تعداد فایل پاسخگویی را به آن معرفی کنید. در صورتیکه فایل را با نام csc.rsp نامگذاری کرده باشید، نیازی به معرفی آن نیست چرا که کامپایلر در صورت وجود، آن را به طور خودکار خواهد خواند و به این فایل global response file یا فایل پاسخگوی عمومی گویند.
در صورتیکه چندین فایل پاسخگو که به آن فایل‌های محلی local می‌گویند، معرفی کنید که دستورات آن‌ها(سوئیچ) با دستورات داخل csc.rsp مقدار متفاوتی داشته باشند، فایل‌های محلی الویت بالاتری نسبت به فایل global داشته و تنظمیات آن‌ها روی فایل global رونوشت می‌گردند.

موقعی‌که شما دات نت فریمورک را نصب می‌کنید، فایل csc.rsp را با تنظیمات پیش فرض در مسیر زیر نصب می‌کند:
%SystemRoot%\
Microsoft.NET\Framework(64)\vX.X.X

حروف x نمایانگر نسخه‌ی دات نت فریمورکی هست که شما نصب کرده‌اید. آخرین ورژن از این فایل در زمان نگارش کتاب، شامل سوئیچ‌های زیر بوده است.
# This file contains command­line options that the C#
# command line compiler (CSC) will process as part
# of every compilation, unless the "/noconfig" option
# is specified.
# Reference the common Framework libraries
/r:Accessibility.dll
/r:Microsoft.CSharp.dll
/r:System.Configuration.dll
/r:System.Configuration.Install.dll
/r:System.Core.dll
/r:System.Data.dll
/r:System.Data.DataSetExtensions.dll
/r:System.Data.Linq.dll
/r:System.Data.OracleClient.dll
/r:System.Deployment.dll
/r:System.Design.dll
/r:System.DirectoryServices.dll
/r:System.dll
/r:System.Drawing.Design.dll
/r:System.Drawing.dll
/r:System.EnterpriseServices.dll
/r:System.Management.dll
/r:System.Messaging.dll
/r:System.Runtime.Remoting.dll
/r:System.Runtime.Serialization.dll
/r:System.Runtime.Serialization.Formatters.Soap.dll
/r:System.Security.dll
/r:System.ServiceModel.dll
/r:System.ServiceModel.Web.dll
/r:System.ServiceProcess.dll
/r:System.Transactions.dll
/r:System.Web.dll
/r:System.Web.Extensions.Design.dll
/r:System.Web.Extensions.dll
/r:System.Web.Mobile.dll
/r:System.Web.RegularExpressions.dll
/r:System.Web.Services.dll
/r:System.Windows.Forms.Dll
/r:System.Workflow.Activities.dll
/r:System.Workflow.ComponentModel.dll
/r:System.Workflow.Runtime.dll
/r:System.Xml.dll
/r:System.Xml.Linq.dll

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

نکته: در صورتی که قصد ارجاعی را دارید، می‌توانید آدرس مستقیم اسمبلی را هم ذکر کنید. ولی اگر تنها به نام اسمبلی اکتفا کنید، مسیرهای زیر جهت یافتن اسمبلی بررسی خواهند شد:
  • دایرکتوری برنامه
  • دایرکتوری که شامل فایل csc.exe می‌شود. که خود فایل mscorlib از همانجا خوانده می‌شود و مسیر آن شبیه مسیر زیر است:
%SystemRoot%\Microsoft.NET\Framework\v4.0.#####

  • هر دایرکتوری که توسط سوئیچ lib/ مشخص شده باشد.
  • هر دایرکتوری که توسط متغیر محلی lib مشخص شده باشد.
استفاده از سوئیچ noconfig/ هم باعث می‌شود که فایل‌های پاسخگوی از هر نوعی، چه عمومی و چه محلی، مورد استفاده قرار نگیرند. همچنین شما مجاز هستید که فایل csc.rsp را هم تغییر دهید؛ ولی این نکته را فراموش نکنید، در صورتی که برنامه‌ی شما به سیستمی دیگر منتقل شود، تنظیمات این فایل در آنجا متفاوت خواهد بود و بهتر هست یک فایل محلی را که همراه خودش هست استفاده کنید.
در قسمت بعدی نگاه دیگری بر متادیتا خواهیم داشت.
مطالب
آشنایی با فریمورک الکترون Electron
فریمورک الکترون، ساخته شده توسط Github، مدتی است سر و صدای زیادی به پا کرده است و شرکت‌های بزرگی در حال استفاده‌ی از این فریمورک در برنامه‌های دسکتاپ خود هستند که Microsoft Visual Studio Code یکی از آنهاست. الکترون از چند لحاظ مورد لطف جامعه‌ی برنامه نویسان قرار گرفته است که تعدادی از علل آن را بررسی می‌کنیم:

  1. ساخت برنامه‌های دسکتاپ به صورت چندسکویی (ویندوز، لینوکس، مک)
  2. استفاده از HTML,CSS,JavaScript که طراحان وب در این زمینه با آن به آسانی ارتباط برقرار می‌کنند.
  3. قابلیت استفاده از کتابخانه‌های قدرتمند تحت وب چون Bootstrap,Jquery,Angular Js و ...
  4. متن باز و رایگان است.
برای راه اندازی الکترون نیاز است که یکی از بسته‌های Node.js یا IO.js را نصب نمایید تا از طریق مخزن npm نسبت به نصب آن اقدام کنیم. محیط کنسول آن را باز میکنیم و مشغول نوشتن می‌شویم. ابتدا از طریق npm در دایرکتوری پروژه، فایل package.json را ایجاد می‌کنیم. بدین منظور دستور زیر را در کنسول وارد می‌کنیم:
D:\electron\test1>npm init
تعدادی سوال از شما میکند و بر اساس پاسخ‌هایتان فایل package.json را می‌سازد که فعلا می‌توانید وارد نکنید و بعدا طبق میلتان آن را ویرایش کنید. بعد از آن نیاز است الکترون را داخل این دایرکتوری نصب کنیم تا در لیست وابستگی‌های (Dependencies) فایل Package.json قرار بگیرد. برای نصب آن لازم است دستور زیر وارد کنید:
npm i electron-prebuilt --save-dev
دستور بالا با فلگ save  یا s، باعث می‌شود نسخه‌ی prebuilt الکترون به عنوان یکی از وابستگی‌ها، به سیستم اضافه شود و فلگ dev اعلام می‌دارد که بسته‌ی الکترون را در وابستگی‌های توسعه و دیباگینگ قرار بده.

 لازم است در اینجا توضیحی کوتاه در مورد انواع وابستگی‌ها داشته باشیم:

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

DevDependencies : این نوع از وابستگی‌های برای کارهای دیباگینگ و ... است؛ مثل آزمون واحد و ... که نیازی نیست در کامپایل نهایی لحاظ گردند. اگر این نوع کتابخانه‌ها را به جای devdependencies به dependencies ارسال کنید، اتفاق خاصی نمی‌افتد. ولی در حجم برنامه‌ی نهایی شما تاثیرگذار خواهند بود.

PeerDependencies: این نوع وابستگی‌ها برای معرفی بسته‌هایی استفاده می‌شوند که در پلاگین‌هایی که استفاده می‌کنید تاثیر دارند. ممکن است پلاگینی نیاز به استفاده‌ی از یک بسته را دارد، ولی آن را در کد، Require نکرده باشد (در مورد Require بعدا صحبت می‌کنیم). ولی برای اجرا نیاز به این بسته دارد. به همین دلیل از نسخه‌ی 3 به بعد، به شما هشدار میدهد که این بسته‌ها را نیز لحاظ کنید (تا نسخه‌ی npm2 به طور خودکار نصب می‌شد). همچنین نسخه بندی این وابستگی‌ها را نیز در نظر می‌گیرد. این حالت را می‌توانید مانند پلاگین‌های جی‌کوئری تصور کنید که نیاز است قبل از آن‌ها، کتابخانه‌ی جی‌کوئری صدا زده شود؛ در صورتی که در خود پلاگین، جی کوئری صدا زده نشده است.

ویرایشگر اتم

قبل از اینکه بخواهیم کدنویسی با هر زبانی را آغاز کنیم، عموما یک ادیتور مناسب را برای کارمان بر می‌گزینیم. الکترون نیازی به ادیتور خاصی ندارد و از Notepad گرفته تا هر ادیتور قدرتمند دیگری را می‌توانید استفاده کنید. ولی ادیتور اتم Atom که توسط خود الکترون هم تولید شده است، برای استفاده رایج است. ویژوال استودیو هم در این زمینه بسیار خوب و قدرتمند ظاهر شده است و حاوی Intellisense هوشمندی است.
 
این ادیتور که با ظاهری جذاب، توسط تیم گیت هاب تولید شده است، یک ویرایشگر متن باز با قابلیت توسعه و تغییر پذیری بالاست و از بسته‌های Node.js پشتیانی میکند و به صورت داخلی مجهز به سیستم گیت می‌باشد. بیشتر فناوری‌های استفاده شده در این ویرایشگر، رایگان بوده و دارای جامعه‌ی بزرگ متن باز می‌باشند. از فناوری‌های مورد استفاده‌ی آن می‌توان به الکترون، CoffeScript ، Node.js ,LESS و ... اشاره کرد. شعار سازندگان این ادیتور «یک ویرایشگر قابل هک برای قرن 21» می‌باشد.
برای پشتیبانی از زبان‌های مختلف، حاوی تعدادی زیادی پلاگین پیش فرض است مانند روبی ، سی شارپ، PHP ,Git,Perl,C/C++, Go,Objective-C,YAML و ...

آغاز کدنویسی
بگذارید کدنویسی را شروع کنیم. اگر اتم را نصب کرده باشید، می‌توانید با وارد کردن عبارت زیر، پروژه خود را در ادیتور باز کنید:
atom .
نماد "." به معنی دایرکتوری جاری است و به ادیتور اتم می‌گوید که این دایرکتوری را به عنوان یک پروژه، باز کن. بعد از باز شدن می‌توانید ساختار دایرکتوری و فایل‌ها را در سمت چپ ببینید. فایل package.json را باز کنید و به شکل زیر، آن را تغییر دهید:
{
  "name": "electron",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "electron ."
  },
  "author": "",
  "license": "ISC"
}
این تغییر، شامل حذف خصوصیت test و افزودن خصوصیت start به بخش scripts است. مقدار خصوصیت start را برابر . electron بگذارید تا موقع اجرا و تست پروژه، الکترون، پروژه‌ی موجود را در دایرکتوری جاری که فایل package.json در آن قرار دارد، اجرا کند. در بخش Main، نام فایل آغازین را نوشته است که باید آن را بسازید ( اگر این خصوصیت وجود هم نداشته باشد به طور پیش به این مقدار تنظیم شده است). به همین علت New File را اجرا کنید تا فایل Index.js را بسازید:
const electron = require('electron');
const {app} = electron;
const {BrowserWindow} = electron;
سه خط بالا را می‌نویسیم ، اولین خط نیاز ما را به کتابخانه و شیء الکترون فراهم می‌کند و بعد از آن، از الکترون درخواست دو شیء را به نام‌های app و BrowserWindow، می‌کنیم. شیء app مسئول چرخه‌ی زندگی اپلیکیشن است و موارد مربوط به آن را کنترل می‌کند؛ مثل رویدادهایی که ممکن است در برنامه رخ بدهند که در جلوتر با یکی از این رویدادهای آشنا می‌شویم. شیء BrowserWindow می‌تواند برای شما یک نمونه پنجره‌ی جدید را ایجاد کند و بتوانید آن پنجره را از این طریق مدیریت کنید.
let win;
app.on('ready', function() {
  // Create the browser window.
  win = new BrowserWindow({
           width: 800,
           height: 600
     });
});
در قسمت بعدی برای رویداد ready، یعنی زمانیکه الکترون آماده سازی‌ها را انجام داده است و برنامه آماده بارگذاری است، متدی را تعریف می‌کنیم که در آن یک پنجره‌ی با پهنای 800 پیکسل در 600 پیکسل، می‌سازد.این پنجره، پنجره‌ی اصلی شماست. کلمه‌ی کلید let و const را که می‌بینید، جز قوانین جدید Ecma Script هستند که در سایت جاری قبلا به آن‌ها پرداخته شده است و از تکرار آن خودداری می‌کنیم. دلیل اینکه متغیر win را به صورت عمومی تعریف کردیم این است که این احتمال زیاد می‌رود بعدا با اجرای سیستم Garbage در جاوااسکریپت، پنجره به طور خودکار بسته شود.
اکنون در کنسول می‌نویسیم:
npm start
حال npm با بررسی خصوصیت start در فایل package.json دستور . electron را اجرا خواهد کرد و برنامه‌ی ما، یک پنجره‌ی خالی را نمایش خواهد داد.
برای اینکه اولین برنامه واقعا خالی نباشد و ظاهری به آن بدهیم، یک فایل html می‌سازیم و در callback رویداد ready، بعد از ساخت پنجره آن را صدا می‌زنیم:
win.loadURL(`file://${__dirname}/index.html`);
با متد loadURL به راحتی می‌توانید یک صفحه‌ی وب را از شبکه و یا از روی سیستم، بخوانید. در بخش آرگومان، از پروتکل فایل استفاده شده است تا به آن بگوییم فایل مورد نظر روی سیستم جاری است. عبارت بعدی که به صورت template string تعریف شده‌است، حاوی مسیر index.js یا همان startup path است و سپس فایل index.html معرفی شده‌است. مجددا برنامه را اجرا کنید تا فایل index.html خود را داخل آن ببینید.
مطالب
ایجاد یک Repository در پروژه برای دستورات EF
ایجاد یک Pattern در پروژتون میتونه نظم, سرعت و زیبایی خاصی به کدتون بده. با وجود frameworkهای  و Patternهایی مسه MVC و MVVM برنامه نویسان را وادار کنه که همه  Actionهای یک پروژه رو به سمت کلاینت ببرن.تو یک فرصت دیگه در مورد فریمورک Knockout حتما تاپیک میزارم.امروز میخوام یک Pattern با استفاده از یک Interface و codefirst model براتون بزارم.
گام اول:ایجاد که class property
Public Class Employee
    Public Property ID As Integer
    Public Property Fname As String
    Public Property Bdate As DateTime
End Class  

گام دوم:ایجاد بانک با استفاده از CodeFirst
Imports System.Data.Entity
Public Class EmployeeDBContext : Inherits DBContext
    Public Property Employees As DbSet(Of Employee)
End Class 
گام سوم: ایجاد repository با استفاده از interface
Interface EmployeeRepository
    ReadOnly Property All As List(Of Employee)
    Function Find(id As Integer) As Employee
    Sub InsertOrUpdate(p As Employee)
    Sub Delete(id As Integer)
    Sub Save()
End Interface 
گام چهارم: تعریف کلاس برای implement کردن از iInterface
Public Class EmployeeClass : Implements EmployeeRepository
    Private DB As New EmployeeDBContext
    Public ReadOnly Property All As List(Of Employee) Implements EmployeeRepository.All
        Get
            Return DB.Employees.ToList()
        End Get
    End Property

    Public Sub Delete(id As Integer) Implements EmployeeRepository.Delete
        Dim query = DB.Employees.Single(Function(q) q.ID = id)
        DB.Employees.Remove(query)
    End Sub

    Public Function Find(id As Integer) As Employee Implements EmployeeRepository.Find
        Return DB.Employees.Where(Function(q) q.ID = id)
    End Function

    Public Sub InsertOrUpdate(p As Employee) Implements EmployeeRepository.InsertOrUpdate
        If p.ID = Nothing Then
            DB.Employees.Add(p)
        Else
            DB.Entry(p).State = Data.EntityState.Modified
        End If
    End Sub

    Public Sub Save() Implements EmployeeRepository.Save
        DB.SaveChanges()
    End Sub
End Class
 برای استفاده تو پروژه براحتی میتونید یک instance  از classتون ایجاد کنید و ..
 Dim cls As New EmployeeClass 
Public Sub BindGrid()
        GridView1.DataSource = cls.All
        GridView1.DataBind()
    End Sub 
 موفق باشید
نظرات اشتراک‌ها
تبدیلگر ایران سیستم به یونیکد
من در ثبت اطلاعات در فایل dbf یک مشکل دارم. وقتی فیلد جدول از نوع nvarchar هست چطور باید دیتا رو به کوئری بفرستم؟ تو اینترنت جستجو کردم و چند راه مختلف رو امتحان کردم اما همش syntax error میده. 
این کد رو نوشتم:
string dbfDirectory = Server.MapPath("~/Content/") + "myDBF.dbf";
            
            string connectionString = "Provider=VFPOLEDB.1;Data Source=" + dbfDirectory + ";Password=;Collating Sequence=MACHINE"; 
            using (OleDbConnection connection = new OleDbConnection(connectionString))
            {
                connection.Open();
                string CreateTableK = "Create Table myDBF (dsw_id1 int, dsw_fname v(250), dsw_lname v(250) )";
                OleDbCommand cmdCreateTable = new OleDbCommand(CreateTableK, connection);
                cmdCreateTable.ExecuteNonQuery();

                OleDbCommand cmd2 = new OleDbCommand("Insert Into myDBF Values (1, @param1, @param2)", connection);
                cmd2.Parameters.AddWithValue("@param1", "شهرزاد");
                cmd2.Parameters.AddWithValue("@param2", "جواهری");
                OleDbCommand cmd3 = new OleDbCommand("Insert Into myDBF Values (2, 'ma', 'bi')", connection);
                
                cmd2.ExecuteNonQuery();
                cmd3.ExecuteNonQuery();

                connection.Close();
            }
روی خط cmd2.ExecuteNonQuery();  خطای syntax error میده.
باید عبارت فارسی رو به ایران سیستم تبدیل و بعد در فایل اضافه کنم. برای این تبدیل هم این لینک  رو دیدم، اما بعد از تبدیل یک رشته از بایت میده، چطور باید در فایل نوشته بشه؟
مطالب
پیاده‌سازی میکروسرویس‌ها توسط Seneca
همانطور که در مطلب آشنایی با معماری Microservices گفته شد، Seneca یک فریم‌ورک مبتنی بر Node.js برای ساخت برنامه‌های سمت سرور بر مبنای معماری Microservices با هسته Monolithic است. در این مطلب قصد ارائه یک مثال عملی را بر اساس این فریم‌ورک ندارم. هدف، آشنایی با اجزای اصلی Seneca و چهارچوب کاری آن است.


فواید استفاده از فریم‌ورک Seneca  

  • فریم‌ورک Seneca کدنویسی برای ایجاد درخواست‌ها، ارسال پاسخ به درخواست‌های رسیده و تبدیل داده‌ها را که از روتین‌های هر سرویسی میتوانند باشند، ساده‌تر میکند.
  • فریم‌ورک Seneca با معرفی ایده Actionها و Pluginها که جلوتر توضیح داده خواهند شد، روند تبدیل کامپوننت‌های یک برنامه Monolithic را به نوع سرویسی، تسهیل میکند. روندی که به صورت عادی میتواند کاری طاقت فرسا باشد و نیاز به Refactoring زیادی دارد. 
  • سرویس‌های نوشته شده با زبانهای برنامه‌نویسی مختلف یا فریم‌ورک‌های متفاوت میتوانند با سرویس‌های نوشته شده توسط فریم‌ورک Seneca در ارتباط و تبادل باشند.


نصب فریم‌ورک Seneca

نصب Seneca تفاوتی با نصب سایر فریم‌ورک‌ها توسط npm ندارد. مثلا میشود فایلی با نام package.json را با تنظیماتی که در پی خواهد آمد داشت و با اجرای دستور npm install در خط فرمان، Seneca را نصب کرد. البته دستور نصب را باید در پوشه‌ای که فایل package.json در آن  ایجاد شده‌است، اجرا کرد.
{
    "name": "seneca-example",
    "dependencies": {
        "seneca": "0.6.5",
        "express": "latest"
    }
}
بعد برای استفاده از فریم‌ورک نصب شده باید نمونه‌ای از آن را ایجاد کنیم. 
var seneca = require("seneca")();

 Actionها

‌Actionها عملکرد بخصوصی را ارائه می‌دهند که باید به نمونه‌ای از فریم‌ورک Seneca که ایجاد کرده‌ایم، اضافه شوند. اینکه یک Action چه عملکرد بخصوصی را ارائه می‌هد، در زمان اضافه کردن آن به نمونه‌ای ایجاد شده، مشخص می‌شود. در مطلب گذشته گفته شد که برای تغییر یک برنامه Monolithic به نوع سرویسی، می‌شود فقط کامپوننت‌های دردسر ساز را تغییر داد. Actionها عملکردهای آن کامپوننت‌های مشکل ساز را به عهده خواهند گرفت. زمان اضافه کردن یک Action به نمونه‌ای از Seneca، از متد add استفاده میکنیم که دو آرگومان را دریافت میکند. اولین آرگومان، یک رشته JSON یا یک object است که هویت عملکرد Action را نشان می‌دهد و دومی در واقع یک متد Callback است و زمانیکه Action فراخوانی میشود، اجرا خواهد شد.  
seneca.add({role: "accountManagement", cmd: "login"}, function(args, respond){
});
seneca.add({role: "accountManagement", cmd: "register"}, function(args, respond){
});
در مثال بالا دو Action را به نمونه‌ای از Seneca اضافه کرده‌ایم. خواص role و cmd موارد بخصوصی نیستند. می‌شود آن‌ها را با  موارد دلخواه، بسته به عملکردی که از Action انتظار داریم، جایگزین کنیم. برای فراخوانی Actionهایی که اضافه کرده‌ایم، باید از متد act استفاده کنیم. البته متد act میتواند Actionهایی را که در سایر سرویس‌ها قرار دارند هم فراخوانی کند. اولین آرگومان act، برای تطبیق با الگوهایی که در زمان اضافه کردن Actionها به نمونه تنظیم شده‌اند، استفاده می‌شود. دومین آرگومان آن هم باز یک Callback است و در زمانیکه Action فراخوانی شده‌است اجرا میشود. 
seneca.act({role: "accountManagement", cmd: "register", username:
"parham", password: "12345!"}, function(error, response){
});

seneca.act({role: "accountManagement", cmd: "login", username:
"parham", password: "12345!"}, function(error, response){
});
در مثال بالا و در پارامتر اول، مشخصه‌های بیشتری نسبت به الگوی Actionهایی که اضافه کرده‌ایم، دیده می‌شود. این مسئله مشکلی ایجاد نمیکند و به هر حال Seneca تمامی الگوهای مشابه را شناسایی میکند و آن موردی که بیشترین تطبیق را دارد، فرا میخواند. 


Pluginها

  Pluginها در Seneca در واقع بسته بندیهایی از Actionهایی هستند که کاربرد مشابهی دارند. در مثال بالا دو Action داشتیم که یکی عملکرد ورود را برعهده دارد و دیگری ثبت‌نام. از آنجایی که هر دو برای بخش مدیریت کاربران تشابهاتی دارند، می‌شود آنها را در یک Plugin بسته بندی کرد. ارزش Pluginها زمانی است که می‌خواهیم آنها را توزیع کنیم. با توزیع Plugin، تمام Actionهای زیرمجموعه آن همزمان توزیع می‌شوند. Pluginها را میشود در قالب توابع یا ماژول‌ها ایجاد کرد.
function account(options){
    this.add({init: "account"}, function(pluginInfo, respond){
        console.log(options.message);
        respond();
    })  

    this.add({role: "accountManagement", cmd: "login"}, function(args, respond){
     }); 

    this.add({role: "accountManagement", cmd: "register"}, function(args, respond){
    });
}

seneca.use(account, {message: "Plugin Added"});
در مثال بالا از روش تابع برای ایجاد Plugin استفاده شده‌است. Plugin را ایجاد می‌کنیم و Actionهایی را که میخواهیم به Plugin اضافه شوند، توسط this اضافه می‌کنیم و در نهایت با متد use به نمونه Seneca میگوییم که از Plugin ساخته شده، استفاده کند. کلمه کلیدی This درون Plugin به نمونه‌ای از Seneca که ایجاد کرده‌ایم، ارجاع دارد. یعنی هر this.add برابر با seneca.add می‌باشد. نکته باقی مانده، Action اضافه‌ای است با مشخصه {init: "account"} که چه نقشی دارد. این Action نقش یک سازنده را برای مقداردهی اولیه، دارد. برای نمونه میشود از آن برای ارتباط با پایگاه داده، پیش از اجرا شدن سایر Actionها که نیاز به آن ارتباط دارند، استفاده کرد. مثال بالا را می‌شود با یک ماژول هم پیاده‌سازی کرد.
module.exports = function(options){
    this.add({init: "account"}, function(pluginInfo, respond){
        console.log(options.message);
        respond();
    })

    this.add({role: "accountManagement", cmd: "login"}, function(args, respond){
    });

    this.add({role: "accountManagement", cmd: "register"},function(args, respond){
    });

    return "account";
}  

seneca.use("./account.js", {message: "Plugin Added"});
 ماژول، نام Plugin را برگشت میدهد که با فرض بر اینکه ماژول در فایل account.js قرار دارد، با متد use به نمونه Seneca میگوییم که از Plugin ساخته شده استفاده کند.


Serviceها  

یک سرویس، یک نمونه از فریم‌ورک Seneca است که Actionهایی را برای استفاده بیرونی، در معرض شبکه قرار می‌دهد. 
var seneca = require("seneca")();
seneca.use("./account.js", {message: "Plugin Added"});
seneca.listen({port: "9090", pin: {role: "accountManagement"}});
در مثال بالا، نمونه‌ای از فریم‌ورک Seneca ایجاد شده، مجموعه‌ای از Actionها تحت یک Plugin به آن اضافه شده و در نهایت Actionهایی که در زمان ساخت و اضافه کردن آنها نقش accountManagement گرفته‌اند، برای استفاده در معرض شبکه و در بستر HTTP قرار می‌گیرند. هر دو بخش port و pin، اختیاری هستند و با صرفنظر از این دو بخش، اولا نمونه‌ی Seneca برای دریافت درخواست‌های ورودی، به درگاه پیش فرض 10101 گوش می‌دهد و ثانیا تمامی Actionهایی را که به نمونه اضافه کرده‌ایم، در معرض شبکه قرار میگیرند.
در سمت دیگر، برای اینکه سایر سرویس‌ها و برنامه‌های Monolithic قادر باشند Actionهای در معرض شکبه قرار داده شده را فراخوانی کنند، باید آنچه را که در معرض قرار داده شده، در خود ثبت کنند. برای مثال سرویس دیگری از Seneca می‌تواند از قطعه کد زیر استفاده کند.
seneca.client({port: "9090", pin: {role: "accountManagement"}});

در اینجا هم موارد port و pin اختیاری هستند. اگر سرویسی که میخواهیم ثبت کنیم در سرور دیگری باشد، بایستی خاصیت host و با مقدار آدرس IP سرور مورد نظر در زمان ثبت، اعمال شود. سوالی که باقی می‌ماند این است که یک سرویس چطور Actionی از یک سرویس دیگر را فراخوانی می‌کند؟

در بخش Actionها آمد که برای فراخوانی یک Action از متد act،  از نمونه‌ی Seneca استفاده میکنیم. رفتار Seneca به این صورت است که ابتدا بر اساس امضای Action درخواست شده، Actionهای محلی را که به سرویس جاری اضافه شده‌اند، جستجو میکند. اگر تطبیقی نیافت به سراغ Actionهای ثبت شده خارجی که دارای خاصیت pin هستند، خواهد رفت و در نهایت اگر آنجا هم موردی نیافت، برای تک تک سرویس‌هایی که آنها را ثبت کرده، اما خاصیت pin را ندارند، درخواستی را ارسال میکند.


برای اطلاعات بیشتر به بخش مستندات  فریم‌ورک Seneca رجوع کنید.

مطالب
استفاده از date picker شمسی جاوا اسکریپتی در Blazor با قابلیت ورود تاریخ به صورت دستی
دیت پیکرهای گوناگونی توسط افراد مختلف نوشته شده‌اند که هر یک مشکلات خاص خود را دارند. در این مطلب به چگونگی استفاده از یکی از سازگارترین  دیت پیکرهای جاوا اسکریپتی که توسط آقای امیرمسعود ایرانی نوشته شده است در Blazor خواهیم پرداخت.
مهم‌ترین ویژگی این دیت پیکر امکان ورود تاریخ به صورت دستی توسط کاربر است.

فرمت‌های قابل قبول برای ورود تاریخ عبارتند از:
۹۰۰۸۱۴ ۱۴۰۸۹۰ ۱۳۹۰۰۸۱۴ ۱۴/۸/۹۰ ۹۰/۸/۱۴ ۱۴/۸/۱۳۹۰ ۱۳۹۰/۸/۱۴ ۱۴-۸-۹۰ ۹۰-۸-۱۴ ۱۴-۸-۱۳۹۰ ۱۳۹۰-۸-۱۴ 
و فرمت‌های ویژه:
۰۸۱۴ ۱۴۰۸ ۱۴-۸ ۸-۱۴ ۱۴/۸ ۸/۱۴ ۱۴
در فرمت‌های ویژه که سال و ماه وارد نشده‌اند، سال و ماه فعلی به حساب خواهد آمد.
در فرمت‌هایی که سال مشخص نشده باشد، دو رقم ابتدایی در صورت امکان روز محاسبه خواهند شد.
بنابراین قادر خواهیم بود که در خروجی یک فرمت استاندارد داشته باشیم حتی با فرمت‌های مختلفی که کاربر وارد خواهد کرد.

روش به کارگیری تقویم در Blazor

در ابتدا فایل‌های مورد نیاز را دانلود کرده (AMIB_jsPersianCal_0.2.1.rar) و به پروژه اضافه می‌کنیم.
سپس به _layout رفته و ارجاعات زیر را برای افزودن فایل‌های css و js به پروژه اضافه می‌کنیم:
<link href="css/js-persian-cal.css" rel="stylesheet"/>
<script src="js/js-persian-cal.min.js"></script>
حال برای استفاده از دیت پیکر در کامپوننت‌ها از تگ input به شکل زیر استفاده می‌کنیم:
<input type="text" id="pcal1" />
Id آن مهم است زیرا توسط آن به تابع جاوااسکریپتی معرفی می‌شود. می‌توان هر اسمی را اختیار کرد فقط بهتر است تمامی دیت پیکرهای موجود در صفحه یک اسم داشته باشند اما با ایندکس‌های مختلف مانند pcal1، pcal2 و ... . دلیل آن این است که می‌توان تمامی دیت پیکرهای را توسط یک حلقه به تابع مربوطه معرفی کرد.
همانطور که می‌دانید برای استفاده از توابع جاوا اسکریپتی در Blazor از JSRuntime استفاده می‌شود. بنابراین به شکل زیر عمل خواهیم کرد.
protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        int dateFieldCount = 1;
        if (firstRender)
        {
            for (int i = 1; i <= dateFieldCount; i++)
            {
                await JsRuntime.InvokeVoidAsync("CallAmib", "pcal" + i.ToString());
            }
        }
    }
توسط حلقه for تمامی تگ‌های input موجود در کامپوننت را که Id آنها با pcal شروع می‌شود به دیت پیکر تبدیل خواهیم نمود. فقط مقدار متغیر dateFieldCount را باید به تعداد تگ‌های دیت پیکر موجود در کامپوننت تنظیم نمود.
لازم به ذکر است که باید در ابتدای کامپوننت، JSRuntime را به شکل زیر تزریق نمود.
@inject IJSRuntime  JsRuntime
حال فقط کافیست اسکریپت CallAmib را ایجاد کرده و به _layout اضافه نمود.
window.CallAmib = (objCal1) => {
    new AMIB.persianCalendar(objCal1);
}
  بنابراین فایل _layout برنامه الان چیزی شبیه به زیر خواهد بود:
@using Microsoft.AspNetCore.Components.Web
@namespace ShamsiDatePickerBlazor.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <base href="~/" />
    <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
    <link href="css/site.css" rel="stylesheet" />
    <link href="css/js-persian-cal.css" rel="stylesheet" />
    <component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
</head>
<body>
    @RenderBody()

    <div id="blazor-error-ui">
        <environment include="Staging,Production">
            An error has occurred. This application may no longer respond until reloaded.
        </environment>
        <environment include="Development">
            An unhandled exception has occurred. See browser dev tools for details.
        </environment>
        <a href="">Reload</a>
        <a>🗙</a>
    </div>
    <script src="js/js-persian-cal.min.js"></script>
    <script src="js/CallAmib.js"></script>
    <script src="_framework/blazor.server.js"></script>
</body>
</html>
تا اینجای کار اگر پروژه را اجرا کنیم، دیت پیکری مانند زیر را خواهیم داشت:

مشکل!!

برای بایند کردن مقدار تاریخ انتخاب شده نمی‌توان از bind-value به طور معمول استفاده کرد؛ زیرا در حقیقت تغییرات input با جاوا اسکریپت انجام می‌گیرد و حالت صفحه تغییری نمی‌کند. برای مرتفع کردن این مشکل نیاز است که در اسکریپت CallAmib متد onchange به شکل زیر صدا زده شده و مقدار تاریخ انتخابی به یک متد داخل کامپوننت ارسال گردیده و در آنجا به یک فیلد منتسب شود.
window.CallCall = (objCal1) => {
    new AMIB.persianCalendar(objCal1,{
        onchange: function(pdate) {
            DotNet.invokeMethodAsync('ShamsiDatePickerBlazor', 'DateChanged', pdate.toString()).then(
                (date) => {
                    console.log(data);
                }
            );
        }
    });
}
توضیحات اسکریپت بالا:
متغیر pdate به صورت توکار مربوط به AMIB.persianCalendar می باشد و مقدار تاریخ انتخابی را در بر دارد.
متد DotNet.invokeMethodAsync یک متد توکار دات نت می‌باشد و برای فراخوانی متدهای سی شارپی از داخل توابع جاوا اسکریپتی به کار می‌رود. آرگومان اول آن در حقیقت نام اسمبلی پروژه می‌باشد. آرگومان دوم آن نام تابع سی شارپی‌است که باید فراخوانی شود و در نهایت آرگومان سوم آن تاریخ انتخاب شده می‌باشد.
در پایان باید متد DateChanged،  به شکل زیر در کامپوننت index نوشته شود:
static string selectedDate;
[JSInvokable]
public static void DateChanged(string pdate)
{
    selectedDate = pdate;
}
این تابع بایستی با صفت [JSInvokable] مزین شود و حتما هم استاتیک باشد.
برای دیدن مقدار جدید selectedDate کافی است روی دکمه ShowNewValue یکبار کلیک نمایید.
نکته: می‌توان به جای input، از InputText مربوط به EditForm هم استفاده نمود. فقط باید یک Id هم به آن انتساب داد. همچنین برای انتساب مقدار دیت پیکر به مدل، باید در متد DateChanged، فیلد مورد نظر از مدل را بجای متغیر selectedDate گذاشت.
شما می‌توانید در اینجا کدهای کامل این مطلب را ملاحظه نمایید.
مطالب دوره‌ها
دسترسی سریع به مقادیر خواص توسط Reflection.Emit
اگر پروژه‌های چندسال اخیر را مرور کرده باشید خصوصا در زمینه ORMها و یا Serializerها و کلا مواردی که با Reflection زیاد سروکار دارند، تعدادی از آن‌ها پیشوند fast را یدک می‌کشند و با ارائه نمودارهایی نشان می‌دهند که سرعت عملیات و کتابخانه‌های آن‌ها چندین برابر کتابخانه‌های معمولی است و ... سؤال مهم اینجا است که رمز و راز این‌ها چیست؟
فرض کنید تعاریف کلاس User به صورت زیر است:
public class User
{
     public int Id { set; get; }
}
همانطور که در قسمت‌های قبل نیز عنوان شد، خاصیت Id در کدهای IL نهایی به صورت متدهای get_Id و set_Id ظاهر می‌شوند.
حال اگر یک متد پویا ایجاد کنیم که بجای هر بار Reflection جهت دریافت مقدار Id، خود متد get_Id را مستقیما صدا بزند، چه خواهد شد؟
پیاده سازی این نکته را در ادامه ملاحظه می‌کنید:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;

namespace FastReflectionTests
{
    /// <summary>
    /// کلاسی برای اندازه گیری زمان اجرای عملیات
    /// </summary>
    public class Benchmark : IDisposable
    {
        Stopwatch _watch;
        string _name;

        public static Benchmark Start(string name)
        {
            return new Benchmark(name);
        }

        private Benchmark(string name)
        {
            _name = name;
            _watch = new Stopwatch();
            _watch.Start();
        }

        public void Dispose()
        {
            _watch.Stop();
            Console.WriteLine("{0} Total seconds: {1}"
                               , _name, _watch.Elapsed.TotalSeconds);
        }
    }

    public class User
    {
        public int Id { set; get; }
    }

    class Program
    {
        public static Func<object, object> GetFastGetterFunc(string propertyName, Type ownerType)
        {
            var propertyInfo = ownerType.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public);

            if (propertyInfo == null)
                return null;
            
            var getter = ownerType.GetMethod("get_" + propertyInfo.Name,
                                             BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);
            if (getter == null)
                return null;

            var dynamicGetterMethod = new DynamicMethod(
                                                name: "_",
                                                returnType: typeof(object),
                                                parameterTypes: new[] { typeof(object) },
                                                owner: propertyInfo.DeclaringType,
                                                skipVisibility: true);
            var il = dynamicGetterMethod.GetILGenerator();

            il.Emit(OpCodes.Ldarg_0); // Load input to stack
            il.Emit(OpCodes.Castclass, propertyInfo.DeclaringType); // Cast to source type
            // نکته مهم در اینجا فراخوانی نهایی متد گت بدون استفاده از ریفلکشن است
            il.Emit(OpCodes.Callvirt, getter); //calls its get method

            if (propertyInfo.PropertyType.IsValueType)
                il.Emit(OpCodes.Box, propertyInfo.PropertyType);//box

            il.Emit(OpCodes.Ret);

            return (Func<object, object>)dynamicGetterMethod.CreateDelegate(typeof(Func<object, object>));
        }


        static void Main(string[] args)
        {
            //تهیه لیستی از داده‌ها جهت آزمایش
            var list = new List<User>();
            for (int i = 0; i < 1000000; i++)
            {
                list.Add(new User { Id = i });
            }

            // دسترسی به اطلاعات لیست به صورت متداول از طریق ریفلکشن معمولی
            var idProperty = typeof(User).GetProperty("Id");
            using (Benchmark.Start("Normal reflection"))
            {
                foreach (var item in list)
                {
                    var id = idProperty.GetValue(item, null);
                }
            }

            // دسترسی از طریق روش سریع دستیابی به اطلاعات خواص
            var fastIdProperty = GetFastGetterFunc("Id", typeof(User));
            using (Benchmark.Start("Fast Property"))
            {
                foreach (var item in list)
                {
                    var id = fastIdProperty(item);
                }
            }
        }
    }
}
توضیحات:
از کلاس Benchmark برای نمایش زمان انجام عملیات دریافت مقادیر Id از یک لیست، به دو روش Reflection متداول و روش صدا زدن مستقیم متد get_Id استفاده شده است.
در متد GetFastGetterFunc، ابتدا به متد get_Id خاصیت Id دسترسی پیدا خواهیم کرد. سپس یک متد پویا ایجاد می‌کنیم تا این get_Id را مستقیما صدا بزند. حاصل کار را به صورت یک delegate بازگشت می‌دهیم. شاید عنوان کنید که در اینجا هم حداقل در ابتدای کار متد، یک Reflection اولیه وجود دارد. پاسخ این است که مهم نیست؛ چون در یک برنامه واقعی، تهیه delegates در زمان آغاز برنامه انجام شده و حاصل کش می‌شود. بنابراین در زمان استفاده نهایی، به هیچ عنوان با سربار Reflection مواجه نخواهیم بود.

خروجی آزمایش فوق بر روی سیستم معمولی من به صورت زیر است:
 Normal reflection Total seconds: 2.0054177
Fast Property Total seconds: 0.0552056
بله. نتیجه روش GetFastGetterFunc واقعا سریع و باور نکردنی است!


چند پروژه که از این روش استفاده می‌کنند
Dapper
AutoMapper
fastJson

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


ماخذ اصلی
این کشف و استفاده خاص، از اینجا شروع و عمومیت یافته است و پایه تمام کتابخانه‌هایی است که پیشوند fast را به خود داده‌اند:
2000% faster using dynamic method calls
نظرات مطالب
EF Code First #9
من یک سری مدل به شکل زیر دارم:
    public class Context : DbContext
    {
        public Context():base("Server=.;Database=EfTest;Trusted_Connection=True;Integrated Security=true;")
        {
            
        }

        public DbSet<Student> Students { get; set; }
        public DbSet<Teacher> Teachers { get; set; }

    }


    public class Person
    {
        public int Id { get; set; }
        public DateTime InsertTime { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }

    }


    public class Student:Person
    {
        public string Student1 { get; set; }
    }

    public class Teacher:Person
    {
        public string Teacher1 { get; set; }

    }
بدون هیچ تنظیماتی، یعنی طبق تعاریف بالا الان باید حالت به شکل TPH باشد ولی در ساخت نهایی دو جدول دانش آموز و معلم تشکیل میشه. من در این مدتی که ef کار کردم روی این حالت بودم و راضی هستم ولی برای یکی از دوستان EF سعی داره جدولی به اسم People رو از روی Person تشکیل بده.
نظرات مطالب
آشنایی با Gridify
فرض کنید مدل زیر رو داریم
public class Order : AggregateRoot<int>
{
    private DateTime _orderDate;
    public Address Address { get; private set; }
    public int? GetBuyerId => _buyerId;
    public int? _buyerId;
    public OrderStatus OrderStatus { get; private set; }
    public  int _orderStatusId;
    private string _description;
    private bool _isDraft;


    private readonly List<OrderItem> _orderItems;
    public IReadOnlyCollection<OrderItem> OrderItems => _orderItems;

    protected Order()
    {
        _orderItems = new List<OrderItem>();
        _isDraft = false;
    }
    public Order(string userId, string userName, Address address,
        int? buyerId = null) : this()
    {
        _buyerId = buyerId;
        _orderStatusId = OrderStatus.Submitted.Id;
        _orderDate = DateTime.UtcNow;
        Address = address;
        //AddOrderStartedDomainEvent(userId, userName);
    }
}

public class OrderStatus : Enumeration
{
    public static OrderStatus Submitted = new OrderStatus(1, nameof(Submitted).ToLowerInvariant());
    public static OrderStatus AwaitingValidation = new OrderStatus(2, nameof(AwaitingValidation).ToLowerInvariant());
    public static OrderStatus StockConfirmed = new OrderStatus(3, nameof(StockConfirmed).ToLowerInvariant());
    public static OrderStatus Paid = new OrderStatus(4, nameof(Paid).ToLowerInvariant());
    public static OrderStatus Shipped = new OrderStatus(5, nameof(Shipped).ToLowerInvariant());
    public static OrderStatus Cancelled = new OrderStatus(6, nameof(Cancelled).ToLowerInvariant());
    public OrderStatus(int id, string name)
        : base(id, name)
    {
    }
}
حال چگونه میتوانیم از Order بر اساس OrderStatus.Id فیلترینگ انجام بدیم ؟
var query = _orderQueryRepository.GetAll(x => x._buyerId == 52).AsNoTracking();
var s = await query.GridifyAsync(request.queryFilter);
return s.Adapt<Paging<OrderQuery>>();
{
  "buyerid": 0,
  "queryFilter": {
    "page": 1,
    "pageSize": 5,
    "orderBy": "id",
    "filter": "Order_OrderStatus_Id==1"
  }
}
خروجی
 "message": "Property 'Order_OrderStatus_Id' not found.",