4

I'm working on a simple desktop app that uses SQL Server Express/LocalDB. I've a data directory in an arbitrary nonpriviledged location and in there I want to create a database file. I initially created the database and generated a model for EF to use; now I want to use that model to recreate the database wherever I want it.

I've found a variety of posts doing similar, but they seem to be deleting and recreating an existing database via a context that's working to start with, for testing purposes. I'm wanting to start from an empty directory.

Using extracts from the code in here I'm able to physically create a database file on the disk, using the SQL statements to create the new .mdf and .ldf files. But they don't have the schema; if I start a context instance off from the .mdf file and then try counting the number of rows in a table, I get an exception thrown because the table doesn't exist.

If I try calling ctx.Database.Create() then I get the error that the database can't be created because it already exists. Which of course it does, just with no tables.

If I don't use the raw SQL queries initially to create the new, empty, database and I try to create the context as follows, with filespec pointing to a non-existent .mdf file in a valid directory, the .Create() always throws an exception "Database '' cannot be created because it already exists"

string connectionString
        = "Data Source=(LocalDB)\\v11.0;AttachDbFilename="
        + fileSpec;

EventsListDBEntities ctx = new EventsListDBEntities();
ctx.Database.Connection.ConnectionString = connectionString;
ctx.Database.Create();
ctx.Database.Initialize(true);

How can I get EF to create the tables in my empty DB, or to create the files from scratch?

2 Answers 2

1

Try using the following instead:

string connectionString
        = "Data Source=(LocalDB)\\v11.0;AttachDbFilename="
        + fileSpec;

EventsListDBEntities ctx = new EventsListDBEntities();
ctx.Database.Connection.ConnectionString = connectionString;
ctx.Database.CreateIfNotExists(); // Change this line.
ctx.Database.Initialize(true);

https://msdn.microsoft.com/en-us/library/system.data.entity.database.createifnotexists%28v=vs.113%29.aspx#M:System.Data.Entity.Database.CreateIfNotExists

Sign up to request clarification or add additional context in comments.

1 Comment

Ta, but tried that earlier- since in both cases it considers the DB to exist, CreateIfNotExists() does nothing, returns false and although the exception's not thrown there, as soon as I try to count the number of rows I get either a "Underlying provider failed on Open" in the second case, because no file's there, and a "Object doesn't exist" or similar in the first case, because the DB's opened but there's no table.
1

After lots of experimentation, here's the code I've ended up with that does the job.

string connectionString
      = "Data Source=(LocalDB)\\v11.0;AttachDbFilename="
      + fileSpec + ";database=EventsListDB";

/* We can't go straight into the context and create the DB because 
 * it needs a connection to "master" and can't create it. Although this
 * looks completely unrelated, under the hood it leaves behind something
 * that EF can pick up and use- and it can't hurt to delete any references 
 * to databases of the same name that may be lurking in other previously
 * used directories.
 */

SqlConnectionStringBuilder masterCSB = new SqlConnectionStringBuilder(connectionString);
masterCSB.InitialCatalog = "master";
masterCSB.AttachDBFilename = "";

using (var sqlConn = new SqlConnection(masterCSB.ToString()))
{
    sqlConn.Open();
    using (var cmd = sqlConn.CreateCommand())
    {
        bool done = false;
        int attempt = 0;
        do
        {
            try
            {
                cmd.CommandText =
                    String.Format(
                        "IF EXISTS (Select name from sys.databases " + 
                        "WHERE name = '{0}') " +
                        "DROP DATABASE {0}", "EventsListDB");
                cmd.ExecuteNonQuery();
                done = true;
            }
            catch (System.Exception ex)
            {
                /* We sometimes get odd exceptions that're probably because LocalDB hasn't finished starting. */
                if (attempt++ > 5)
                {
                    throw ex;
                }
                else Thread.Sleep(100);

            }
        } while (!done);
    }
}

/* Now we can create the context and use that to create the DB. Note that
 * a custom constructor's been added to the context exposing the base
 * constructor that can take a connection string- changing the connection
 * string after the default constructor reads it from App.config isn't 
 * sufficient.
 */
EventsListDBEntities ctx = new EventsListDBEntities(connectionString);
ctx.Database.Create();
int numRecords = ctx.EventLists.Count(); //See if it really worked.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.