Search code examples
databaseasp.net-mvcconnection-stringaspnetdb

Merge aspnetdb.mdf with my own database (automatically generated)


I've searched the internet thoroughly but couldn't find a clear answer to the problem. I have got the aspnet.db database. But i want to add my own tables and data to this database. If i try to connect to it with the connection string:

<add name ="ToernooiCompanionDBContext" connectionString ="Data Source= .\SQLEXPRESS; Integrated Security = SSPI; Trusted_Connection=True; Initial Catalog= aspnetdb"  providerName ="System.Data.SqlClient"/>

A new database will be created (aspnetdb.mdf) in C:\Program Files\Microsoft SQL Server\MSSQL10.SQLEXPRESS\MSSQL\DATA.

I want the database (which is automatically generated by codefirst) to merge with the existing one in my APP_DATA folder. What am I doing wrong?

I've tried adding AttachDbFilename=|DataDirectory|aspnetdb.mdf and User Instance=true to my connection string, or using the LocalSqlServer connection string which is defined in machine.config, but in all cases this overwrites the existing database. If I remove Initial Catalog=aspnetdb then I get an error that the initial catalog is needed.


Solution

  • I had the same problem but this link got me on the track to something that worked at least for me. I hope this helps someone at least! :)

    1. Create a database
    2. Add the aspnet tables to the new database
    3. Fix the database connections in web.config so they point to the same database
    4. Write some sql that removes all tables except the ones that start with "aspnet_"
    5. Add the sql to the database initializer you write by your self
    6. Add a call to the database initializer in Global.asax.cs

    1. Create a database

    I usually do this with SQL Server Management Studio. The database I used for this example code is SQL Server 2008R2 but I have done the same with SQL Server Express that you use.

    2. Add the aspnet tables to the new database

    I use the following tool which if you use it without any command line arguments works like a wizard. %windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_regsql.exe

    3. Fix the database connections so they point to the same database

    The following two lines are from the test application I made. Notice that the name of the second connectionstring (MyHealthContext) is identical to the name of the DbContext I am using for my code first classes.

    DbContext:

    public class MyHealthContext : DbContext
    {
        public DbSet<Person> People { get; set; }
        public DbSet<PersonAttribute> PeopleAttributes { get; set; }
    }
    

    Web.config

    <add name="ApplicationServices" connectionString="Server=localhost\mssql2008r2;Database=MyHealth;Integrated Security=True;" providerName="System.Data.SqlClient"/> 
    <add name="MyHealthContext" connectionString="Server=localhost\mssql2008r2;Database=MyHealth;Integrated Security=True;" providerName="System.Data.SqlClient"/>
    

    4. SQL that removes all but the aspnetdb-tables

    DECLARE @cmdDropConstraints VARCHAR(4000)
    DECLARE @cmdDropTables      VARCHAR(4000)
    
    -- ======================================================================
    -- DROP ALL THE FOREIGN KEY CONSTRAINTS FROM THE TABLES WE WANT TO DROP
    -- ======================================================================
    DECLARE cursorDropConstraints CURSOR FOR 
        SELECT 
            'ALTER TABLE ['+ s.name + '].[' + t.name + '] DROP CONSTRAINT [' + f.name +']' 
        FROM 
            sys.foreign_keys f 
            INNER JOIN sys.tables t ON f.parent_object_id=t.object_id 
            INNER JOIN sys.schemas s ON t.schema_id=s.schema_id 
        WHERE 
            t.is_ms_shipped=0
            AND t.name NOT LIKE 'aspnet_%'
            AND t.name <> 'sysdiagrams'
    
    OPEN cursorDropConstraints
    WHILE 1=1
    BEGIN
        FETCH cursorDropConstraints INTO @cmdDropConstraints
        IF @@fetch_status != 0 BREAK
        EXEC(@cmdDropConstraints)
    END
    CLOSE cursorDropConstraints
    DEALLOCATE cursorDropConstraints;
    
    -- ======================================================================
    -- DROP ALL THE RELEVANT TABLES SO THAT THEY CAN BE RECREATED
    -- ======================================================================
    DECLARE cursorDropTables CURSOR FOR 
        SELECT 
            'DROP TABLE [' + Table_Name + ']'
        FROM 
            INFORMATION_SCHEMA.TABLES
        WHERE
            Table_Name NOT LIKE 'aspnet_%'
            AND TABLE_TYPE <> 'VIEW'
            AND TABLE_NAME <> 'sysdiagrams'
    
    OPEN cursorDropTables
    WHILE 1=1
    BEGIN
        FETCH cursorDropTables INTO @cmdDropTables
        IF @@fetch_status != 0 BREAK
        EXEC(@cmdDropTables)
    END
    CLOSE cursorDropTables
    DEALLOCATE cursorDropTables;
    

    5. Code for the database initializer:

    Replace the "SQL CODE GOES HERE" below with the sql from step 4

    public class MyHealthInitializerDropCreateTables : IDatabaseInitializer<MyHealthContext>
    {
        public void InitializeDatabase(MyHealthContext context)
        {
            bool dbExists;
            using (new TransactionScope(TransactionScopeOption.Suppress))
            {
                dbExists = context.Database.Exists();
            }
    
            if (dbExists)
            {
                // Remove all tables which are specific to the MyHealthContext (not the aspnetdb tables)
                context.Database.ExecuteSqlCommand(@"SQL CODE GOES HERE");
    
                // Create all tables which are specific to the MyHealthContext (not the aspnetdb tables)
                var dbCreationScript = ((IObjectContextAdapter)context).ObjectContext.CreateDatabaseScript();
                context.Database.ExecuteSqlCommand(dbCreationScript);
    
                Seed(context);
                context.SaveChanges();
            }
            else
            {
                throw new ApplicationException("No database instance");
            }
        }
    
        protected virtual void Seed(MyHealthContext context)
        {
            //TODO: Add code for seeding your database with some initial data...
        }
    }
    

    6. Code that hooks in your new database initializer

    To make sure that the custom database initializer isn't accidentily run in the production environment i added a #if DEBUG statement since I always compile my code in release mode before publishing.

        protected void Application_Start()
        {
            //TODO: Comment out this database initializer(s) before going into production
            #if DEBUG
            Database.SetInitializer<MyHealthContext>(new MyHealthInitializerDropCreateTables()); // Create new tables in an existing database
            #endif
    
            AreaRegistration.RegisterAllAreas();
    
            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);
        }