مطالب
استفاده از postgres در برنامه‌های ASP.NET Core - قسمت اول
postgres یک بانک اطلاعاتی متن باز، قدرتمند و relational میباشد که پس از 30 سال توسعه‌ی فعال، به کارآیی بالا، قابل اطمینان بودن و قدرتمند بودن شهرت دارد. همچنین در بنچمارک‌های مربوط به وبسایت techempower نیز استفاده از  این پایگاه داده در کنار asp.net core باعث شده‌است تا جایگاه خوبی کسب شود. علاوه بر این ویژگی‌ها، انعطاف نوع داده‌ها (data type) سبب تفاوت بین رقبا شده است. برای مثال فرض کنید که یک جدول به اسم Blog و قصد ذخیره تگ‌های مقاله را داریم. کار به چه صورت خواهد بود؟
اگر پیش‌تر با postgres آشنایی نداشته باشید، یکی از دو سناریو زیر را پیاده سازی خواهید کرد:
- ذخیره در یک فیلد به صورت nvarchar و جدا کردن حرف‌ها با یک کاراکتر (برای مثال: dntips, csharp با کارکتر , از هم جدا شده اند)
Blog ID
BlogID int
Title nvarchar(250)
Tags nvarchar(500)

- ساخت جدول و رابطه چند به چند 
Blog Tags Tbl
BlogID int
TagID int
ایراد قطعه کد اول عدم امکان سرچ اصولی در بین کلمات کلیدی میباشد؛ زیرا شما مجبور به جستجو در یک فیلد هستید که واژه‌ها با کاراکتری از یکدیگر جدا شده‌اند. پس شما سرچ دقیقی نخواهید داشت. پس از این مشکل میتوان به وجود تگ‌های تکراری در یک رکورد و یا نداشتن تگ‌ها به صورت یکپارچه اشاره کرد و به عنوان مشکل آخر میتواند به یک سربار برای سیستم تبدیل شود. چون هر بار که دیتا واکشی میشود، باید یکبار کلمات را از یکدیگر جدا و سپس به یک آرایه تبدیل و هنگام ذخیره شدن نیز مجددا این رشته‌ها را به هم بچسبانیم.
در مورد کد دوم هم شاید بتوان به انباشته شدن زیاد سطر‌ها و یا عدم ساختار مدرن اشاره کرد.
اما در postgres به راحتی میتوان این گونه دیتا‌ها را به صورت آرایه ذخیره سازی کرد:
CREATE TABLE sal_emp (
    name            text,
    pay_by_quarter  integer[],
    schedule        text[][]
);
که سبب سرچ اصولی و واکشی سریع‌تر اطلاعات خواهد شد.
راجع به نوع داده array در postgres در بیشتر مطالعه کنید.
در همین بحث دیتا تایپ‌ها میتوان به نوع text اشاره کرد که جایگزینی برای nvarchar و یا varchar میباشد. در اینجا نیازی به مشخص کردن سایز رشته نیز نیست و تمام فرآیند، به صورت خودکار در پس‌زمینه انجام خواهد شد.  مثلا بجای nvarcahr 500 و یا MAX تنها، نوع داده را برابر text قرار می‌دهید.
از نظر ساختار زبانی و syntax بسیار شبیه به سایر provider‌‌ها میباشد و تنها تفاوت اساسی آن، حساس بودن به حروف کوچک و بزرگ است.
سایر مزیت‌های postgres را میتوانید از زبان shayro jan sky، در کانال دات نت و در ویدیو لینک شده مشاهده کنید.

مزیت بزرگ آن که باعث میشود تا از آن بتوانیم در پروژه‌های خود استفاده کنیم، سازگاری آن با ef core میباشد. یعنی اگر کل برنامه‌ی شما با ef core پیاده سازی شده باشد، با عوض کردن متد UseSqlServer به UseNpgsql در کلاس program، مشکلی در برنامه رخ نخواهد داد و اپلیکیشن شما بجای استفاده از sql server به راحتی از postgres استفاده خواهد کرد.
متد نام برده شده در پکیج زیر قابل دسترسی میباشد:
Npgsql.EntityFrameworkCore.PostgreSQL

کار‌ها تماما مانند ef و حتی با کتابخانه‌های مربوط به آن انجام خواهد شد و تنها تغییر در کدها، همین متد UseNpgsql میباشد که provider را عوض خواهد کرد. 

در قسمت بعد به نصب و راه اندازی postrgress و دشبرد‌های مدیریتی آن از طریق داکر و پیاده سازی CRUD خواهیم پرداخت.
مطالب
تغییر مهم تعریف رشته‌های اتصالی SQL Server در EF Core 7.0
اگر برنامه‌های مبتنی بر SQL Server خود را به EF 7.0 ارتقاء دهید، در حین اجرای برنامه، به خطای زیر برخواهید خورد:
A connection was successfully established with the server, but then an error occurred during the login process.
(provider: SSL Provider, error: 0 - The certificate chain was issued by an authority that is not trusted.)
علت اینجا است که پروایدر SQL Server مخصوص EF 7.0، از نگارش جدید Microsoft.Data.SqlClient استفاده می‌کند و در این نگارش، تنظیم Encrypt=True، به صورت پیش‌فرض اعمال شده‌است. این تنظیم، پیشتر به صورت Encrypt=False وجود داشت و به همین جهت عموما در حین تعریف رشته‌های اتصالی دیده نمی‌شد.

