Entity Framework 5 code-first not creating databas

2019-02-02 06:14发布

问题:

I'm trying to create a new database using the code first concept of Entity Framework. However when running the code the database isn't created (using the DropCreateDatabaseIfModelChanges setting), though the code is running fine. I'm seeing the following exception when I try to get something from the database.

My project is setup using a separate DataAccess layer with an generic service and repository construction. So all my entities, repository and also database context are in a separate project within the solution.

My global.asax file contains the following piece of code.

Database.SetInitializer(new DropCreateDatabaseIfModelChanges<MyContext>());

This should initialise a new database if it isn't there, right?

My database context class looks like this;

namespace Website.DAL.Model
{
    public class MyContext : DbContext
    {
        public IDbSet<Project> Projects { get; set; }
        public IDbSet<Portfolio> Portfolios { get; set; }

        /// <summary>
        /// The constructor, we provide the connectionstring to be used to it's base class.
        /// </summary>
        public MyContext()
            : base("MyConnectionString")
        {
        }

        static MyContext()
        {
            try
            {
                Database.SetInitializer<MyContext>(new DropCreateDatabaseIfModelChanges<MyContext>());
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// This method prevents the plurarization of table names
        /// </summary>
        /// <param name="modelBuilder"></param>
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Conventions.Remove<System.Data.Entity.ModelConfiguration.Conventions.PluralizingTableNameConvention>();
        }
    }
}

I've created this class following several tutorials and articles on the internet. It's all new to me, but as far as I can see everything seems correct so far. So now the two entities I'm using. They're called 'Project' and 'Portfolio'. They look like this;

public class Portfolio
    {
        [Key]
        public Guid Id { get; set; }
        public String Name { get; set; }
        public DateTime StartDate { get; set; }
        public DateTime? EndDate { get; set; }
        public bool IsPublished { get; set; }

        public virtual ICollection<Project> Projects { get; set; }
    }

And

public class Project 
    {
        [Key]
        public Guid Id { get; set; }
        public DateTime StartDate { get; set; }
        public DateTime? EndDate { get; set; }
        public bool IsPublished { get; set; }
        public String Title { get; set; }
    }

The database I'm using is running on an external server, it came with the hosting provider I'm using. I've got a SQL Server database up and running and the connection string to the database is in the web.config of the website project. I've already tried removing the database and let the code recreate it, which unfortunately didn't work. Am I missing something obvious here? Or could it be a simple thing as access-rights to the server to create databases?

Note: When I run the Database-Update -Script command to generate SQL code, it seems that the correct SQL statements to create all tables are created.

UPDATE 1: Okay, thanks to some comments I came a bit further. I've added two properties to my entities to force some changes and I've also created an custom initializer like this;

public class ForceDeleteInitializer : IDatabaseInitializer<MyContext>
    {
        private readonly IDatabaseInitializer<MyContext> _initializer = new DropCreateDatabaseIfModelChanges<MyContext>();

        public ForceDeleteInitializer()
        {
            //_initializer = new ForceDeleteInitializer();
        }

        public void InitializeDatabase(MyContext context)
        {
            //This command is added to prevent open connections. See http://stackoverflow.com/questions/5288996/database-in-use-error-with-entity-framework-4-code-first
            context.Database.ExecuteSqlCommand("ALTER DATABASE borloOntwikkel SET SINGLE_USER WITH ROLLBACK IMMEDIATE");
            _initializer.InitializeDatabase(context);
        }
    }

I've also removed the initializer from the constructor of my context, so this mean i've remove this line of code;

Database.SetInitializer<MyContext>(new DropCreateDatabaseIfModelChanges<MyContext>());

After that i've added these three lines to my Global.asax file;

Database.SetInitializer(new ForceDeleteInitializer());
MyContext c = new MyContext();
c.Database.Initialize(true);

When debugging i'm now getting this exception;

This gives me the following information:

