عنوان:

‫وادار کردن EF Code first به ساخت بانک اطلاعاتی پیش از شروع به کار برنامه


نویسنده: وحید نصیری
تاریخ: ۱۳۹۱/۰۷/۱۰ ۹:۴۹
آدرس: www.dntips.ir
یکی از مواردی که در EF Code First سبب سردرگمی تازه‌کاران می‌شود (بارها در کامنت‌های سایت مطرح شده)، فراخوانی متد Database.SetInitializer و ... عدم تشکیل بانک اطلاعاتی در این لحظه است. تنها کاری که توسط متد Database.SetInitializer صورت می‌گیرد، مشخص سازی استراتژی نحوه آغاز بانک اطلاعاتی است و نه اجرای آن استراتژی.
این اجرا تا زمانیکه اولین کوئری به بانک اطلاعاتی ارسال نشود مثلا فراخوانی context.Entity.Find، به تعویق خواهد افتاد. به همین جهت برای وادار کردن EF به ساخت بانک اطلاعاتی و یا اعمال تغییرات جدید به آن، می‌توان از نکته زیر استفاده کرد:
protected void Application_Start() {
     //...
     Database.SetInitializer(...همانند سابق...);
     using (var context = new MyContext()) {
          context.Database.Initialize(force: true);
     } 
     //...
}
در اینجا در روال آغاز برنامه و پیش از اینکه رابط کاربری نمایان شود، توسط متد context.Database.Initialize، سبب اجرای اجباری استراتژی آغاز بانک اطلاعاتی خواهیم شد.
 