اگر نیاز به استفاده‌ی از پرچم Encrypt=True وجود داشته باشد:
- باید سرور اس کیوال با یک certificate معتبر تنظیم شود.
- کلاینت باید این certificate را تائید کند. اگر کلاینت چنین قصدی را ندارد، می‌تواند تنظیم TrustServerCertificate=True را به رشته‌ی اتصالی اضافه کند.
در غیراینصورت، خطای ذکر شده را دریافت خواهیم کرد.

برای رفع این مشکل یا باید رمزنگاری اتصالات را در SQL Server پیاده سازی کرد و یا از این پس، ذکر پرچم Encrypt=False در انتهای رشته‌ی اتصالی به آن، ضروری است. برای مثال یک نمونه رشته‌ی اتصالی تغییر یافته‌ی بر اساس این تنظیم، به صورت زیر است:
Data Source=.\\SQLEXPRESS;Initial Catalog=MyTestDb;Integrated Security=true;Encrypt=False;

بدیهی است این تغییر مختص به EF 7.0 نیست و تمام برنامه‌هایی که از ADO.NET و نگارش جدید Microsoft.Data.SqlClient استفاده می‌کنند، باید به آن توجه داشته باشند.
مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 16 - کار با Sessions
سشن‌ها نیز همانند تمام قسمت‌های دیگر یک برنامه‌ی ASP.NET Core، به صورت پیش فرض غیرفعال هستند و نیاز به مراحل خاصی است تا امکان استفاده‌ی از آن‌ها فراهم شود. همچنین روش کار کردن با آن‌ها نیز متفاوت است با نگارش‌های قبلی ASP.NET (تمام نگارش‌ها).


سشن چیست؟

شیء سشن، مجموعه‌ای از اشیاء serialized مرتبط با جلسه‌ی کاری جاری یک کاربر است. این اشیاء عموما در حافظه‌ی محلی سرور ذخیره می‌شوند؛ اما امکان ذخیره سازی توزیع شده‌ی آن‌ها در بانک‌های اطلاعاتی نیز پیش بینی شده‌است.
عموما استفاده‌ی از اشیاء سشن توصیه نمی‌شوند. از این جهت که این نوع اشیاء بسیار شبیه هستند به متغیرهای سراسری و وجود این نوع متغیرها اساسا ضعف طراحی شیء‌گرا به حساب می‌آیند. اما با توجه به ماهیت stateless بودن برنامه‌های وب، به این معنا که با پایان رندر یک صفحه، تمام اشیاء مرتبط با آن‌ها نیز در سمت سرور تخریب می‌شوند، نیاز است برای یک سری از داده‌های عمومی کاربر، راه حلی را پیدا کرد تا بتوان از اطلاعات آن‌ها استفاده‌ی مجدد کرد. برای مثال نگهداری رشته‌ی اتصالی بانک اطلاعاتی که کاربر در حین لاگین به سیستم آن‌را انتخاب کرده‌است (اگر برنامه به ازای هر سال از یک بانک اطلاعاتی مجزا استفاده می‌کند) و یا زمانیکه کاربری captcha را پر می‌کند و مقدار آن‌را به سمت سرور ارسال می‌کند، نیاز است مقدار ارسالی او را با مقدار ابتدایی captcha مقایسه کرد. یک چنین اطلاعاتی نباید با پایان رندر صفحه تخریب شوند و نیاز است تا زمانیکه جلسه‌ی کاری کاربر به پایان نرسیده‌است، در دسترس باشند. به همین جهت است که مفهومی را به نام «اشیاء سشن» طراحی کرده‌اند.
درکل خارج از این موارد بهتر است از سشن استفاده نکنید و در جای جای برنامه‌ی خود ردپای آن‌را باقی نگذارید و به خاطر داشته باشید:
متغیر سشن = متغیر سراسری = ضعف طراحی شی‌‌ءگرا


توصیه‌ی به استفاده‌ی از روش‌های سبک وزن‌تر

سشن‌ها تنها روش به اشتراک گذاری اطلاعات نیستند. اگر می‌خواهید اطلاعاتی را در بین میان افزارهای برنامه در طی یک درخواست به اشتراک بگذارید، شاید سشن هم یک راه حل باشد؛ اما راه حلی سنگین وزن. راه حل بهتر برای این موارد، استفاده‌ی از HttpContext.Items است. HttpContext.Items نیز همانند سشن، یک key/value store است؛ اما طول عمر آن محدود است به طول عمر درخواست جاری و در تمام میان افزارهای برنامه در دسترس است.
برای مثال در یک میان افزار آن‌را تنظیم می‌کنید:
app.Use(async (context, next) =>
{
   context.Items["isVerified"] = true;
   await next.Invoke();
});
و سپس در میان افزاری دیگر از آن استفاده خواهید کرد:
app.Run(async (context) =>
{
   await context.Response.WriteAsync("Verified request? " + context.Items["isVerified"]);
});


فعال سازی سشن‌ها در ASP.NET Core

