Entity Framework CTP 4 - Code First Custom Database Initializer

后端 未结 6 656
傲寒
傲寒 2020-11-28 23:53

I would like to implement a custom database initialization strategy so that I can generate the database schema and apply it to an EXISTING EMPTY SQL database using a supplie

6条回答
  •  猫巷女王i
    2020-11-29 00:30

    I took a slightly different approach to this problem. This seems like as good a place as any to share the results.

    I want to only create tables that don't already exist in the database. This has the benefit of being able to roll out new tables without erasing the rest of the database.

    This also helps if you have multiple data contexts in an inheritance chain. For example, if you split your application into different assemblies. You might have a data context in a "core" module, and then inherit it in a different assembly for add-on modules. This configuration works fine, but the built-in Drop/Create initializers don't like it because the model hash is changing all the time. By checking table existance, initialization takes slightly longer, but then you have none of these issues.

    Anyway, here's the code:

    /// 
    /// Database Initializer to create tables only if they don't already exist.
    /// It will never drop the database.  Does not check the model for compatibility.
    /// 
    /// The data context
    public class CreateTablesOnlyIfTheyDontExist : IDatabaseInitializer
      where TContext : DataContext
    {
      public void InitializeDatabase(TContext context)
      {
        using (new TransactionScope(TransactionScopeOption.Suppress))
        {
          // If the database doesn't exist at all then just create it like normal.
          if (!context.Database.Exists())
          {
            context.Database.Create();
            return;
          }
    
          // get the object context
          var objectContext = ((IObjectContextAdapter)context).ObjectContext;
    
          // get the database creation script
          var script = objectContext.CreateDatabaseScript();
    
    
          if (context.Database.Connection is SqlConnection)
          {
            // for SQL Server, we'll just alter the script
    
            // add existance checks to the table creation statements
            script = Regex.Replace(script,
              @"create table \[(\w+)\]\.\[(\w+)\]",
              "if not exists (select * from INFORMATION_SCHEMA.TABLES " +
              "where TABLE_SCHEMA='$1' and TABLE_NAME = '$2')\n$&");
    
            // add existance checks to the table constraint creation statements
            script = Regex.Replace(script,
              @"alter table \[(\w+)\]\.\[(\w+)\] add constraint \[(\w+)\]",
              "if not exists (select * from INFORMATION_SCHEMA.TABLE_CONSTRAINTS " +
              "where TABLE_SCHEMA='$1' and TABLE_NAME = '$2' " +
              "and CONSTRAINT_NAME = '$3')\n$&");
    
            // run the modified script
            objectContext.ExecuteStoreCommand(script);
          }
          else if (context.Database.Connection is SqlCeConnection)
          {
            // SQL CE doesn't let you use inline existance checks,
            // so we have to parse each statement out and check separately.
    
            var statements = script.Split(new[] { ";\r\n" },
                            StringSplitOptions.RemoveEmptyEntries);
            foreach (var statement in statements)
            {
              var quoteSplitStrings = statement.Split('"');
              if (statement.StartsWith("CREATE TABLE"))
              {
                // Create a table if it does not exist.
                var tableName = quoteSplitStrings[1];
                const string sql = 
                  "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES " +
                  "WHERE TABLE_NAME='{0}'"
                var checkScript = string.Format(sql, tableName);
                if (objectContext.ExecuteStoreQuery(checkScript).First() == 0)
                  objectContext.ExecuteStoreCommand(statement);
              }
              else if (statement.Contains("ADD CONSTRAINT"))
              {
                // Add a table constraint if it does not exist.
                var tableName = quoteSplitStrings[1];
                var constraintName = quoteSplitStrings[3];
                const string sql = 
                  "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS " +
                  "WHERE TABLE_NAME='{0}' AND CONSTRAINT_NAME='{1}'";
                var checkScript = string.Format(sql, tableName, constraintName);
                if (objectContext.ExecuteStoreQuery(checkScript).First() == 0)
                  objectContext.ExecuteStoreCommand(statement);
              }
              else
              {
                // Not sure what else it could be. Just run it.
                objectContext.ExecuteStoreCommand(statement);
              }
            }
          }
          else
          {
            throw new InvalidOperationException(
              "This initializer is only compatible with SQL Server or SQL Compact Edition"
              );
          }
        }
      }
    }
    

提交回复
热议问题