نظرات

  • مهمان در ۱۳۹۱/۰۷/۱۰ ۱۰:۱۸
    نکته مفیدی بود . سپاس
  • فرهاد یزدان پناه در ۱۳۹۱/۰۷/۱۰ ۱۲:۹
    خیلی مفیده. ممنون.
    در ضمن میشه توی سازنده استاتیک کلاس هم این موارد رو قرار داد تا فورا بعد از شروع برنامه همه کارها انجام شود.
  • فرشید علی اکبری در ۱۳۹۲/۰۲/۱۰ ۱۷:۵۷
    با سلام
    موقعی که برنامه شروع شده و سراغ کانکست میره که اونو چک کنه و دیتابیس رو بسازه این پیغام میده :
    The target context 'Common_Infrastructure.ContextCentralSystem' is not constructible. Add a default constructor or provide an implementation of IDbContextFactory. 
    با وجودیکه من از این دوحالت استفاده کردم : 
     public ContextCentralSystem(DbConnection db)
                : base(db, true)
            {
    
            }
            public ContextCentralSystem(string connectionnamestring)
                : base(nameOrConnectionString: connectionnamestring)
            {
               
            }
    و میخوام طبق مشخصاتی که سیستم ازکاربرگرفته کانکت کرده و دیتابیسم رو با نامه مثلا XX بسازه ولی این پیغام میگه حتما باید یک ctor پیش فرض پیاده کنی ... که با این وجود رشته‌ی منو قبول نمی‌کنه... اگه لطف کنین ممنون میشم ..... درضمن از فایل app.config هم نمی‌خوام استفاده کنم و برنامه دسکتاپ هستش.
    تشکر.
    • وحید نصیری در ۱۳۹۲/۰۲/۱۰ ۱۸:۵۶
      یکی از روش‌های تعریف رشته اتصالی است:
      public class CustomContext : DbContext
      {
           public CustomContext() :  base("AppConfigConnectionStringName") { }
      
      // or
      public class CustomContext : DbContext
      {
          public CustomContext() :
                  base(@"Data Source=(local);Initial Catalog=MyDBName;Integrated Security=True;Pooling=False") { }
      روش دیگر :
      var ctx = new MyContext();
      ctx.Database.Connection.ConnectionString = "...";
      و یا
      Database.DefaultConnectionFactory = 
      new SqlConnectionFactory(@"Data Source=(local);Initial Catalog=MyDBName;Integrated Security=True;Pooling=False");
      و ...
  • سانای رحیمی در ۱۳۹۳/۰۵/۲۲ ۹:۲۸
    سلام
    من از الگوی UoW استفاده می‌کنم. حالا اومدم داخل Context خودم یک متد InitializeDatabase نوشتم که دستورات بالا رو داخلش قرار دادم. ولی زمانی که فراخوانی می‌کنم هیچ اتفاقی نمی‌افته. برنامه خطا نمیده ولی اجرا که می‌کنم خود به خود بسته میشه، بدون اینکه خطایی بده.
    • وحید نصیری در ۱۳۹۳/۰۵/۲۲ ۹:۴۷
      - این کدها دقیقا به همین شکلی که در اینجا مشخص شده باید اجرا شوند. ارتباطی با مباحث UoW ندارند. راسا و مستقلا توسط EF مدیریت می‌شوند و در اصل از یک Context درونی مخصوص این کار استفاده می‌کنند که با Context اصلی برنامه یکی نیست. نامش HistoryContext است.
      - اگر برنامه‌ای خودبخود بسته می‌شود، ابتدا به Event viewer توکار ویندوز مراجعه کنید. به احتمال زیاد لاگ خطای آن در آنجا قابل مشاهده است. همچنین خطاهای مدیریت نشده را هم باید بررسی کنید.
  • مهدی ملائیان در ۱۳۹۵/۰۷/۲۸ ۱۱:۳۴
     با اجرای کد فوق متد Seed بصورت خودکار فراخوانی خواهد شد ؟ البته من در کلاس سفارشی  MyInitilizer  استراتژی مقدماتی تشکیل بانک اطلاعاتی را تنظیم می‌کنم :
     public class MyInitilizer : IDatabaseInitializer<Context>
    {
      public void InitializeDatabase(Context context)
      {
    Database.SetInitializer(new MigrateDatabaseToLatestVersion<Context, Migrations.Configuration>());
      }
    }
    ولی باز هم با اجرای دستورات به شکل معرفی شده در این مطلب بانک اطلاعاتی من ایجاد نمیشه.البته با دستورات nuget براحتی بانک ایجاد میشه و خطایی صادر نمیشه.(کلیه دستورات نوشته شده در Seed اعمال می‌شود)
    • وحید نصیری در ۱۳۹۵/۰۷/۲۸ ۱۱:۵۰
      بله. آیا کلاس Configuration حاوی متد Seed هست به همراه کدهای مقدار دهی اولیه بانک اطلاعاتی؟
      • مهدی ملائیان در ۱۳۹۵/۰۷/۲۸ ۱۲:۳
        بله .بخشی از کد‌های متد Seed :
         context.Groups.Add(new Group
                    {
                        Name = "مدیران",
                    });
                    context.SaveChanges();
                    base.Seed(context);
        و کلاس  201610180732155_initial  با متد‌های Up  و Down  هم در پروژه موجود هست
        • مهدی ملائیان در ۱۳۹۵/۰۷/۲۸ ۱۲:۷
          البته قبل از آغاز برنامه در ابتدا من تنظیمات کانکشن استرینگ مربوط به بانک اطلاعاتی رو تنظیم می‌کنم .(کاربر رشته اتصالی به بانک اطلاعاتی رو تنظیم می‌کنه و کانکشن موجود می‌باشد)
        • وحید نصیری در ۱۳۹۵/۰۷/۲۸ ۱۲:۳۰
          - مطلب جاری برای حالت «AutomaticMigrationsEnabled = true» است که در آن نیازی به اعمال دستی مهاجرت‌ها نیست (در این حالت متد Up و Down ایی وجود ندارد) و همه چیز پس از آن توسط MigrateDatabaseToLatestVersion خودکار است. اگر از مهاجرت‌های «دستی» استفاده می‌کنید، نیازی به این مطلب ندارید. هر زمانیکه دستور Update-Database اجرا می‌شود (یعنی حالت مهاجرت «دستی»)، اجرای متد Seed هم جزئی از آن است.
          - اگر رشته‌ی اتصالی به صورت دستی تنظیم می‌شود، MigrateDatabaseToLatestVersion نیاز به اصلاح دارد: « استفاده از چندین بانک اطلاعاتی به صورت همزمان در EF Code First »