ASP.NET Core یک choose-what-you-need framework است. به این معنا که تا زمانیکه قابلیتی را به صورت صریح فعال سازی نکرده باشید، در دسترس نخواهد بود. همین مساله در نهایت به کاهش مصرف منابع این نوع برنامه‌ها و همچنین طراحی ماژولار سیستم ختم می‌شوند. برای مثال در نگارش‌های قبلی ASP.NET (تمام نگارش‌ها)، سشن‌ها به صورت پیش فرض فعال هستند، مگر آنکه HTTP Module آن‌را در فایل web.config حذف کنید؛ اما در اینجا برعکس است.
اگر تنها در موارد خاصی که ذکر شد، نیاز به استفاده‌ی از متغیرهای سشن را داشتید، روش فعال سازی آن به صورت ذیل است:
الف) نصب بسته‌ی نیوگت Microsoft.AspNetCore.Session
برای این منظور وابستگی ذیل را به فایل project.json اضافه کنید:
{
    "dependencies": {
      //same as before
      "Microsoft.AspNetCore.Session": "1.0.0"
    }
}
ب) انجام تنظیمات آغازین برنامه
برای این کار به کلاس آغازین برنامه مراجعه کرده و ابتدا سرویس سشن‌ها را فعال کنید:
public void ConfigureServices(IServiceCollection services)
{
    services.AddSession();
و سپس در متد Configure، استفاده‌ی از سشن‌ها را نیز باید ذکر کنید:
public void Configure(IApplicationBuilder app)
{
    app.UseSession();
در اینجا امکان سفارشی سازی مقادیر پیش فرض مدیریت سشن‌ها نیز وجود دارند. برای مثال زمان پیش فرض انقضای سشن کاربر، پس از 20 دقیقه عدم فعالیت او است که قابل تغییر می‌باشد:
app.UseSession(options: new SessionOptions
{
    IdleTimeout = TimeSpan.FromMinutes(30),
    CookieName = ".MyApplication"
});


روش استفاده‌ی از سشن‌ها

در مثال ذیل نحوه‌ی ذخیره سازی اطلاعات را در شیء سشن جلسه‌ی جاری یک کاربر، ملاحظه می‌کنید:
public ActionResult TestSession()
{
 
   this.HttpContext.Session.Set("key-1", BitConverter.GetBytes(DateTime.Now.Ticks));
   this.HttpContext.Session.SetInt32("key-2", 1);
   this.HttpContext.Session.SetString("key-3", "DNT");
 
   return Content("OK!");
}
به همراه نحوه‌ی بازیابی این اطلاعات در متدهای دیگر برنامه:
 public IActionResult Index()
{
   byte[] key1 = this.HttpContext.Session.Get("key-1");
   long key1Value = BitConverter.ToInt64(key1, 0);
 
   int? key2Value = this.HttpContext.Session.GetInt32("key-2");
   string key3Value = this.HttpContext.Session.GetString("key-3");
 
   return Content("OK!");
}
همانطور که ملاحظه می‌کنید، سه متد Set، SetInt32 و SetString در اینجا برای تنظیم key/valueهای سشن، از پیش موجود هستند.
حالت Set آن آرایه‌ای از بایت‌ها را دریافت می‌کند و می‌توان برای حالت serialization اشیاء، مفید باشد.
دقیقا معادل همین سه متد، متدهای Get، GetInt32 و GetString برای بازیابی مقادیر سشن طراحی شده‌اند و باید دقت داشت که خروجی‌های این‌ها می‌توانند نال نیز باشند. به همین جهت خروجی GetInt32 آن نال پذیر است.


توسعه‌ی متدهای پیش فرض کار با سشن‌ها

سه متد یاد شده‌ی کار با سشن‌ها در ASP.NET Core هرچند ضروری هستند، اما کافی نیستند. برای توسعه‌ی آن‌ها می‌توان متدهای الحاقی را تدارک دید که نمونه‌ای از آن‌ها را ذیل مشاهده می‌کنید:
using System;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
 
namespace Core1RtmEmptyTest.StartupCustomizations
{
    public static class SessionExts
    {
        public static void SetDateTime(this ISession collection, string key, DateTime value)
        {
            collection.Set(key, BitConverter.GetBytes(value.Ticks));
        }
 
        public static DateTime? GetDateTime(this ISession collection, string key)
        {
            var data = collection.Get(key);
            if (data == null)
            {
                return null;
            }
            var dateInt = BitConverter.ToInt64(data, 0);
            return new DateTime(dateInt);
        }
 
        public static void SetObject(this ISession session, string key, object value)
        {
            var stringValue = JsonConvert.SerializeObject(value);
            session.SetString(key, stringValue);
        }
 
        public static T GetObject<T>(this ISession session, string key)
        {
            var stringValue = session.GetString(key);
            return JsonConvert.DeserializeObject<T>(stringValue);
        }
    }
}
در اینجا با استفاده از کلاس BitConverter و امکان سریالایز مقادیر توسط آن به آرایه‌ای از بایت‌ها، امکان کار با متد‌های عمومی Set و Get را یافته‌ایم.
و یا جهت کار با اشیاء پیچیده‌تر می‌توان از کتابخانه‌ی JSON.NET استفاده کرد. به عبارتی در این نگارش از ASP.NET، کار سریالایز و دی‌سریالایز اشیاء، به برنامه نویس واگذار شده‌است و اینکه در پشت صحنه از چه کتابخانه‌ای می‌خواهید استفاده کنید، در اختیار خودتان است.
البته باید دقت داشت که در اینجا وابستگی JSON.NET به صورت خودکار در دسترس است. از این جهت که بسیاری از وابستگی‌های ASP.NET Core مانند مورد ذیل، به JSON.NET وابسته‌اند و نصب آن‌ها به معنای نصب خودکار JSON.NET نیز هست:
{
    "dependencies": {
     //same as before
     "Microsoft.Extensions.Configuration.Json": "1.0.0"
   }
}
اگر لیست بسته‌های وابسته‌ی به JSON.NET را می‌خواهید مشاهده کنید، فایل project.lock.json را گشوده و در آن Newtonsoft.Json را جستجو کنید.


یک مطلب تکمیلی

در اینجا نیز امکان ذخیره سازی سشن‌ها در بانک اطلاعاتی بجای حافظه‌ی فرار سرور درنظر گرفته شده‌است و برای این حالت، بانک‌های اطلاعاتی NoSQL ویژه‌ای به نام key/value stores مانند بانک اطلاعاتی فوق سریع Redis پیشنهاد می‌شود؛ هرچند امکان کار با SQL Server نیز در اینجا وجود دارد، اما برای کش سرورهای مبتنی بر key/value ها، بانک اطلاعاتی Redis، انتخاب اول است.
Managing Application State
پروژه‌ها
فروشگاه اینترنتی (الکترونیک) با معماری سه لایه
دانلود پروژه فروشگاه اینترنتی با معماری سه لایه

این پروژه به کمک امکانات شیرین C#‎.NET, ASP.NET,jQuery در محیط Visual Studio 2010 طراحی و پیاده سازی شده است.
ممکن است در این برنامه به اشکالاتی برخورد کنید چون یک پروژه کاری بوده که در طی یک کلاس درس تکمیل شده و هم اکنون در اختیار شما قرار می‌گیرد لطفا در صورت بروز مشکل از طریق بازخورد مطرح فرمایید و از ارسال پیام خصوصی خودداری نمایید.
پروژه با معماری سه لایه طراحی و پیاده سازی شده است
 که شامل لایه DAL برای ارتباط با بانک
 BLL برای اعمال قوانین تجاری
و لایه نمایش آن که ASP.NET استفاده شده است
 بانک اطلاعاتی برنامه با SQL SERVER 2008 می‌باشد که می‌توانید آن را تغییر داده و از بانک اطلاعاتی دلخواه خود استفاده کنید.
فایل اسکریپت نیز در کنار پروژه برای نسخه‌های دیگر SQL قرار گرفته است.
 لازم به ذکر است که برنامه ProviderBase نیز می‌باشد.
یعنی شما میتوانید بانک دلخواه خود را استفاده نموده و بدون کوچکترین تغییری در برنامه سایت از آن استفاده نمایید فقط کافی است که در فایل web.config مشخصات آن Provider را ثبت نموده و کدهای مربوط به Provider خود را بنویسید.
برای استفاده ابتدا دیتابیس را در SQL خود Attach کرده و در فایل web.config در قسمت تنظیمات ConnectionString تنظیمات سرور و نام دیتابیس را وارد نمایید.

این پروژه دارای امکانات
  • فروشگاه اینترنتی با قابلیت گروه بندی محصولات
  • ذخیره تصاویر در دیتابیس و بازیابی با یک Handler
  • سرویس اعلانات سایت
  • درج کلمات کلیدی هر صفحه به صورت اتوماتیک و با توجه به محتوای صفحه
  • استفاده از قابلیت Profile برای کاربران
  • کارت خرید و ذخیره آن در پروفایل کاربر
  • ثبت سوابق خرید کاربر
  • ثبت فیش‌های پرداختی کاربر
  • ثبت نام کاربران جدید در سایت
  • ثبت، نمایش و مدیریت سخنان قصار در نرم افزار
  • امکان Cache کردن اطلاعات دریافتی از بانک و مدیریت Cache 
  • پیاده سازی تمامی کویری‌های بان با Store Procedure در SQL Server
  • استفاده از سرویس Membership
  • خواندن تنظیمات برنامه از فایل Web.Config و معادل‌سازی آن با کلاس‌های مربوطه
  • و ...
البته شاید کامل نباشد اما به عنوان یک منبع آموزشی در زمینه برنامه نویسی سه لایه و بعضی ایده‌های خاص ممکن است برای دوستان مبتدی و متوسطه در زمینه طراحی سایت مفید واقع شود.
در صورت لزوم می‌توانید به لینک اصلی این پروژه در آدرس سایت ما مراجعه نمایید.
نام کاربری و کلمه عبور مدیریت سایت:
UserName: Admin
Password: 1234567@
----------
UserName: Saber_Fatholahi
Password: 1234567@

نظرات نظرسنجی‌ها
ضرورت دانش پایه برای پیشرفت در صنعت نرم افزار کشور

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

به علاوه اندازه‌ی کسب و کارها هستند که تعیین کننده سطح دانش مورد نیاز خودشان هستند. کسی که یک گروه 5 نفره دارد، آیا برایش مقرون به صرفه است که به فکر تولید سیستم عامل باشد؟ برای مثال یک کسب و کار کوچک شاید الزاما نیازی به راه حل‌های NoSQL نداشته باشد؛ اما حتما باید با نحوه‌ی کار با SQLite یا SQL CE آشنا باشد. در یک کسب و کار بزرگ شاید بانک‌های اطلاعاتی موجود پاسخگو نباشند و نیاز باشد تا واحد تحقیق و توسعه‌ی آن‌ها دست به کار شود و بانک اطلاعاتی متناسبی را طراحی کند. برای مثال فیس بوک برداشته تمام قابلیت‌های سی‌شارپ رو به PHP اضافه کرده، یک زبان جدید برای خودشون درست کردند.

بازخوردهای دوره
دریافت قالب WpfFramework.vsix و نحوه نصب و راه اندازی آن
با سلام 
با تشکر  بابت شروع این دوره ، می‌خواستم بدونم این فریمورک قراره ماهیت آموزشی داشته باشه یا اینکه توسعه داده میشه؟ آیا می‌تونیم توی توسعه این فریم ورک همکاری داشته باشیم؟
یک باگ کوچیک توی قسمت GetTableName  برای sql server وجود داره، با توجه به نام جدول ترتیب جایگزینی صحیح نیست.
//table="[dbo].[Users]"
    return table
                     .Replace("dbo.", string.Empty)
                     .Replace("`", string.Empty)
                     .Replace("[", string.Empty)
                     .Replace("]", string.Empty)
                     .Trim()


     return table
                     .Replace("`", string.Empty)
                     .Replace("[", string.Empty)
                     .Replace("]", string.Empty)
                     .Replace("dbo.", string.Empty)
                     .Trim();
نظرات مطالب
ایجاد جداول بهینه سازی شده برای حافظه در SQL Server 2014
بعد از ایجاد یک جدول بهینه سازی شده برای حافظه، هر یک دقیقه یک خطا در لاگ پایگاه داده ثبت میشه با این عنوان:

Disallowing page allocations for database 'MyDatabase' due to insufficient memory in the resource pool 'default'. See 'http://go.microsoft.com/fwlink/?LinkId=510837' for more information. 
با توجه به لینکی که داده Resource governor رو هم فعال کردم ولی مشکل حل نشد.
مشخصات سیستم:
OS:  Windows 10
Database: SQL Server 2017 - Resource governor is enable
Hardware: Dedicated local system with 16 GB RAM
نظرات مطالب
بازسازی جدول MigrationHistory با کد نویسی در EF Code first
- مطلب جاری فقط برای حالتی است که جدول migration حذف شده یا وجود ندارد.
+ مطلب « ارتقاء به Entity framework 6 و استفاده از بانک‌های اطلاعاتی غیر از SQL Server » را باید دقیق مطالعه کنید. یک سری اسمبلی باید حذف شوند. تعدادی اضافه شوند. فایل کانفیگ حتما باید ویرایش شود و تعریف پروایدر را داشته باشد. این کارها را نیوگت به صورت خودکار انجام می‌دهد. ضمنا اینکار باید برای تمام زیر پروژه‌های شما نیز تکرار شود و طوری نباشد که دو کتابخانه از 4 استفاده کنند، دوتای دیگر از 5 و اصلی هم از 6. همه باید یک دست شوند و اسمبلی منسوخ شده قدیمی نیز حذف.
- اسمبلی EF به تنهایی کافی نیست ولی از اینجا به صورت جداگانه قابل دریافت است. باید دقت داشت که ارتقاء به نگارش 6 سه مرحله‌ای است که عنوان شد.
- از اینجا
نظرات مطالب
EF Code First #9
آقای نصیری با سلام.
من 4 تا Entity دارم و یک Entity پایه. استراژی پباده سازی اون هم TPT است . جدول پایه حدود 100 فیلد دارد. موجودیت‌های فرزند هم هرکدام حدود 30 ستون دارند. وقتی با EF Code First داده‌ها را واکشی میکنم کوئری بسیار سنگین را در SQL Server Profiler ایجاد می‌شود  که وقتی آنرا در NotePad کپی میکنم چیزی حدود 60 کیلوبایت می‌شود. ضمناً به تمام ستون‌های جداول نیاز دارم تا آنها را در گرید نمایش دهم.
درصورت امکان راهنمایی کنید.heavyQuery.txt
مطالب
شروع به کار با EF Core 1.0 - قسمت 1 - برپایی تنظیمات اولیه
در ادامه‌ی سری «ارتقاء به ASP.NET Core 1.0» اگر بخواهیم مباحث اعتبارسنجی کاربران و ASP.NET Identity مخصوص آن‌را بررسی کنیم، نیاز است ابتدا مباحث Entity framework Core 1.0 را بررسی کنیم. به همین جهت در طی چند قسمت مباحث پایه‌ای کار با EF Core 1.0 را در ASP.NET Core 1.0، بررسی خواهیم کرد. بنابراین پیشنیاز ضروری این مباحث، مطالعه‌ی سری «ارتقاء به ASP.NET Core 1.0» است و در آن از مباحثی مانند چگونگی کار با فایل‌های کانفیگ جدید، تزریق وابستگی‌ها و سرویس‌ها، فعال سازی سرویس Logging، فعال سازی صفحات مخصوص توسعه دهنده‌ها و ... در ASP.NET Core 1.0 استفاده خواهد شد.


EF Core چیست؟

EF Core یک ORM یا object-relational mapper چندسکویی است که امکان کار با بانک‌های اطلاعاتی مختلف را از طریق اشیاء دات نتی میسر می‌کند. توسط آن قسمت عمده‌ی کدهای مستقیم کار با بانک‌های اطلاعاتی حذف شده و تبدیل به کدهای دات نتی می‌شوند. مزیت این لایه‌ی Abstraction اضافی (لایه‌ای بر روی کدهای مستقیم لایه ADO.NET زیرین)، امکان تعویض بانک اطلاعاتی مورد استفاده، تنها با تغییر کدهای آغازین برنامه‌است؛ بدون نیاز به تغییری در سایر قسمت‌های برنامه. همچنین کار با اشیاء دات نتی و LINQ، مزایایی مانند تحت نظر قرار گرفتن کدها توسط کامپایلر و برخورداری از ابزارهای Refactoring پیشرفته را میسر می‌کنند. به علاوه SQL خودکار تولیدی توسط آن نیز همواره پارامتری بوده و مشکلات حملات تزریق SQL در این حالت تقریبا به صفر می‌رسند (اگر مستقیما SQL نویسی نکنید و صرفا از LINQ استفاده کنید). مزیت دیگر همواره پارامتری بودن کوئری‌ها، رفتار بسیاری از بانک‌های اطلاعاتی با آن‌ها همانند رویه‌های ذخیره شده است که به عدم تولید Query plan‌های مجزایی به ازای هر کوئری رسیده منجر می‌شود که در نهایت سبب بالا رفتن سرعت اجرای کوئری‌ها و مصرف حافظه‌ی کمتری در سمت سرور بانک اطلاعاتی می‌گردد.


تفاوت EF Core با نگارش‌های دیگر Entity framework در چیست؟

سورس باز بودن
EF از نگارش‌های آخر آن بود که سورس باز شد؛ اما EF Core از زمان نگارش‌های پیش نمایش آن به صورت سورس باز در GitHub قابل دسترسی است.

چند سکویی بودن
EF Core برخلاف EF 6.x (آخرین نگارش مبتنی بر Full Framework آن)، نه تنها چندسکویی است و قابلیت اجرای بر روی Mac و لینوکس را نیز دارا است، به علاوه امکان استفاده‌ی از آن در انواع و اقسام برنامه‌های دات نتی مانند UWP یا Universal Windows Platform و Windows phone که پیشتر با EF 6.x میسر نبود، وجود دارد. لیست این نوع سکوها و برنامه‌های مختلف به شرح زیر است:
 • All .NET application (Console, ASP.NET 4, WinForms, WPF)
 • Mac and Linux applications (Mono)
 • UWP (Universal Windows Platform)
 • ASP.NET Core applications
 • Can use EF Core in Windows phone and Windows store app

افزایش تعداد بانک‌های اطلاعاتی پشتیبانی شده
در EF Full یا EF 6.x، هدف اصلی، تنها کار با بانک‌های اطلاعاتی رابطه‌‌ای بود و همچنین مایکروسافت صرفا نگارش‌های مختلف SQL Server را به صورت رسمی پشتیبانی می‌کرد و برای سایر بانک‌های اطلاعاتی دیگر باید از پروایدرهای ثالث استفاده کرد.
در EF Core علاوه بر افزایش تعداد پروایدرهای رسمی بانک‌های اطلاعاتی رابطه‌ای، پشتیبانی از بانک‌های اطلاعاتی NoSQL هم اضافه شده‌است؛ به همراه پروایدر ویژه‌‌ای به نام In Memory جهت انجام ساده‌تر Unit testing. کاری که با نگارش‌های پیشین EF به سادگی و از روز اول پشتیبانی نمی‌شد.

حذف و یا عدم پیاده سازی تعدادی از قابلیت‌های EF 6.x
اگر موارد فوق جزو مهم‌ترین مزایای کار با EF Core باشند، باید درنظر داشت که به علت حذف و یا تقلیل یافتن یک سری از ویژگی‌ها در NET Core.، مانند Reflection (جهت پشتیبانی از دات نت در سکوهای مختلف کاری و خصوصا پشتیبانی از حالتی که کامپایلر مخصوص برنامه‌های UWP نیاز دارد تمام نوع‌ها را همانند زبان‌های C و ++C، در زمان کامپایل بداند)، یک سری از قابلیت‌های EF 6.x مانند Lazy loading هنوز در EF Core پشتیبانی نمی‌شوند. لیست کامل و به روز شده‌ی این موارد را در اینجا می‌توانید مطالعه کنید.
بنابراین امکان انتقال برنامه‌های EF 6.x به EF Core 1.0 عموما وجود نداشته و نیاز به بازنویسی کامل دارند. هرچند بسیاری از مفاهیم آن با EF Code First یکی است.


برپایی تنظیمات اولیه‌ی EF Core 1.0 در یک برنامه‌ی ASP.NET Core 1.0

برای نصب تنظیمات اولیه‌ی EF Core 1.0 در یک برنامه‌ی ASP.NET Core 1.0، جهت کار با مشتقات SQL Server (و SQL LocalDB) نیاز است سه بسته‌ی ذیل را نصب کرد (از طریق منوی Tools -> NuGet Package Manager -> Package Manager Console):
PM> Install-Package Microsoft.EntityFrameworkCore.SqlServer
PM> Install-Package Microsoft.EntityFrameworkCore.Tools -Pre
PM> Install-Package Microsoft.EntityFrameworkCore.SqlServer.Design
البته در این قسمت صرفا از بسته‌ی اول که جهت اتصال به SQL Server است استفاده می‌کنیم. بسته‌های دیگر را در قسمت‌های بعد، برای به روز رسانی اسکیمای بانک اطلاعاتی (مباحث Migrations) و مباحث scaffolding استفاده خواهیم کرد.
پس از اجرای سه دستور فوق، تغییرات مداخل فایل project.json برنامه به صورت ذیل خواهند بود:
{
    "dependencies": {
       // same as before
        "Microsoft.EntityFrameworkCore.SqlServer": "1.0.0",
        "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final",
        "Microsoft.EntityFrameworkCore.SqlServer.Design": "1.0.0"
    }
}
این مداخلی که توسط نیوگت اضافه شده‌اند، نیاز به اصلاح دارند؛ به این صورت:
{
    "dependencies": {
       // same as before
        "Microsoft.EntityFrameworkCore.SqlServer": "1.0.0",
        "Microsoft.EntityFrameworkCore.Tools": {
            "version": "1.0.0-preview2-final",
            "type": "build"
        },
        "Microsoft.EntityFrameworkCore.SqlServer.Design": {
            "version": "1.0.0",
            "type": "build"
        }
    },

    "tools": {
       // same as before
        "Microsoft.EntityFrameworkCore.Tools": {
            "version": "1.0.0-preview2-final",
            "imports": [
                "portable-net45+win8"
            ]
        }   
   }
}
نیاز است در قسمت dependencies مشخص کنیم که ابزارهای اضافه شده مخصوص build هستند و نه اجرای برنامه. همچنین قسمت tools را باید با Microsoft.EntityFrameworkCore.Tools مقدار دهی کرد تا بتوان از این ابزار در خط فرمان، جهت اجرای فرامین migrations استفاده کرد.
بنابراین از همین ابتدای کار، بدون مراجعه‌ی به Package Manager Console، چهار تغییر فوق را به فایل project.json اعمال کرده و آن‌را ذخیره کنید؛ تا کار به روز رسانی و نصب بسته‌ها، به صورت خودکار و همچنین صحیحی انجام شود.


فعال سازی صفحات مخصوص توسعه دهنده‌های EF Core 1.0

در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 5 - فعال سازی صفحات مخصوص توسعه دهنده‌ها» با تعدادی از اینگونه صفحات آشنا شدیم. برای EF Core نیز بسته‌ی مخصوصی به نام Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore وجود دارد که امکان فعال سازی صفحه‌ی نمایش خطاهای بانک اطلاعاتی را میسر می‌کند. بنابراین ابتدا به فایل project.json مراجعه کرده و این بسته را اضافه کنید:
{
    "dependencies": {
       // same as before
        "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": "1.0.0"
    }
}
سپس می‌توان متد جدید UseDatabaseErrorPage را در متد Configure کلاس آغازین برنامه، فراخوانی کرد:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
   if (env.IsDevelopment())
   {
      app.UseDatabaseErrorPage();
   }
با فعال سازی این صفحه، اگر در حین توسعه‌ی برنامه و اتصال به بانک اطلاعاتی، خطایی رخ دهد، بجای مشاهده‌ی یک صفحه‌ی خطای عمومی (اگر UseDeveloperExceptionPage را فعال کرده باشید)، اینبار ریز جزئیات بیشتری را به همراه توصیه‌هایی از طرف تیم EF مشاهده خواهید کرد.


تعریف اولین Context برنامه و مشخص سازی رشته‌ی اتصالی آن


در این تصویر، زیر ساخت نگاشت‌های EF Core را مشاهده می‌کنید. در سمت چپ، ظرفی را داریم به نام DB Context که در برگیرنده‌ی Db Setها است. در سمت راست که بیانگر ساختار کلی یک بانک اطلاعاتی است، معادل این‌ها را مشاهده می‌کنیم. هر Db Set به یک جدول بانک اطلاعاتی نگاشت خواهد شد و متشکل است از کلاسی به همراه یک سری خواص که این‌ها نیز به فیلدها و ستون‌های آن جدول در سمت بانک اطلاعاتی نگاشت می‌شوند.
بنابراین برای شروع کار، پوشه‌ای را به نام Entities به پروژه اضافه کرده و سپس کلاس ذیل را به آن اضافه می‌کنیم:
namespace Core1RtmEmptyTest.Entities
{
    public class Person
    {
        public int PersonId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
}
کلاس Person بیانگر ساختار جدول اشخاص بانک اطلاعاتی است. برای اینکه این کلاس را تبدیل و نگاشت به یک جدول کنیم، نیاز است آن‌را به صورت یک DbSet در معرض دید EF Core قرار دهیم و اینکار در کلاسی که از DbContex مشتق می‌شود، صورت خواهد گرفت:
using Microsoft.EntityFrameworkCore;

namespace Core1RtmEmptyTest.Entities
{
    public class ApplicationDbContext : DbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
        {
        }

        public DbSet<Person> Persons { get; set; }
    }
}
بنابراین در ادامه کلاس جدید ApplicationDbContext را که از کلاس پایه DbContext مشتق می‌شود تعریف کرده و سپس کلاس Person را به صورت یک DbSet در معرض دید EF Core قرار می‌دهیم.
سازنده‌ی این کلاس نیز به نحو خاصی تعریف شده‌است. اگر به سورس‌های EF Core مراجعه کنیم، کلاس پایه‌ی DbContext دارای دو سازنده‌ی با و بدون پارامتر است:
protected DbContext()
   : this((DbContextOptions) new DbContextOptions<DbContext>())
{
}

public DbContext([NotNull] DbContextOptions options)
{
  // …
}
اگر از سازنده‌ی بدون پارامتر استفاده کنیم و برای مثال در کلاس ApplicationDbContext فوق، به طور کامل سازنده‌ی تعریف شده را حذف کنیم، باید به نحو ذیل تنظیمات بانک اطلاعاتی را مشخص کنیم:
using Microsoft.EntityFrameworkCore;

namespace Core1RtmEmptyTest.Entities
{
    public class ApplicationDbContext : DbContext
    {
        public DbSet<Person> Persons { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(@"... connection string ...");
        }
    }
}
در این حالت باید متد OnConfiguring را override و یا بازنویسی کنیم، تا بتوان از اول مشخص کرد که قرار است از پروایدر SQL Server استفاده کنیم و ثانیا رشته‌ی اتصالی به آن چیست.
اما چون در یک برنامه‌ی ASP.NET Core، کار ثبت سرویس مربوط به EF Core، در کلاس آغازین برنامه انجام می‌شود و در آنجا به سادگی می‌توان به خاصیت Configuration برنامه دسترسی یافت و توسط آن رشته‌ی اتصالی را دریافت کرد، مرسوم است از سازنده‌ی با پارامتر DbContext به نحوی که در ابتدا عنوان شد، استفاده شود.
بنابراین در ادامه، پس از مطالعه‌ی مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 7 - کار با فایل‌های config» به فایل appsettings.json مراجعه کرده و تنظیمات رشته‌ی اتصالی برنامه را به صورت ذیل در آن مشخص می‌کنیم:
{
    "ConnectionStrings": {
        "ApplicationDbContextConnection": "Data Source=(local);Initial Catalog=TestDbCore2016;Integrated Security = true"
    }
}
باید دقت داشت که نام این مداخل کاملا اختیاری هستند و در نهایت باید در کلاس آغازین برنامه به صورت صریحی مشخص شوند.
در اینجا به وهله‌ی پیش فرض SQL Server اشاره شده‌است؛ از حالت اعتبارسنجی ویندوزی SQL Server استفاده می‌شود و بانک اطلاعاتی جدیدی به نام TestDbCore2016 در آن مشخص گردیده‌است.

پس از تعریف رشته‌ی اتصالی، متد OnConfiguring را از کلاس ApplicationDbContext حذف کرده و از همان نگارش دارای سازنده‌ی با پارامتر آن استفاده می‌کنیم. برای اینکار به کلاس آغازین برنامه مراجعه کرده و توسط متد AddDbContext این Context را به سرویس‌های ASP.NET Core معرفی می‌کنیم:
    public class Startup
    {
        public IConfigurationRoot Configuration { set; get; }

        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                                .SetBasePath(env.ContentRootPath)
                                .AddJsonFile("appsettings.json", reloadOnChange: true, optional: false)
                                .AddJsonFile($"appsettings.{env}.json", optional: true);
            Configuration = builder.Build();
        }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton<IConfigurationRoot>(provider => { return Configuration; });
            services.AddDbContext<ApplicationDbContext>(options =>
            {
                options.UseSqlServer(Configuration["ConnectionStrings:ApplicationDbContextConnection"]);
            });
در اینجا جهت یادآوری مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 7 - کار با فایل‌های config» نحوه‌ی وهله سازی خاصیت Configuration که در متد UseSqlServer مورد استفاده قرار گرفته‌است، نیز ذکر شده‌است.
بنابراین قسمت options.UseSqlServer را یا در اینجا مقدار دهی می‌کنید و یا از طریق بازنویسی متد OnConfiguring کلاس Context برنامه.


یک نکته: امکان تزریق IConfigurationRoot به کلاس Context برنامه وجود دارد

با توجه به اینکه Context برنامه را به صورت یک سرویس به ASP.NET Core معرفی کردیم، امکان تزریق وابستگی‌ها نیز در آن وجود دارد. یعنی بجای روش فوق، می‌توان IConfigurationRoot را به سازنده‌ی کلاس Context برنامه نیز تزریق کرد:
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;

namespace Core1RtmEmptyTest.Entities
{
    public class ApplicationDbContext : DbContext
    {
        private readonly IConfigurationRoot _configuration;

        public ApplicationDbContext(IConfigurationRoot configuration)
        {
            _configuration = configuration;
        }

        //public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
        //{
        //}

        public DbSet<Person> Persons { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(_configuration["ConnectionStrings:ApplicationDbContextConnection"]);
        }
    }
}
با توجه به اینکه IConfigurationRoot در کلاس ConfigureServices به صورت Singleton، به مجموعه‌ی سرویس‌های برنامه معرفی شده‌است، از آن در تمام کلاس‌های برنامه که تحت نظر سیستم تزریق وابستگی‌های توکار ASP.NET Core هستند، می‌توان استفاده کرد.
در این حالت متد ConfigureServices کلاس آغازین برنامه، چنین شکلی را پیدا می‌کند و ساده می‌شود:
public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>();


یک نکته: امکان تزریق ApplicationDbContext به تمام کلاس‌های برنامه وجود دارد

همینقدر که ApplicationDbContext را به عنوان سرویسی در ConfigureServices تعریف کردیم، امکان تزریق آن در اجزای مختلف یک برنامه‌ی ASP.NET Core نیز وجود دارد:
using System.Linq;
using Core1RtmEmptyTest.Entities;
using Microsoft.AspNetCore.Mvc;

namespace Core1RtmEmptyTest.Controllers
{
    public class TestDBController : Controller
    {
        private readonly ApplicationDbContext _ctx;

        public TestDBController(ApplicationDbContext ctx)
        {
            _ctx = ctx;
        }

        public IActionResult Index()
        {
            var name = _ctx.Persons.First().FirstName;
            return Json(new { firstName = name });
        }
    }
}
در اینجا نحوه‌ی تزریق DB Context برنامه را به یک کنترلر مشاهده می‌کنید. البته هرچند تزریق یک کلاس مشخص به این شکل، تزریق وابستگی‌ها نام ندارد و هنوز این کنترلر دقیقا وابسته‌است به پیاده سازی خاص کلاس ApplicationDbContext، اما ... در کل امکان آن هست.

در این حالت پس از اجرای برنامه، خطای ذیل را مشاهده خواهیم کرد:


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