  • InnerException says: The provider didn't return a ProviderManifestToken
  • InnerException in the InnerException says: "For this operation an connection to the masterdatabase is required. A connection can't be made width the 'master'-database because the original connection is opened and the references has been removed from the connection. Please provide a non-open connection"

After these action the database is inaccessible, so most likely deleted..

What can possibly be done about this? It's most likely that I can't access the master database because my hostingprovider won't give me the proper access right ofcourse.

回答1:

Since no other solution came by I decided to change my approach.

I've first created the database myself and made sure the correct SQL user was configured and I had access.

Then I removed the initializer and the code from the Global.asax file. After that I ran the following command in the Package Manager Console (since the layered design I had to select the correct project in the console);

Enable-Migrations

After the migrations where enabled and I made some last minute changes to my entities I ran the command below to scaffold an new migration;

Add-Migration AddSortOrder

After my migrations were created I ran the following command in the console and voila, the database was updated with my entities;

Update-Database -Verbose

To be able to seed the database when running the migration i've overridden the Seed method in my Configuraton.cs class, which was created when enabling the migrations. The final code in this method is like this;

protected override void Seed(MyContext context)
        {
            //  This method will be called after migrating to the latest version.

            //Add menu items and pages
            if (!context.Menu.Any() && !context.Page.Any())
            {
                context.Menu.AddOrUpdate(new Menu()
                                             {
                                                 Id = Guid.NewGuid(),
                                                 Name = "MainMenu",
                                                 Description = "Some menu",
                                                 IsDeleted = false,
                                                 IsPublished = true,
                                                 PublishStart = DateTime.Now,
                                                 LastModified = DateTime.Now,
                                                 PublishEnd = null,
                                                 MenuItems = new List<MenuItem>()
                                                                 {
                                                                     new MenuItem()
                                                                         {
                                                                             Id = Guid.NewGuid(),
                                                                             IsDeleted = false,
                                                                             IsPublished = true,
                                                                             PublishStart = DateTime.Now,
                                                                             LastModified = DateTime.Now,
                                                                             PublishEnd = null,
                                                                             Name = "Some menuitem",
                                                                             Page = new Page()
                                                                                        {
                                                                                            Id = Guid.NewGuid(),
                                                                                            ActionName = "Some Action",
                                                                                            ControllerName = "SomeController",
                                                                                            IsPublished = true,
                                                                                            IsDeleted = false,
                                                                                            PublishStart = DateTime.Now,
                                                                                            LastModified = DateTime.Now,
                                                                                            PublishEnd = null,
                                                                                            Title = "Some Page"
                                                                                        }
                                                                         },
                                                                     new MenuItem()
                                                                         {
                                                                             Id = Guid.NewGuid(),
                                                                             IsDeleted = false,
                                                                             IsPublished = true,
                                                                             PublishStart = DateTime.Now,
                                                                             LastModified = DateTime.Now,
                                                                             PublishEnd = null,
                                                                             Name = "Some MenuItem",
                                                                             Page = new Page()
                                                                                        {
                                                                                            Id = Guid.NewGuid(),
                                                                                            ActionName = "Some Action",
                                                                                            ControllerName = "SomeController",
                                                                                            IsPublished = true,
                                                                                            IsDeleted = false,
                                                                                            PublishStart = DateTime.Now,
                                                                                            LastModified = DateTime.Now,
                                                                                            PublishEnd = null,
                                                                                            Title = "Some Page"
                                                                                        }
                                                                         }
                                                                 }
                                             });
            }

            if (!context.ComponentType.Any())
            {
                context.ComponentType.AddOrUpdate(new ComponentType()
                {
                    Id = Guid.NewGuid(),
                    IsDeleted = false,
                    IsPublished = true,
                    LastModified = DateTime.Now,
                    Name = "MyComponent",
                    PublishEnd = null,
                    PublishStart = DateTime.Now
                });
            }


            try
            {
                // Your code...
                // Could also be before try if you know the exception occurs in SaveChanges

                context.SaveChanges();
            }
            catch (DbEntityValidationException e)
            {
                //foreach (var eve in e.EntityValidationErrors)
                //{
                //    Console.WriteLine("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:",
                //        eve.Entry.Entity.GetType().Name, eve.Entry.State);
                //    foreach (var ve in eve.ValidationErrors)
                //    {
                //        Console.WriteLine("- Property: \"{0}\", Error: \"{1}\"",
                //            ve.PropertyName, ve.ErrorMessage);
                //    }
                //}
                //throw;

                var outputLines = new List<string>();
                foreach (var eve in e.EntityValidationErrors)
                {
                    outputLines.Add(string.Format(
                        "{0}: Entity of type \"{1}\" in state \"{2}\" has the following validation errors:",
                        DateTime.Now, eve.Entry.Entity.GetType().Name, eve.Entry.State));
                    foreach (var ve in eve.ValidationErrors)
                    {
                        outputLines.Add(string.Format(
                            "- Property: \"{0}\", Error: \"{1}\"",
                            ve.PropertyName, ve.ErrorMessage));
                    }
                }
                System.IO.File.AppendAllLines(@"c:\temp\errors.txt", outputLines);
                throw;
            }
        }

The disadvantage at the moment is that I have to manually migrate with (only) 2 commands in the package manager console. But the same time, the fact that this doesn't happen dynamically is also good because this prevents possibly inwanted changes to my database. Further everything works just perfect.



回答2:

+1 for the detailed question.

Check that your connection string is pointing at the correct database and add the authorization attributes like this to access your database:

<add name="PatientContext" providerName="System.Data.SqlClient" connectionString="Server=SQLSERVER2; Database=Patients; uid=PatientUser; password=123456; Integrated Security=False;" />


回答3:

befor intialize it run following code to create your database:

context.Database.CreateIfNotExists();



回答4:

So First Remove the Db Name from the constructor parameter on context class and provide Db_name on the connection string and then try to rebuild your solution and run the application it will create Database for you application.

For Example :

I am not passing the Db-Name on constructor parameter

public EmployeeContext()
            : base()
        {
            Database.SetInitializer<EmployeeContext>(new DropCreateDatabaseIfModelChanges<EmployeeContext>());
        }

and on the Connection string I am passing the Db-name like below.

    <add name="EmployeeContext" connectionString="server=.; database=EFCodeFirstTPHInheritance; uid=sa;Password=crius@123;persistsecurityinfo=True" providerName="System.Data.SqlClient"/>
  </connectionStrings>