SQL Code First DB - создавать таблицы, а не удалять / создавать - PullRequest
4 голосов
/ 13 сентября 2011

Я пытаюсь развернуть развертывание MVC 3 с нуля в рамках пробной версии Azure, но, похоже, ударился о стену, заставив базу данных EF4.1 создать себя.

При запуске он будетподключиться успешно, но выдать ошибку:

Invalid object name 'dbo.TableName'.

Я считаю, что это потому, что БД существует, но в ней нет таблиц.Я смог воспроизвести его локально - восстановление базы данных работает нормально, если я удаляю свою базу данных SQL Express, но не тогда, когда я удаляю все таблицы и покидаю базу данных.

Я прочитал о методахэто может быть добавлено к Global.asax:

protected void Application_Start()
{
    // Use any of:
    System.Data.Entity.Database.SetInitializer<MyDatabaseContext>(new DropCreateDatabaseIfModelChanges<MyDatabaseContext>());
    System.Data.Entity.Database.SetInitializer<MyDatabaseContext>(new CreateDatabaseIfNotExists<MyDatabaseContext>());
    System.Data.Entity.Database.SetInitializer<MyDatabaseContext>(new DropCreateDatabaseAlways<MyDatabaseContext>());
}

... которые, похоже, не работают.Некоторые выдают ошибки о том, что не собираются получить хеш модели в таблице EdmMetadata.

Как получить EF4.1 / Code First для создания структуры БД в уже существующей БД?(И в Лазурном ...).Или мне нужно написать сценарий для БД из SQL Management studio и запустить его для целевой БД?

Ответы [ 3 ]

2 голосов
/ 11 мая 2013

проверьте блог David.Cline (Development) на предмет «Перетаскивание и создание таблиц при изменении модели»:

http://www.davidcline.info/2012/05/technologies-c-v4.html

, и эта ссылка может быть полезной:

http://blogs.microsoft.co.il/blogs/gilf/archive/2011/05/30/creating-a-code-first-database-initializer-strategy.aspx

Спасибо, Хачатур

1 голос
/ 27 ноября 2011

Вам нужно создать другой инициализатор базы данных, который будет просто очищать базу данных вместо воссоздания.

Вот оно

using System.Collections.ObjectModel;
using System.Data.Entity;
using System.Data.Entity.Design;
using System.Data.Entity.Infrastructure;
using System.Data.Metadata.Edm;
using System.Data.Objects;
using System.Globalization; 
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Xml;

namespace Infrastructure
{  
public partial class RecreateSchemaIfModelChanges<T> : IDatabaseInitializer<T> where T : DbContext
{
    private EdmMetadata _edmMetaData; 

    private bool CompatibleWithModel(string modelHash, DbContext context, ObjectContext objectContext)
    {
        if (objectContext.ExecuteStoreQuery<int>("Select COUNT(*) \r\n              FROM INFORMATION_SCHEMA.TABLES T \r\n              Where T.TABLE_NAME = 'EdmMetaData'", new object[0]).FirstOrDefault<int>() == 1)
        {
            this._edmMetaData = context.Set<EdmMetadata>().FirstOrDefault<EdmMetadata>();
            if (this._edmMetaData != null)
            {
                return (modelHash == this._edmMetaData.ModelHash);
            }
        }
        return false;
    }

    private static string ComputeSha256Hash(string input)
    {
        byte[] buffer = new SHA256Managed().ComputeHash(Encoding.ASCII.GetBytes(input));
        StringBuilder builder = new StringBuilder(buffer.Length * 2);
        foreach (byte num in buffer)
        {
            builder.Append(num.ToString("X2", CultureInfo.InvariantCulture));
        }
        return builder.ToString();
    }

    private void CreateTables(ObjectContext objectContext)
    {
        string commandText = objectContext.CreateDatabaseScript();
        objectContext.ExecuteStoreCommand(commandText, new object[0]);
    }

    private string GetCsdlXmlString(ObjectContext context)
    {
        if (context != null)
        {
            ReadOnlyCollection<EntityContainer> items = context.MetadataWorkspace.GetItems<EntityContainer>(DataSpace.SSpace);
            if (items != null)
            {
                EntityModelSchemaGenerator generator = new EntityModelSchemaGenerator(items.FirstOrDefault<EntityContainer>());
                StringBuilder output = new StringBuilder();
                XmlWriter writer = XmlWriter.Create(output);
                generator.GenerateMetadata();
                generator.WriteModelSchema(writer);
                writer.Flush();
                return output.ToString();
            }
        }
        return string.Empty;
    }

    private string GetModelHash(ObjectContext context)
    {
        return ComputeSha256Hash(this.GetCsdlXmlString(context));
    }

