در نگارش قبلی EF Code first به ازای یک پروژه تنها یک سیستم Migrationقابل تعریف بود و این سیستم مهاجرت، تنها با یک DbContext کار میکرد. در نگارش ششم این کتابخانه، سیستم مهاجرت Code first آن از چندین DbContext، به ازای یک پروژه که به یک بانک اطلاعاتی اشاره میکنند، پشتیبانی میکند. مزیت اینکار اندکی بهبود در نگهداری تنها کلاس DbContext تعریف شده است. برای مثال میتوان یک کلاس DbContext مخصوص قسمت ثبت نام را ایجاد کرد. یک کلاس DbContext مخصوص کلیه جداول مرتبط با مقالات را و همینطور الی آخر. نهایتا تمام این Contextها سبب ایجاد یک بانک اطلاعاتی واحد خواهند شد.
اگر در یک پروژه EF Code first چندین Context وجود داشته باشد و دستور enable-migrations را بدون پارامتری فراخوانی کنیم، پیغام خطای More than one context type was found in the assmbly xyz را دریافت خواهیم کرد.
الف)اما در EF 6 میتوان با بکار بردن سوئیچ جدید ContextTypeName، به ازای هر Context، مهاجرت مرتبط با آنرا تنظیم نمود:
همچنین در اینجا نیز میتوان با استفاده از سوئیچ MigrationDirectory، فایلهای تولید شده را در پوشههای مجزایی ذخیره کرد.
ب)در مرحله بعد، نیاز به فراخوانی دستور add-migration است:
با اجرای دستور enable-migrations یک کلاس Configuration جهت DbContext مشخص شده، ایجاد میشود. سپس آدرس کامل این کلاس را به همراه ذکر دقیق فضای نام آن در اختیار دستور add-migration قرار میدهیم.
ذکر کامل فضای نام، از این جهت مهم است که کلاس Configuration به ازای Contextهای مختلف ایجاد شده، یک نام را خواهد داشت؛ اما در فضاهای نام متفاوتی قرار میگیرد.
با اجرای دستور add-migration، کدهای سی شارپ مورد نیاز جهت اعمال تغییرات بر روی ساختار بانک اطلاعاتی تولید میشوند. در مرحله بعد، این کدها تبدیل به دستورات SQL متناظری شده و بر روی بانک اطلاعاتی اجرا خواهند شد.
بدیهی است اگر دو Context در برنامه تعریف کرده باشید، دوبار باید دستور enable-migrations و دوبار دستور add-migration را با پارامترهای اشاره کننده به Conetxtهای مدنظر اجرا کرد.
ج)سپس برای اعمال این تغییرات، باید دستور update-database را اجرا کرد.
اینبار دستور update-database نیز بر اساس نام کامل کلاس Configuration مدنظر باید اجرا گردد و به ازای هر Context موجود، یکبار نیاز است اجرا گردد.
نهایتا اگر به بانک اطلاعاتی مراجعه کنید، تمام جداول و تعاریف را یکجا در همان بانک اطلاعاتی میتوانید مشاهده نمائید.
داشتن چندین Context در برنامه و مدیریت تراکنشها
در EF، هر DbContext معرف یک واحد کار است. یعنی تراکنشها و چندین عمل متوالی مرتبط انجام شده، درون یک DbContext معنا پیدا میکنند. متد SaveChanges نیز بر همین اساس است که کلیه اعمال ردیابی شده در طی یک واحد کار را در طی یک تراکنش به بانک اطلاعاتی اعمال میکند. همچنین مباحثی مانند lazy loading نیز در طی یک Context مفهوم دارند. به علاوه دیگر امکان join نویسی بین دو Context وجود نخواهد داشت. باید اطلاعات را از یکی واکشی و سپس این اطلاعات درون حافظهای را به دیگری ارسال کنید.
یک نکته
میتوان یک DbSet را در چندین Context تعریف کرد. یعنی اگر بحث join نویسی مطرح است، با تکرار تعریف DbSetها اینکار قابل انجام است اما این مساله اساس جداسازی Contextها را نیز زیر سؤال میبرد.
داشتن چندین Context در برنامه و مدیریت رشتههای اتصالی
در EF Code first روشهای مختلفی برای تعریف رشته اتصالی به بانک اطلاعاتی وجود دارند. اگر تغییر خاصی در کلاس مشتق شده از DbContext ایجاد نکنیم، نام کلید رشته اتصالی تعریف شده در فایل کانفیگ باید به نام کامل کلاس Context برنامه اشاره کند. اما با داشتن چندین Context به ازای یک دیتابیس میتوان از روش ذیل استفاده کرد:
در اینجا در سازنده کلاسهای Context تعریف شده، نام کلید رشته اتصالی موجود در فایل کانفیگ برنامه ذکر شده است. به این ترتیب این دو Context به یک بانک اطلاعاتی اشاره خواهند کرد.
چه زمانی بهتر است از چندین Context در برنامه استفاده کرد؟
عموما در طراحیهای سازمانی SQL Server، تمام جداول از schema مدیریتی به نام dbo استفاده نمیکنند. جداول فروش از schema خاص خود و جداول کاربران از schema دیگری استفاده خواهند کرد. با استفاده از چندین Context میتوان به ازای هر کدام از schemaهای متفاوت موجود، «یک ناحیه ایزوله» را ایجاد و مدیریت کرد.
در این حالت در EF 6 میتوان DefaultSchema کلی یک Context را در متد بازنویسی شده OnModelCreating به نحو فوق تعریف و مدیریت کرد. در این مثال به صورت خودکار کلیه DbSetهای Ctx2 از schema ایی به نام sales استفاده میکنند.
اگر در یک پروژه EF Code first چندین Context وجود داشته باشد و دستور enable-migrations را بدون پارامتری فراخوانی کنیم، پیغام خطای More than one context type was found in the assmbly xyz را دریافت خواهیم کرد.
الف)اما در EF 6 میتوان با بکار بردن سوئیچ جدید ContextTypeName، به ازای هر Context، مهاجرت مرتبط با آنرا تنظیم نمود:
enable-migrations -ContextTypeName dbContextName1 -MigrationDirectory DataContexts\Name1Migrations
ب)در مرحله بعد، نیاز به فراخوانی دستور add-migration است:
add-migration -ConfigurationTypeName FullNameSpaceCtx1.Configuration "InitialCreate"
ذکر کامل فضای نام، از این جهت مهم است که کلاس Configuration به ازای Contextهای مختلف ایجاد شده، یک نام را خواهد داشت؛ اما در فضاهای نام متفاوتی قرار میگیرد.
با اجرای دستور add-migration، کدهای سی شارپ مورد نیاز جهت اعمال تغییرات بر روی ساختار بانک اطلاعاتی تولید میشوند. در مرحله بعد، این کدها تبدیل به دستورات SQL متناظری شده و بر روی بانک اطلاعاتی اجرا خواهند شد.
بدیهی است اگر دو Context در برنامه تعریف کرده باشید، دوبار باید دستور enable-migrations و دوبار دستور add-migration را با پارامترهای اشاره کننده به Conetxtهای مدنظر اجرا کرد.
ج)سپس برای اعمال این تغییرات، باید دستور update-database را اجرا کرد.
update-database -ConfigurationTypeName FullNameSpaceCtx1.Configuration
نهایتا اگر به بانک اطلاعاتی مراجعه کنید، تمام جداول و تعاریف را یکجا در همان بانک اطلاعاتی میتوانید مشاهده نمائید.
داشتن چندین Context در برنامه و مدیریت تراکنشها
در EF، هر DbContext معرف یک واحد کار است. یعنی تراکنشها و چندین عمل متوالی مرتبط انجام شده، درون یک DbContext معنا پیدا میکنند. متد SaveChanges نیز بر همین اساس است که کلیه اعمال ردیابی شده در طی یک واحد کار را در طی یک تراکنش به بانک اطلاعاتی اعمال میکند. همچنین مباحثی مانند lazy loading نیز در طی یک Context مفهوم دارند. به علاوه دیگر امکان join نویسی بین دو Context وجود نخواهد داشت. باید اطلاعات را از یکی واکشی و سپس این اطلاعات درون حافظهای را به دیگری ارسال کنید.
یک نکته
میتوان یک DbSet را در چندین Context تعریف کرد. یعنی اگر بحث join نویسی مطرح است، با تکرار تعریف DbSetها اینکار قابل انجام است اما این مساله اساس جداسازی Contextها را نیز زیر سؤال میبرد.
داشتن چندین Context در برنامه و مدیریت رشتههای اتصالی
در EF Code first روشهای مختلفی برای تعریف رشته اتصالی به بانک اطلاعاتی وجود دارند. اگر تغییر خاصی در کلاس مشتق شده از DbContext ایجاد نکنیم، نام کلید رشته اتصالی تعریف شده در فایل کانفیگ باید به نام کامل کلاس Context برنامه اشاره کند. اما با داشتن چندین Context به ازای یک دیتابیس میتوان از روش ذیل استفاده کرد:
public class Ctx1 : DbContext { public Ctx1() : base("DefaultConnection") { //Database.Log = sql => Debug.Write(sql); } } public class Ctx2 : DbContext { public Ctx2() : base("DefaultConnection") { //Database.Log = sql => Debug.Write(sql); } }
<connectionStrings><add name="DefaultConnection" connectionString="…." providerName="System.Data.SqlClient" /></connectionStrings>
چه زمانی بهتر است از چندین Context در برنامه استفاده کرد؟
عموما در طراحیهای سازمانی SQL Server، تمام جداول از schema مدیریتی به نام dbo استفاده نمیکنند. جداول فروش از schema خاص خود و جداول کاربران از schema دیگری استفاده خواهند کرد. با استفاده از چندین Context میتوان به ازای هر کدام از schemaهای متفاوت موجود، «یک ناحیه ایزوله» را ایجاد و مدیریت کرد.
public class Ctx2 : DbContext { public Ctx2() : base("DefaultConnection") { //Database.Log = sql => Debug.Write(sql); } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.HasDefaultSchema("sales"); base.OnModelCreating(modelBuilder); } }