در زمان ساخت مدل از بانک اطلاعاتی در روش Database First به صورت پیش فرض تنظیمات مربوط به اتصال (Connection String) مدل به بانک اطلاعاتی در فایل config برنامه ذخیره میشود. مشکل این روش آن است که در سیستمهای مختلف، بسته به بستری که نرم افزار قرار است بر روی آن اجرا شود، باید تنظیمات مربوط به بانک اطلاعاتی صورت گیرد.
همانطور که مشاهده میکنید، در Constructor این کلاس، نام Connection String مورد استفاده جهت اتصال به بانک اطلاعاتی به صورت زیر آورده شده که به Connection String ذخیره شده در فایل Config اشاره میکند:
جهت تولید پویای Connection String، بسته به تنظیمات کاربر، نیاز است تا در آخر Connection String ی با فرمت بالا در اختیار Entity Framework قرار دهیم تا امکان اتصال به بانک فراهم شود. جهت تبدیل Connection String معمول ADO.NET به Connection String قابل فهم EF میتوان از کلاس EntityConnectionStringBuilder به صورت زیر استفاده کرد:
همانطور که مشاهده میکنید، متد بالا با دریافت یک connectionString که همان ADO.NET ConnectionString ما میباشد، تنظیمات و Metadata مورد نیاز Entity Framework را به آن اضافه کرده و یک EF ConnectionString برمیگرداند.
با اضافه شدن پارامتر connectionString به سازنده کلاس PersonnelyEntities برای ساخت یک نمونه از مدل ساخته شده در کد نیاز است تا Connection String مورد نظر جهت برقراری ارتباط با بانک را به عنوان پارامتر، به متد سازنده پاس دهیم. سپس مقدار این پارامتر به کلاس والد ( DbContext ) جهت برقراری ارتباط با بانک اطلاعاتی ارجاع داده شده:
در آخر به صورت زیر میتوان توسط EF به بانک اطلاعاتی مورد نظر متصل شد :
با این روش میتوان ADO Connection String مربوط به اتصال بانک اطلاعاتی را به راحتی به صورت داینامیک به وسیله اطلاعات وارد شده توسط کاربر و کلاسهای تولید Connection String نظیر SQLConnectionStringBuilder تولید کرد و بدون تغییر در کدهای برنامه، به بانکهای مختلفی متصل شد. همچنین با داینامیک کردن متد Provider کلاس EntityConnectionStringBuilder که در کد بالا با "System.Data.SqlClient" مقدار دهی شده، میتوان وابستگی برنامه بانک اطلاعی خاص را از بین برد و بسته به تنظیمات مورد نظر کاربر، به موتورهای مختلف بانک اطلاعاتی متصل شد که البته لازمه این کار رعایت یکسری نکات فنی در پیاده سازی پروژه است که از حوصله این مقاله خارج است.
مثلا فرض کنید شما در زمان توسعه نرم افزار، SQL Server را به صورت Local بر روی سیستم خود نصب کرده اید و Connection String ساخته شده توسط ویزارد Entity Framework بر همین اساس ساخته و ذخیره شدهاست. حال بعد از انتشار برنامه، شخصی تصمیم دارد برنامه را بر روی سیستمی نصب کند که بانک اطلاعاتی Local نداشته و تصمیم به اتصال به یک بانک اطلاعاتی بر روی سرور دیگر یا با مشخصات (Login و Password و ...) دیگر را دارد. برای این مواقع نیاز به پیاده سازی روشی است تا کاربر نهایی بتواند تنظیمات مربوط به اتصال به بانک اطلاعاتی را تغییر دهد.
روشهای مختلفی مثل تغییر فایل app.config به صورت Runtime یا ... در سایتهای مختلف ارائه شده که اکثرا روشهای غیر اصولی و زمانبری جهت پیاده سازی هستند.
سادهترین روش جهت انجام این کار، اعمال تغییری کوچک در Constructor کلاس مدل مشتق شده از DBContext میباشد. فرض کنید مدلی از بانک اطلاعاتی Personnely با نام PersonallyEntities ساخته اید که حاصل آن کلاس زیر خواهد بود:
public partial class PersonallyEntities : DbContext { public PersonallyEntities() : base("name=PersonallyEntities") { } }
"name=PersonallyEntities"
اگر به Connection String ذخیره شده در فایل Config دقت کنید متوجه میشوید که Connection String ذخیره شده، دارای فرمتی خاص و متفاوتی نسبت به Connection String معمولی ADO.NET است. متن ذخیره شده شامل تنظیمات و Metadata مدل ساخته شده جهت ارتباط با بانک اطلاعاتی نیز میباشد:
metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlClient;provider connection string="data source=.;initial catalog=Personally;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"
public static string BuildEntityConnection(string connectionString) { var entityConnection = new EntityConnectionStringBuilder { Provider = "System.Data.SqlClient", ProviderConnectionString = connectionString, Metadata = "res://*" }; return entityConnection.ToString(); }
برای اینکه بتوان EF ConnectionString تولید شده را در هنگام اجرای برنامه به صورت Runtime اعمال کرد، نیاز است تا تغییر کوچکی در Constructor کلاس مدل تولید شده توسط Entity Framework ایجاد کرد. کلاس PersonnelyEntities به صورت زیر تغییر پیدا میکند:
public partial class PersonallyEntities : DbContext { public PersonallyEntities(string connectionString) : base(connectionString) { } }
: base(connectionString)
var entityConnectionString = BuildeEntityConnection("Data Source=localhost;Initial Catalog=Personally; Integrated Security=True"); var PersonallyDb = new PersonallyEntities(entityConnectionString);
موفق باشید
اشتراکها
NET 7 Release Candidate 1. منتشر شد
در اینجا به صورت اختصاصی در موردش بحث شده: بالا بردن سرعت بارگذاری اولیه EF Code first با تعداد مدلهای زیاد
نکته جالب اینجاست که EF Core دیگه چنین کاری رو انجام نمیده (بررسی تطابق ساختار بانک اطلاعاتی با مدلهای برنامه در حین اجرا). یعنی برنامه به سرعت اجرا میشه. اگر بانک اطلاعاتی با مدلهای شما هماهنگ نبود، فقط خطای عدم امکان اجرای کوئری رو میگیرید. مثلا این خطار رو میگیرید که فلان جدول وجود نداره و این خطا هم از طرف بانک اطلاعاتی صادر میشه و نه از طرف EF Core. راهحلش اجرای مهاجرتها به صورت دستی است. دیگر هیچ چیزی در اینجا خودکار نیست و مسئولیتش هم با خود شما است.
اشتراکها
خلاصه جلسه EF6/EF7 در TechEd 2014
بد نیست لیست تعدادی از بانکهای اطلاعاتی مهم قابل استفاده در دات نت به همراه درایورهای ADO.NET آنها را با هم مرور نمائیم.
بانکهای اطلاعاتی قابل استفاده در دات نت فریم ورک | ||||||
ردیف | بانک اطلاعاتی | سایت مرجع | درایور ADO.NET | امکان استفاده از LINQ | مجوز استفاده | توضیحات |
1 | SQL Server 2000/2005/2008/2008 R2 | + | توکار (به صورت پیش فرض در دات نت فریم ورک موجود است) | بلی . به کمک LINQ to SQL ، Entity Framework ، NHibernate و بسیاری از ORM های دیگر | رایگان - تجاری | نسخههای Express آن رایگان است. |
2 | Microsoft SQL Azure | + | بلی : + | بلی. به کمک LINQ to SQL و Entity Framework | تجاری | |
3 | SQL Server Compact | + | بلی : + | بلی. به کمک LINQ to SQL و Entity Framework | رایگان | |
4 | Advantage Database Server | + | قابل دریافت از سایت اصلی: + | بلی. به کمک Entity framework و Telerik OpenAccess ORM | تجاری | |
5 | SQL Anywhere | + | قابل دریافت از سایت اصلی: + | بلی. به کمک Entity framework و Telerik OpenAccess ORM | رایگان - تجاری | Web Edition آن رایگان است. |
6 | MySQL | + | قابل دریافت از سایت اصلی : + | بلی . به کمک NHibernate ، LightSpeed ، DbLinq و تعدادی دیگر از ORM's | رایگان - تجاری | |
7 | Oracle | + | پشتیبانی توکار آن به زودی حذف خواهد شد اما از سایت اصلی قابل دریافت است : + | بلی . به کمک NHibernate ، LightSpeed ، DbLinq و تعدادی دیگر از ORM's | رایگان - تجاری | نسخهی Express آن رایگان است. |
8 | Access | + | توکار | بلی. به کمک ALinq ، NHibernate و یا LINQ to DataSets | تجاری | اگر از دات نت فریم ورک سه و نیم، سرویس پک یک استفاده کنید، امکان استفاده از LINQ to SQL جهت کار با بانکهای اطلاعاتی اکسس نیز مهیا است: + |
9 | SQLite | + | مهیا به صورت سورس باز : + | بلی. درایور ADO.NET آن پشتیبانی از Entity Framework را نیز اضافه میکند. همچنین NHibernate ، ALinq و سایر ORM's را باید به این لیست اضافه کرد. | رایگان | |
10 | Firebird | + | قابل دریافت از سایت اصلی: + | بلی. توسط ALinq ، NHibernate و موارد دیگر. | رایگان | |
11 | PostgreSQL | + | قابل دریافت از سایت اصلی: + | بلی. توسط NHibernate ، DBLinq و موارد دیگر | رایگان | |
12 | DB2 UDB | + | قابل دریافت از سایت اصلی: + | بلی. توسط NHibernate | تجاری | |
13 | ScimoreDB | + | قابل دریافت از سایت اصلی: + | محدود. توسط LINQ to DataSets | رایگان | |
14 | MongoDB | + | معرفی شده در سایت اصلی : + | بلی. درایور ADO.NET معرفی شده به همراه پروایدر LINQ نیز میباشد. | رایگان | |
15 | CouchDB | + | معرفی شده در سایت اصلی : + | محدود | رایگان | |
16 | VistaDB | + | اساسا برای دات نت نوشته شده است. | بلی. به کمک Entity framework | تجاری |
نظرات مطالب
EF Code First #1
یک موتور اصلی EF بیشتر وجود نداره. برای کار کردن با این موتور اصلی سه روش حداقل تدارک دیده شده:
الف) database first: مربوط به زمانیکه یک دیتابیس از پیش طراحی شده با ساختار جداول و ارتباطات آن موجود است. این روش، ساختار بانک اطلاعاتی شما رو مهندسی معکوس کرده و یک سری کد و دیاگرام را برای استفاده توسط موتور اصلی EF تولید میکنه.
ب) روش model first: در VS.NET میتونید طراحیهای مرتبط با جداول، کلاسها و روابط رو انجام بدید. کدهای اضافی برای کار با موتور EF و همچنین به روز رسانی بانک اطلاعاتی رو به صورت خودکار انجام خواهد داد.
ج) روش code first: در این روش دیگر خبری از طراح بصری نیست. کار با کدنویسی و طراحی کلاسها و ایجاد روابط بین آنها توسط برنامه نویس شروع میشود. نهایتا این کلاسها توسط موتور EF استفاده خواهند شد. امکان تبدیل این کلاسها به بانک اطلاعاتی متناظر و همچنین به روز رسانی خودکار بانک اطلاعاتی با تغییر ساختار کلاسها هم پیش بینی شده. روش code first بهترین حالت است برای کسانی که نمیخواهند از انبوهی از کدهای تولید شده به صورت خودکار (حاصل از مهندسی معکوس یک بانک اطلاعاتی موجود) استفاده کنند و میخواهند کنترل بیشتری بر روابط و اختصاصی سازی آنها داشته باشند. در این حالت میتونید بدون نیاز به یک بانک اطلاعاتی یک برنامه را کامل کنید (منهای مباحث تست سیستم).
روش code first در حال حاضر روشی است که بیشتر توسط تیم EF تبلیغ میشود و در حال توسعه است. مابقی رو هم کم کم دارند تبدیل میکنند به پوستهای برای حالت code first. مثلا ابزار تهیه کردند برای مهندسی معکوس یک بانک اطلاعاتی موجود به روش code first. کد نهایی تمیزتری داره؛ چون کلاسها را خودتان طراحی میکنید و توسط ابزارها به صورت خودکار تولید نمیشوند، کنترل بیشتر و نهایتا کیفیت بالاتری دارند. ساده است؛ درگیر نگهداری edmx modelها نخواهید بود. به روز رسانی بانک اطلاعاتی آن هم میتواند کاملا خودکار شود.
برای اطلاعات بیشتر در مورد مزایای این روش یا تاریخچه EF متن قسمت جاری را یکبار مطالعه کرده و قسمتهای «مروری سریع بر تاریخچه Entity framework code first» و «مزایای EF Code first» را بررسی کنید.
+ کل قسمت EF از اینجا به صورت یک فایل PDF قابل دریافت است. در مورد اکثر مواردی که عنوان کردید به صورت مجزا بحث شده و توضیحات کافی ارائه شدن.
الف) database first: مربوط به زمانیکه یک دیتابیس از پیش طراحی شده با ساختار جداول و ارتباطات آن موجود است. این روش، ساختار بانک اطلاعاتی شما رو مهندسی معکوس کرده و یک سری کد و دیاگرام را برای استفاده توسط موتور اصلی EF تولید میکنه.
ب) روش model first: در VS.NET میتونید طراحیهای مرتبط با جداول، کلاسها و روابط رو انجام بدید. کدهای اضافی برای کار با موتور EF و همچنین به روز رسانی بانک اطلاعاتی رو به صورت خودکار انجام خواهد داد.
ج) روش code first: در این روش دیگر خبری از طراح بصری نیست. کار با کدنویسی و طراحی کلاسها و ایجاد روابط بین آنها توسط برنامه نویس شروع میشود. نهایتا این کلاسها توسط موتور EF استفاده خواهند شد. امکان تبدیل این کلاسها به بانک اطلاعاتی متناظر و همچنین به روز رسانی خودکار بانک اطلاعاتی با تغییر ساختار کلاسها هم پیش بینی شده. روش code first بهترین حالت است برای کسانی که نمیخواهند از انبوهی از کدهای تولید شده به صورت خودکار (حاصل از مهندسی معکوس یک بانک اطلاعاتی موجود) استفاده کنند و میخواهند کنترل بیشتری بر روابط و اختصاصی سازی آنها داشته باشند. در این حالت میتونید بدون نیاز به یک بانک اطلاعاتی یک برنامه را کامل کنید (منهای مباحث تست سیستم).
روش code first در حال حاضر روشی است که بیشتر توسط تیم EF تبلیغ میشود و در حال توسعه است. مابقی رو هم کم کم دارند تبدیل میکنند به پوستهای برای حالت code first. مثلا ابزار تهیه کردند برای مهندسی معکوس یک بانک اطلاعاتی موجود به روش code first. کد نهایی تمیزتری داره؛ چون کلاسها را خودتان طراحی میکنید و توسط ابزارها به صورت خودکار تولید نمیشوند، کنترل بیشتر و نهایتا کیفیت بالاتری دارند. ساده است؛ درگیر نگهداری edmx modelها نخواهید بود. به روز رسانی بانک اطلاعاتی آن هم میتواند کاملا خودکار شود.
برای اطلاعات بیشتر در مورد مزایای این روش یا تاریخچه EF متن قسمت جاری را یکبار مطالعه کرده و قسمتهای «مروری سریع بر تاریخچه Entity framework code first» و «مزایای EF Code first» را بررسی کنید.
+ کل قسمت EF از اینجا به صورت یک فایل PDF قابل دریافت است. در مورد اکثر مواردی که عنوان کردید به صورت مجزا بحث شده و توضیحات کافی ارائه شدن.
الگوی decorator، امکان محصور کردن یک شیء مفروض را با لایهای بر فراز آن میسر میکند. برای مثال بجای اینکه در تمام متدهای سرویسی از try/catch استفاده کنیم، میتوانیم این متدها را با یک ExceptionHandlingDecorator مزین کنیم و یا از این دست اعمال تکراری میتوان به لاگ کردن ورودی و خروجیهای یک متد و یا کش کردن اطلاعات آنها نیز اشاره کرد. حتی عملیاتی مانند تشخیص خواص تغییر یافتهی یک شیء در Entity framework نیز به کمک همین مزین کنندهها که شیء اصلی در حال استفاده را با ایجاد لایهای بر روی آنها محصور میکنند، انجام میشود. به این عملیات Aspect oriented programming و یا AOP نیز میگویند؛ در اینجا واژهی Aspect به اعمال مشترک و متداول موجود در برنامه اشاره میکند. در این مطلب قصد داریم نمونهای از این تزئین کنندهها را به کمک سیستم تزریق وابستگیهای NET Core. پیاده سازی کنیم.
پیاده سازی الگوی Decorator به کمک سیستم تزریق وابستگیهای NET Core.
مثال زیر را در نظر بگیرید که در آن یک سرویس تعریف شدهاست و در این بین استثنائی رخ دادهاست.
میخواهیم بدون تغییری در کدهای این کلاس، به متدهای آن در حین اجرای نهایی، یک try/catch را به همراه logging، اضافه کنیم. به همین جهت نیاز خواهیم داشت تا یک محصور کننده (تزئین کننده یا decorator در اینجا) را برای آن طراحی کنیم:
این محصور کننده نیز دقیقا همان ITaskService را پیاده سازی میکند؛ اما در سازندهی آن یک ITaskService را نیز دریافت میکند. علت اینجا است که توسط آن بتوان متدهای ITaskService تزریقی را اجرا کرد و بر روی آن اعمالی مانند کش کردن، لاگ کردن و مدیریت استثناءها و غیره را انجام داد. برای مثال در متد Run آن مشاهده میکنید که متد Run همان وهلهی تزریقی اجرا شدهاست؛ اما درون یک try/catch به همراه لاگ کردن جزئیات استثنای رخ داده.
مزیت اینکار، پیاده سازی اصل DRY یا Don't repeat yourself است. کاری که برای رفع این مشکل قرار است انجام دهیم، استفاده از یک تزئین کننده (محصور کننده)، کپسوله سازی اعمال تکراری و سپس اتصال آن به قسمتهای مختلف برنامه است. همچنین در این حالت اصل open closed principle نیز بهتر رعایت خواهد شد. از این جهت که کدهای تکراری برنامه به یک لایهی دیگر منتقل شدهاند و دیگر نیازی نیست برای تغییر آنها، کدهای قسمتهای اصلی برنامه را تغییر داد (کدهای برنامه باز خواهند بود برای توسعه و بسته برای تغییر).
پس از طراحی این تزئین کننده، اکنون نوبت به معرفی آن به سیستم تزریق وابستگیهای NET Core. است:
روش انجام اینکار را نیز در «قسمت ششم - دخالت در مراحل وهله سازی اشیاء توسط IoC Container» بیشتر بررسی کردهایم.
در اینجا هم میتوان در صورت نیاز اصل کلاس MyTaskService را بدون هیچ نوع تزئین کنندهای از سیستم تزریق وابستگیها دریافت کرد و یا اگر وهلهای از سرویس ITaskService را از آن درخواست کردیم، ابتدا شیء MyTaskServiceDecorator وهله سازی شده و سپس توسط آن یک نمونهی محصور شده و تزئین شدهی MyTaskService به فراخوان بازگشت داده خواهد شد.
ساده سازی معرفی تزئین کنندهها به سیستم تزریق وابستگیهای NET Core. به کمک Scrutor
در «قسمت هشتم - ساده سازی معرفی سرویسها توسط Scrutor» با کتابخانهی Scrutor آشنا شدیم. یکی دیگر از قابلیتهای آن، امکان ساده سازی تعریف تزئین کنندها است:
در اینجا معادل کدهایی را که با روش factory خود NET Core. نوشتیم، ملاحظه میکنید. ابتدا نیاز است خود سرویس اصلی غیر تزئین شده، به نحو متداولی به سیستم معرفی شود. سپس متد الحاقی جدید <,>Decorate را با همان اینترفیس و اینبار با Decorator مدنظر معرفی میکنیم. کاری که Scrutor در اینجا انجام میدهد، یافتن سرویس ITaskService معرفی شدهی پیشین و تعویض آن با MyTaskServiceDecorator میباشد. بنابراین نیاز است تعریف services.AddTransient پیش از تعریف services.Decorate انجام شده باشد. این روش تمیزتر از روش قبلی به نظر میرسد و شامل وهله سازی مستقیم MyTaskServiceDecorator به همراه فراهم آوردن تمام پارامترهای سازندهی آن توسط ما نیست.
پیاده سازی الگوی Decorator به کمک سیستم تزریق وابستگیهای NET Core.
مثال زیر را در نظر بگیرید که در آن یک سرویس تعریف شدهاست و در این بین استثنائی رخ دادهاست.
public interface ITaskService { void Run(); } public class MyTaskService : ITaskService { public void Run() { throw new InvalidOperationException("An exception from the MyTaskService!"); } }
using System; using Microsoft.Extensions.Logging; namespace CoreIocServices { public class MyTaskServiceDecorator : ITaskService { private readonly ILogger<MyTaskServiceDecorator> _logger; private readonly ITaskService _decorated; public MyTaskServiceDecorator( ILogger<MyTaskServiceDecorator> logger, ITaskService decorated) { _logger = logger; _decorated = decorated; } public void Run() { try { _decorated.Run(); } catch (Exception ex) { _logger.LogCritical(ex, "An unhandled exception has been occurred."); } } } }
مزیت اینکار، پیاده سازی اصل DRY یا Don't repeat yourself است. کاری که برای رفع این مشکل قرار است انجام دهیم، استفاده از یک تزئین کننده (محصور کننده)، کپسوله سازی اعمال تکراری و سپس اتصال آن به قسمتهای مختلف برنامه است. همچنین در این حالت اصل open closed principle نیز بهتر رعایت خواهد شد. از این جهت که کدهای تکراری برنامه به یک لایهی دیگر منتقل شدهاند و دیگر نیازی نیست برای تغییر آنها، کدهای قسمتهای اصلی برنامه را تغییر داد (کدهای برنامه باز خواهند بود برای توسعه و بسته برای تغییر).
پس از طراحی این تزئین کننده، اکنون نوبت به معرفی آن به سیستم تزریق وابستگیهای NET Core. است:
namespace CoreIocSample02 { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddTransient<MyTaskService>(); services.AddTransient<ITaskService>(serviceProvider => new MyTaskServiceDecorator( serviceProvider.GetService<ILogger<MyTaskServiceDecorator>>(), serviceProvider.GetService<MyTaskService>()) );
در اینجا هم میتوان در صورت نیاز اصل کلاس MyTaskService را بدون هیچ نوع تزئین کنندهای از سیستم تزریق وابستگیها دریافت کرد و یا اگر وهلهای از سرویس ITaskService را از آن درخواست کردیم، ابتدا شیء MyTaskServiceDecorator وهله سازی شده و سپس توسط آن یک نمونهی محصور شده و تزئین شدهی MyTaskService به فراخوان بازگشت داده خواهد شد.
ساده سازی معرفی تزئین کنندهها به سیستم تزریق وابستگیهای NET Core. به کمک Scrutor
در «قسمت هشتم - ساده سازی معرفی سرویسها توسط Scrutor» با کتابخانهی Scrutor آشنا شدیم. یکی دیگر از قابلیتهای آن، امکان ساده سازی تعریف تزئین کنندها است:
namespace CoreIocSample02 { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddTransient<ITaskService, MyTaskService>(); services.Decorate<ITaskService, MyTaskServiceDecorator>();