    public void InitializeDatabase(T context)
    {
        ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext;
        string modelHash = this.GetModelHash(objectContext);
        if (!this.CompatibleWithModel(modelHash, context, objectContext))
        {
            this.DeleteExistingTables(objectContext);
            this.CreateTables(objectContext);
            this.SaveModelHashToDatabase(context, modelHash, objectContext);
            this.Seed(context);
        }
    }

    private void SaveModelHashToDatabase(T context, string modelHash, ObjectContext objectContext)
    {
        if (this._edmMetaData != null)
        {
            objectContext.Detach(this._edmMetaData);
        }
        this._edmMetaData = new EdmMetadata();
        context.Set<EdmMetadata>().Add(this._edmMetaData);
        this._edmMetaData.ModelHash = modelHash;
        context.SaveChanges();
    }

    private void DeleteExistingTables(ObjectContext objectContext)
    {
        var dropConstraintsScript =
            @"declare @cmd varchar(4000)
declare cmds cursor for 
           select  'ALTER TABLE ' + so.TABLE_NAME + ' DROP CONSTRAINT ' +         
so.constraint_name  from INFORMATION_SCHEMA.TABLE_CONSTRAINTS so order by  
so.CONSTRAINT_TYPE
open cmds
while 1=1
   begin
      fetch cmds into @cmd   
          if @@fetch_status != 0 break
                           print @cmd
                           exec(@cmd)
   end
close cmds
            deallocate cmds";
        string dropTablesScript =
            @"declare @cmd varchar(4000)
declare cmds cursor for 
           Select 'drop table [' + Table_Name + ']' From INFORMATION_SCHEMA.TABLES
open cmds
while 1=1
   begin
      fetch cmds into @cmd   
          if @@fetch_status != 0 break
                           print @cmd
                           exec(@cmd)
   end
close cmds
            deallocate cmds";
        objectContext.ExecuteStoreCommand(dropConstraintsScript);
        objectContext.ExecuteStoreCommand(dropTablesScript);
    }

}
}
0 голосов
/ 27 июня 2015

Был построен для EF6, но все еще хороший DbInitializer для этой проблемы:

using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using System.Data.Entity.Migrations;

namespace MyProject.Models
{
public class MrSaleDbInitializer :
    //System.Data.Entity.DropCreateDatabaseIfModelChanges<MrSaleDbContext>
    System.Data.Entity.DropCreateDatabaseAlways<MrSaleDbContext>
{

    /*
     * Seed: Don't delete the current db, don't delete any tables, but clear all rows data in current 
     * db-tables, then seed the db with a new initial data.
     * note: Won't clear any Migration like tables or any AspNet tables such as: __MigrationHistory, AspNetUsers, AspNetRoles.
     */
    protected override void Seed(MrSaleDbContext context)        
    {
        this.ClearDb(context);
        this.SeedAfterClearingDb(context);
        base.Seed(context);         
    }


    private void ClearDb(MrSaleDbContext context)
    {
        //Optional: disable all foreign keys (db-schema will be loosed).
        //context.Database.ExecuteSqlCommand("EXEC sp_MSforeachtable @command1 = 'ALTER TABLE ? NOCHECK CONSTRAINT all'");

        List<string> tableNames = context.Database.SqlQuery<string>("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME NOT LIKE '%Migration%' AND TABLE_NAME NOT LIKE 'AspNet%'").ToList();

        for (int i = 0; tableNames.Count > 0; i++)
        {
            try
            {
                //To delete all tables and not just clean them from data, replace "DELETE FROM {0}" in "DROP TABLE {0}":
                context.Database.ExecuteSqlCommand(string.Format("DELETE FROM {0}", tableNames.ElementAt(i % tableNames.Count)));

                tableNames.RemoveAt(i % tableNames.Count);
                i = -1; //flag: a table was removed. in the next iteration i++ will be the 0 index.
            }
            catch (System.Data.SqlClient.SqlException e)   // ignore errors as these are expected due to linked foreign key data    
            {
                //throw new Exception("Unable to clear any relevant table in data-base (due to foriegn key constraint ?). See inner-exception for more details.", e);
                if ((i % tableNames.Count) == (tableNames.Count - 1))
                {
                    //end of tables-list without any success to delete any table, then exit with exception:
                    throw new System.Data.DataException("Unable to clear all relevant tables in database (foriegn key constraint ?). See inner-exception for more details.", e);
                }

            }

        }

        context.SaveChanges();
    }


    /*
     * SeedAfterClearingDb: seed the data-base with initial date after database was created. 
     */
    public void SeedAfterClearingDb(MrSaleDbContext context)
    {
        //seed the data-base with initial date after database was created. 

        //then...

        //update all posts and save changes:

        context.SaveChanges();

    }
}

}

...