Welcome to MSDN Blogs Sign in | Join | Help

Visual Studio Premium and Database Projects

Visual Studio 2010 Beta 2 is available!  For those of you who are MSDN subscribers you can download it today through MSDN subscriber’s page. For everyone else you will have access to it on Wednesday.

 

Visual Studio Database Edition features are now in Visual Studio 2010 Premium.  The Database Project is also available in Visual Studio 2010 Professional, but with limited features.  Database Projects are provided in PRO to introduce developers to offline database development and to allow the building and deploying of projects in a mixed Visual Studio Team environment.  

 

I invite you to try out Visual Studio Beta 2 and evaluate the improvements for Database Projects.  In this release you will find the follow:

 

·         Performance Improvements – Database Projects are quicker to load and build.

·         T-SQL Editor Intellisense – Enjoy Intellisense in the new T-SQL editor for both online database connections and offline database projects.

·         Deployment Options – New deployment options to increase the success of automated deployments.

·         Team Build – Improved Team Build integration.  Building X86 and x64 are supported.  Database projects are supported for headless build servers (No local VS instance required)

·         Unit Test - Expected Schema and Data Checksum Unit Test conditions are included.

·         Data Generation – Database Transform Plan is included. Easily create a databound data generation plan and customize as necessary.

·         Build and Deploy Extensibility – Produce new build and deployment artifacts. Customize the deployment plan.

·         Project Extensibility – Add your own custom features to the offline database development environment.

·         And many other improvements.

 

In the coming weeks I plan on blogging about some of these new features.  In the meantime let me know what you like or don’t like about Beta 2.

Posted by bahill | 0 Comments
Filed under:

Using local 3-part names in programmability objects

VSTS:DB does not support self-referencing 3 part names. It does support external 3 and 4 part references.  For folks wondering what the difference is it is basically the use of fully qualified 3 part names identifiers for all objects in your programmability objects including local objects.  For instance you would refer to a table that is in the current database from a stored procedure using the 3-part name like so:  [Local Database Name].[Schema Name].[Object Name]. 

 

While VSTS:DB does not support local 3 part names it does support the use of variables and literals to resolve references to external databases.  The $(DatabaseName) variable is an ambient variable that will have its value replaced at the time of deployment. This variable gets its value from the project properties deployment tab.  Since $(DatabaseName) is always replaced at deployment with the target database name and references through variables are resolved you may use a variable in your local 3-part names.

 

Our guidance is to not use local 3-part names as it introduces an unnecessary layer of abstraction and dependency on the database name. For teams that use local 3 part names and would like to continue using local 3-part names can use the $(DatabaseName) variable to resolve local references.  This would make your object references look like this: [$(Database Name)].[Schema Name].[Object Name].

 

To update all the local 3-part names to use a variable can be done through refactoring.  To refactor all of your 3 part names to use the variable you do the following:

 

1.       Right click on your project and select Refactor -> Rename Server/Database References

2.       On the window, uncheck the box for “Replace the name of a server or server variable”

3.       Check the box for "Replace the name of Database or a database variable”

4.       Enter in the name of your database in the Old name field:  ex “ThreePart”

5.       Enter $(DatabaseName) for the New Name

6.       Click OK

7.       Click Apply on the preview window

 

Refactoring will update all the local 3 part names to use the variable $(DatabaseName) for the Database segment of the 3 part name reference.  Your references to local 3 part named objects should now all be resolved. Below are the screen shots showing the UI in steps 1-7.

 

Thanks to Tom, a QA Lead on the team, for identifying this work around.

Step 1:

 

Step 2-6:

 

Step 7:

Posted by bahill | 0 Comments
Filed under: ,

Installing GDR R2 with different languages side-by-side

The GDR R2 has a limitation that it uses the same product id for all localized versions of the product.  This prevents you from installing more than one localized version of the product.  If you have a need to install multiple language versions of GDR R2, you can do so by employing the following work around.  Thanks to Jeff on the team for identifying this work around.

 

This assumes you already have some version of the GDR R2 installed already.

 

1.       Start->Run->RegEdit

2.       Search for  BEA3F8AE04D2DEE4A9A882322897A07B which will be found under HKCR\Installer\Products

3.       Update the existing ProductID reg key to  BEA3F8AE04D2DEE4A9A882322897A07Ba (This is simply an “a” added to the key)

4.       Install you favorite secondary language from Microsoft Downloads

 

This work around will cause only the second language to be listed in Add or Remove Programs.  To uninstall the GDR R2 you would uninstall the second language installed, then change the product id back to the original value, and finally uninstall the first localized version you installed.

 

Keywords: Localized; Multiple Languages

Posted by bahill | 0 Comments
Filed under:

Overview of composite and partial database projects

A colleague published a new white paper on composite and partial  database projects.  If you want to understand the difference or see how these project types are used check this out:

 

Collaborative Development with Team System 2008 Database Edition

http://msdn.microsoft.com/en-us/library/dd452417.aspx

Posted by bahill | 0 Comments
Filed under:

Deploying Composite Projects through a Database Unit Test Run

A customer asked me the other day how they could deploy Composite Projects as a part of a Database Unit Test run. They are using the Composite Project to segment their Database Projects into 2 projects for maintainability and deployment reasons.  1 schema is for a database they ship to customers as part of a BI application and the other is a superset schema that includes the shipped version, but also some additional schema for a hosted version of the same application. They are running unit tests through their database projects and needed to test the hosted version of their application. 

 

Database Unit Test Projects provide the ability to deploy a database project and run data generation on a target database that can be used by the tests all in one test run.  What is unique for Database Unit Tests is that you can define your tests using the language of the target and in this case it is T-SQL. The configuration of the unit test project only allows you to select one project as can be seen in the following:

 

 

 

You could define additional tests for each project, but that adds additional overhead and challenges to maintainability.  I asked Jamie Laflen, a senior developer on my team, alternatives to use and he showed me a nifty approach that works really well and can be used for other customization of test runs if necessary.  You can subclass the Database Test service.  It’s pretty straight forward to implement.  Replace the code in your DatabaseSetup class [DatabaseSetup.cs in the Unit Test Project] and set the relative paths to the projects in your solution you want to deploy. One thing to note is that you want to deploy them in the order of inner-most to outer-most based on how the references are setup.

 

Below is the code to subclass the test service and call deploy on database projects in your solution.  Enjoy!

 

using System;

using System.Collections.Generic;

using System.Data;

using System.Data.Common;

using System.Configuration;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using Microsoft.Data.Schema.UnitTesting;

using Microsoft.Data.Schema.UnitTesting.Configuration;

 

namespace DBTestProject

{

    [TestClass()]

    public class DatabaseSetup

    {

        [AssemblyInitialize()]

        public static void IntializeAssembly(TestContext ctx)

        {

 

            //Comment out the original call to deploy the database project

            //DatabaseTestClass.TestService.DeployDatabaseProject();

           

            //New up your own class and call your method to deploy the composite projects

            MyDBTestService MyDatabaseTestService = new MyDBTestService();

            MyDatabaseTestService.DeployProjects();

 

            DatabaseTestClass.TestService.GenerateData();

        }

    }

 

    class MyDBTestService : DatabaseTestService

    {

        public void DeployProjects()

        {

            DeployDatabaseProject(@"..\..\..\TestDBInner\TestDBInner.dbproj", "Debug", "System.Data.SqlClient", GetConnectionString());

            DeployDatabaseProject(@"..\..\..\TestDBOuter\TestDBOuter.dbproj", "Debug", "System.Data.SqlClient", GetConnectionString());

        }

 

        private static string GetConnectionString()

        {

            DatabaseUnitTestingSection dbConfig =

                (DatabaseUnitTestingSection)ConfigurationManager.GetSection("DatabaseUnitTesting");

            return dbConfig.ExecutionContext.ConnectionString;

        }

    }

}

 

Posted by bahill | 0 Comments

Come visit/revisit The Beer House! (Continuous Integration)

Isn’t it funny how a simple word like Beer can grab your attention?  In this post we will look at migrating The Beer House (TBH) sample application database to be managed by VSTS:DB.  For those in the NW you may have seen this previously as I have presented this to the PNWSQL User Group. Since then I have received many requests for sharing the demo walkthrough. 

 

This post illustrates how Visual Studio Team System Database Edition enables continuous integration for databases. The walkthrough demonstrates how to setup Team Build to automate building and deploying databases.  Using VSTSDB’s incremental deployment engine in conjunction with Team Build build automation enables database continuous integration for teams working with Team System. 

 

The best thing about this walkthrough is that anyone with a PC capable of running a VM (VPC or Hyper-V) can follow along on their own boxes because everything I will be using is FREE!  Well, it’s free as in Trial Software and Sample Applications.

 

This post will include the following sections:

 

1.    Why continuous integration?

2.    Migrating the Beer House Sample Application to use Database Projects

3.    Setting up the Database Projects for Sandbox Development

4.    Adding our Solution to Source Code Control

5.    Setting up our Database for Rolling Builds/Continuous Integration

6.    Overriding a SQLCMD variable to Version Stamp DBs

 

Why continuous integration?

 

Continuous Integration (CI) is a core tenant of being an agile team. Teams that use agile methodologies typically work on producing software in short iterative cycles producing a functional piece of software with every check-in, at the end of every day, and at the completion of every iteration or sprint.  Basically, the codebase that is checked into the shared repository should be functional at all times.  CI is practice where each check-in results in a build being run which produces a software product that can be assessed for quality, completeness, and other indicators that communicate the project is moving in the right direction.  This assessment typically includes build verification tests (BVTs), but may also include other tests as well.  Since the CI build, or sometimes called rolling build, is automated many teams run all their automated tests.  This frees up the development team from having to run long running test suites that may impact productivity if ran by multiple individuals of the team many times a day. 

 

Some of the benefits of CI include: spotting integration issues early; early build break detection; complete and regular test runs; automated code promotion; deployment issues identified early; redundant tasks automated; stakeholder visibility into project progression increased.  While CI is by no means a new concept, in fact it has been used by many teams successfully for years now, what teams continue to struggle with is how to integrate their database components into their CI process. The challenge is the same I outlined in a previous post in regards to database deployment; it is the existence of state.

 

With Team System Database Edition GDR and Team Foundation Server you can enable CI for Visual Studio projects.  Using the Incremental Deployment Engine of VSTS:DB and the build automation platform of Team Build you can enable CI for your databases. In this post I outline the walkthrough I have presented a few times.  I hope this inspires teams to automate their builds and reap the many benefits CI has to offer.

 

If you want to dig into CI more I recommend the following reading:

Continuous Integration – Martin Fowler

http://martinfowler.com/articles/continuousIntegration.html

See also his related whitepaper on Evolutionary Database Design.

 

 

Before we get rolling here is what you will need:

·         Microsoft® Visual Studio® Team System 2008 Team Foundation Server SP1 and Team Suite SP1 VPC or Hyper-V Image (Trial)

·         Microsoft® Visual Studio Team System 2008 Database Edition GDR R2

·         TheBeerHouse: CMS & E-commerce Site Starter Kit (Code Plex Project)

 

If you have Windows Server x64 and a spare 2GB RAM free I highly recommend using the Hyper-V image.  The performance of the Hyper-V image is superior to that of VPC.  I will assume in this article that you can effectively use VPC or Hyper-V. If you need help, please see Hyper-V/VPC.

 

 

Migrating the Beer House Sample Application to use Database Projects

 

Once you have your virtualized environment setup you can start migrating the application.  Do this by downloading the starter kit from the link above. Unzip the file on your VM to a directory called DasRoot. You should end up with a directory structure that looks like this:

 

 

 

Let’s now attach the app’s Database to the default SQL Instance.  Note there are 2 SQL instances on this VM. The default instance running is 2005 SQL Enterprise along with other services like SSRS.  The default instance also has the TFS databases on it.  We will consider this our production server for this walkthrough.  The other instance is a 2005 SQL Express instance.  This is installed as a part of VSTS.  We will use this instance as our development and integration server. You can also install SQL 2008 on the VM if you like. I have run 4 instances of SQL on a VM with 2GB RAM with acceptable performance for evaluation and proofing purposes. 

 

To easily attach the TBH database to the production server copy the sql files to the default data directory of the default instance and run this T-SQL script:

Copy Command

 

C:\DasRoot>copy "C:\DasRoot\TheBeerHouse\TBH_Web\App_Data\ASPNETDB.MDF" "C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data\TBHDB.mdf"

        1 file(s) copied.

C:\DasRoot>copy "C:\DasRoot\TheBeerHouse\TBH_Web\App_Data\ASPNETDB_log.LDF" "C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data\TBHDB_log.ldf"

        1 file(s) copied.

 

T-SQL to attach DB and add a login

 

EXEC sp_attach_db @dbname=N'TheBeerHouse',

     @filename1=N'C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data\TBHDB.mdf',

     @filename2=N'C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data\TBHDB_log.ldf'

GO

USE TheBeerHouse

GO

EXEC sp_addlogin 'TheBeerHouseApp', 'BeerNow!', 'master'

GO

EXEC sp_grantdbaccess 'TheBeerHouseApp', 'TheBeerHouseApp'

GO

EXEC sp_addrolemember 'db_datareader', 'TheBeerHouseApp'

GO

EXEC sp_addrolemember 'db_datawriter', 'TheBeerHouseApp'

GO

EXEC sp_addrolemember 'db_owner', 'TheBeerHouseApp'

GO

 

You will need to also change your databases to use mix mode security since the application will be using a SQL Login for authentication.  You can change the script to use a Windows login if you like, but for this walkthrough we will be using SQL Login.

 

This sample database includes some of the SQL diagramming tables.  We won’t need these so you can clean them up by executing this T-SQL:

USE [TheBeerHouse]

GO

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[sp_alterdiagram]') AND type in (N'P', N'PC'))

DROP PROCEDURE [dbo].[sp_alterdiagram]

GO

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[sp_creatediagram]') AND type in (N'P', N'PC'))

DROP PROCEDURE [dbo].[sp_creatediagram]

GO

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[sp_dropdiagram]') AND type in (N'P', N'PC'))

DROP PROCEDURE [dbo].[sp_dropdiagram]

GO

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[sp_helpdiagramdefinition]') AND type in (N'P', N'PC'))

DROP PROCEDURE [dbo].[sp_helpdiagramdefinition]

GO

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[sp_helpdiagrams]') AND type in (N'P', N'PC'))

DROP PROCEDURE [dbo].[sp_helpdiagrams]

GO

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[sp_renamediagram]') AND type in (N'P', N'PC'))

DROP PROCEDURE [dbo].[sp_renamediagram]

GO

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[sp_upgraddiagrams]') AND type in (N'P', N'PC'))

DROP PROCEDURE [dbo].[sp_upgraddiagrams]

GO

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[sysdiagrams]') AND type in (N'U'))

DROP TABLE [dbo].[sysdiagrams]

GO

 

Ok, the database is there now so let’s adjust the solution to use this database.  The application comes as a RANU Database.  Since we will be maintaining the database as a project we don’t need the contents in App_Data anymore.  We also need to change the connection string for the application. Before doing those things we also want everything in the same solution.  We do this by:

1.       Create a new solution  [C:\DasRoot\TheBeerHouse\TBH.sln]. You do this by selecting File->New->Project->Other Project Types->Visual Studio Solutions.

2.       Move both [C:\DasRoot\TheBeerHouse\TBH_Web] and [C:\DasRoot\TheBeerHouse\TBH_CustomEvents\TBHCustomEvents.csproj] directories under the TBH folder [C:\DasRoot\TheBeerHouse\TBH].

3.       Add the website project to the new solution.  You do this be right clicking the solution file in the Solution Explorer and selecting Add->Existing Website->Select Folder on Disk [C:\DasRoot\TheBeerHouse\TBH\TBH_Web]. Answer yes to upgrade question.

4.       Add the Custom Events project to the new solution.  You do this be right clicking the solution file in the Solution Explorer and selecting Add->Existing Project->Select Folder on Disk [C:\DasRoot\TheBeerHouse\TBH\TBH_CustomEvents\TBHCustomEvents.csproj]. Answer yes to upgrade question.

5.       In the Web Project locate the App_Data folder and delete the ASPNETDB.MDF files.

6.       In the Web Project open the web.config and update the connection string to use our new database. You do this by replacing the connection string section with this element:

<connectionStrings>

<remove name="LocalSqlServer"/>

<add name="LocalSqlServer" connectionString="Data Source=.\;Initial Catalog=TheBeerHouse;User=TheBeerHouseApp;Password=BeerNow!" providerName="System.Data.SqlClient"/>

</ connectionStrings >

7.       Set the Web Project as the Start Up Project. (It should have already defaulted to this)

8.       F5 to run the Web App against our newly attached Database.  You should see something like this: 

 

 

 

If you get errors then you probably didn’t set the SQL Security to Mixed Mode. ;)

 

We now have a working Web App running against our “production” database.  Let’s now put the database into a database project.  We will be using a 2005 Database Project and a 2005 Database Server Project.  The Database Project is for the user created database objects.  This is for all the database objects used by the web application.  The Database Server Project is for our server level objects.  We only have one, the SQL Login the web application uses to log into database.  You accomplish this by:

1.       Add a 2005 Database Project to the solution called TBHDB.  You do this by  right clicking the solution file in the Solution Explorer and selecting Add->New Project->Database Projects->SQL Server 2005.

2.       Add a 2005 Database Server Project to the solution called TBHDBServer.  You do this by  right clicking the solution file in the Solution Explorer and selecting Add->New Project->Database Projects->SQL Server 2005.

 

Your solution should now look like:

 

 

We now need to populate the Database Project.  We do this be reverse engineering the database we setup previously. You do this by importing the database into the database project [TBHDB].  When you import the database the database is shredded into individual SQL files.  These individual files represent the schema of the Database.  Since these files are simply SQL files and the files contain individual objects they are suitable to maintain and version in the project system.   You import the database by right clicking on the database project node in Solution Explorer and select  “Import Database Objects and  Settings”. You will be presented with a dialog where you can define the connection to the database and define import settings.  Let’s import the database by:

1.       Defining a new connection to the TheBeerHouse database on the default instance. Click new connection. Server Name = “.”.  Database Name = “TheBeerHouse”

2.       Keep the “Script the column collation only when it..” option checked.

3.       Keep the “Import extended properties”  option checked.

4.       Check the “Import permissions” and check the “Add imported permissions to the project model” also.  We want to work with permissions for this walkthrough.

5.       Check the “Override configuration with imported schema settings” . This will import all the database settings along with the schema. An example would be the collation of the database.

6.       Leave the Max files per directory at 1000.  This walkthrough doesn’t have very many files, but if you are working on a large schema you may want to drop this down to 250-500.  Lots of files in one directory makes VS sluggish for some operations.

7.       Click Start. When the database schema import completes click finish.

 

Setting up the Database Projects for Sandbox Development

 

You will notice that VS is now displaying status messages that it is building out the project’s model.  It should complete quickly as the TBH  schema is pretty simple.  When it says “Ready” take a look at the VS Error List.  You can do this by selecting View->Error List from the Main menu.  You should see 9 Warnings and 1 Error.

 

 

 

When you import Database Projects the Error List is the first place you want to start. It will help you work through any Errors and Warnings as you do your house keeping to tidy up the database. Just like other VS project types, Errors block the build and Warnings are warnings that something may not be right with the project. 

 

Let’s take care of the error first.  The error is for an unresolved reference to a Login object from a database user object.  The Login is the same login we previously added when setting up the database. Since Logins are a server level object it was not imported into the database project.  We need to add it to our server project [TBHDBServer]. We could import the master database from the server into our server project to get the login.  This would also bring down all the other user created objects in master.  Since it is only one object let’s just add it to the server project ourselves. You do this by:

1.       Locate the Server Project in Solution Explorer

2.       Expand the Schema Objects->Server Level Objects->Security folder.

3.       Right click on the Security folder and select Add->New Item.

4.       Select the Login (SQL) template from the New Item Dialog.

5.       Name the Login “TheBeerHouseApp” and click Ok.

6.       You should now be staring at the CREATE LOGIN Script.  Update password variable to “BeerNow!” and save the file.

7.       Your TheBeerHouseApp.login.sql file should look like this:

8.       We now need to create a database reference from the database project to the server project to resolve our error. Right click on the “References” folder under the TBHDB project.  Select Add Database Reference.

9.       You are now presented with a dialog for creating new Database References. It has selected the TBHDBServer project already. It has also added “master” as the literal for database variable.  It does this since master is always master on the database server.

10.   Click Ok. You will see the reference is added under the “References” folder of the TBHDB project. This dialog should look like:

11.   Right Click on the TBHDBServer project and click Build. You do this so the database server project is validated and its model is updated.

12.   Notice how the Error disappears from the error list.  We now are resolving the Login to the database server project.

 

We should be left with 9 warnings. All the warnings are from stored procedures in the TBHDB project.  One thing to note here is that unresolved references in stored procedures are usually warnings.  The reason is that stored procedures have deferred name resolution.  This means it is possible to create a stored procedure without the objects it uses as table sources existing.  Out of the 9 warnings we will resolve 8 of them.  6 of these warnings are for references to sysobjects.  Database and server projects do not include all built-ins like system tables, views, and catalogs.  You need another database reference for these.  Much like how we resolved the error to the login, we will resolve these warnings for sysobjects by referencing a logical model of the master database that contains all these built-ins. You may be thinking “why is this not in the server project we just created”? The fact is that database server projects only include user level created objects.  You can resolve these reference warnings to sysobjects by: 

1.       Right click on the “References” folder under the TBHDB project again.  Select Add Database Reference.

2.       Select the “dbschema” option.

3.       Select the master.dbschema file from disk located: C:\Program Files\Microsoft Visual Studio 9.0\VSTSDB\Extensions\SqlServer\2005\DBSchemas\master.dbschema

4.       Leave database variable literal as “master”

5.       The database reference dialog should look like this.  Click Ok.

 

After a little while you should see 6 of the warnings for the missing [sysobjects] object disappear.  What we have done to resolve this is added the master.dbschema model to our project that represents all the built-ins provided by master.  We now have 2 references and both represent the master database.  Yes, this is possible.  You may have also noticed that processing the reference to master.dbschema takes much longer than it did for the server project.  That is because the model for master.dbschema is much larger.  Even though we only need the definition for the sysobjects table we took on the overhead of the whole master.dbschema.  You may want to check this out for how to customize your master.dbschema file. Note: If you use customized dbschema files you will want to add them to your solution so everyone on the team can reference the same file.  This also will become a part of the source and will be available when or where ever you do your builds.

 

We should be left with 3 warnings now. 2 of the warnings are for ambiguous column references in the ORDER BY clause of 2 stored procedures. The 2 table sources in each of the stored procedures both have the column that the results are ordered by.  While this is fine for SQL to execute (it assumes the column in the SELECT list) it makes maintenance problematic.  It’s better to be explicit in this case to prevent runtime errors should the other Added Date be included in the SELECT list. To resolve the warnings simply double click on each other warnings for tbh_Forums_GetUnapprovedPosts and tbh_Forums_GetUnapprovedPosts and replace the Added column in the ORDER BY clause with tbh_Posts.AddedDate. Save and close both files.  You should see 2 warnings disappear. This leaves one warning left. The warning is for a session level temporary table.  GDR R2 does not resolve references to session level temporary tables.  Using session level temp tables is not a good design practice.  Our guidance is to use table parameter instead as it makes the code more maintainable and design clearer.  Since we don’t plan to resolve this warning we can suppress it.  You can suppress the warning by:

1.       Locate the  aspnet_Setup_RestorePermissions.proc.sql in Solution Explorer

2.       Right Click on file and select “Properties”

3.       On the “Properties” dialog enter the warning number for this warning (4151).

4.       The properties window will look like this:

5.       Save the file.

 

You have no warnings now. Caution: By suppressing the warning 4151 for the  aspnet_Setup_RestorePermissions.proc.sql file we just suppressed all 4151 warnings for this file.  This might not be desirable as you will probably still want to know if other objects are unresolved.  Unfortunately warning suppression is only supported at the file and project levels. If you want other unresolved warnings for the file you will have to live with the unresolved session level temporary warning or refactor the code to use a table variable instead.  Sorry.

 

Now that we have dealt with our errors and warnings we have our solution in good shape to move on.  Next we should deploy the database to our local sandbox.  Deploying it locally and running the app against the sandbox database will give us the confidence that the database and application is fully functional. But, before we can do that we need to consider reference data.  TBH application depends on some simple version metadata for the ASPNET User Profile components.  We could run the Aspnet_regsql.exe that adds this data everytime we deploy a new database or we can just deploy it ourselves.  After all, we are already versioning the User Profile tables we might as well version the reference data required for ASPNET to make use of them.  To do this easily we just simply add this T-SQL to the post deployment script of our TBHDB project:

IF NOT EXISTS

      (SELECT * FROM dbo.aspnet_SchemaVersions)

BEGIN

SET NOCOUNT ON

PRINT 'Adding [dbo].[aspnet_SchemaVersions] Data'

INSERT INTO [dbo].[aspnet_SchemaVersions]([Feature],[CompatibleSchemaVersion],[IsCurrentVersion])

VALUES('common',1,1)

INSERT INTO [dbo].[aspnet_SchemaVersions]([Feature],[CompatibleSchemaVersion],[IsCurrentVersion])

VALUES('health monitoring',1,1)

INSERT INTO [dbo].[aspnet_SchemaVersions]([Feature],[CompatibleSchemaVersion],[IsCurrentVersion])

VALUES('membership',1,1)

INSERT INTO [dbo].[aspnet_SchemaVersions]([Feature],[CompatibleSchemaVersion],[IsCurrentVersion])

VALUES('personalization',1,1)

INSERT INTO [dbo].[aspnet_SchemaVersions]([Feature],[CompatibleSchemaVersion],[IsCurrentVersion])

VALUES('profile',1,1)

INSERT INTO [dbo].[aspnet_SchemaVersions]([Feature],[CompatibleSchemaVersion],[IsCurrentVersion])

VALUES('role manager',1,1)

SET NOCOUNT OFF

END

 

Now let’s configure the projects to deploy locally so we can proof the deployment.  To do this:

1.       Right click on the TBHDB project and select properties

2.       Select the Deploy tab

3.       In the second combo box select “My isolated development environment” for the Configure deployment settings for option.

4.       In the first combo box select “Create and deployment script and deploy…” for the Deployment Acton option

5.       In the Target Connection click edit.

6.       Add the server name as “.\SQLEXPRESS”

7.       Leave the database name the same as the project [TBHDB]

8.       Save and close the Project Properties

9.       Repeat the same steps for the database server project [TBHDBServer], but leave the database name as master on this one

10.   Right click on the TBHDBServer project and select Deploy

11.   Here is the output for the database server project deployment:

12.   Now, Deploy the TBHDB database project.

13.   Here is the output for the database project deployment:

 

Note how when we deploy the Database Project that it also deploys the database server project.  This is because there is a dependency between the database and the database server project that is managed by VS.  If you deployed from the command line you would not have this dependency checking and would need to deploy them in the correct order.  This is true of all database references except references to dbschema files.

 

Let’s go back and edit the web.config of the project.  We need to point it at our sandbox database.  We do this by changing the connection string to:

<add name="LocalSqlServer" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=TBHDB;User=TheBeerHouseApp;Password=BeerNow!" providerName="System.Data.SqlClient"/>

 

Now we can view the TBH app with it using the local sandbox database.  Again, you can start up the web app by F5 (Debugging) or by right clicking on web project and select “View in Browser”.  We see TBH app working! Note how this app instance has no data as compared to what we saw earlier.  This is because when we deployed locally we only deployed the schema, security, and the minimum reference data.  You could use the Data Compare feature if you want to easily pump the data into our sandbox database.  You could also generate test data for this database using the Data  Generation feature, but for now we will move forward with configuring our database projects.

 

Adding our Solution to Source Code Control

 

We now have our solution and projects setup (mostly) and its time to check-in! Before we can check-in we need a team project with source control.  To create source control for this solution:

1.       Click on the Team Explorer Window. (If Team Explorer is not shown select it from VS Menu View->Team Explorer)

2.       Right click on the Server Node [TFSRTM08] and select “New Team Project”

3.       A wizard dialog will be presented to help you configure your Team Project. On this first screen add the name of the project “TheBeerHouse” and click next.

4.       On the second screen select “MSF for Agile...” instead of CMMI because at the Beer House we are an agile team!

5.       Click Finish and wait….

6.       When it is done creating and hooking up your Team Project Click Close. The process guidance should be loaded up in VS main window.  If you can see it that is good because the associated SharePoint site is functioning properly.

 

Your Team Project in Team Explorer should now look like this:

 

 

Now that we have our TFS project we can add our solution to Source Code Control.  There are a few different ways to do this, but I will do it from Solution Explorer. 

1.       Right click on the solution in Solution Explorer  and select “Add Solution to Source Code Control”

2.       You will be presented a dialog that will help you add the project to source control. You can see there are handfuls of other projects there as well.  Feel free to check those out, go on, I will wait here for you.

3.       Select the TheBeerHouse project.

4.       In the box for the name of the solution folder clear “TBH” and enter “MAIN”.  This will make more sense later.

5.       Click OK. This process is quick, usually.

6.       Right click on the Solution again and select “View Pending Changes”

7.       The Pending Changes window will appear showing you all the changes that you can commit to the repository. Click the Check In button.

8.       Go back to Team Explorer and open the Source Control in your team project.  You will see a branch of code now under in source control that has just been checked-in.

 

Your Source Control for your project should look like this:

 

 

A couple of things to mention: when you are adding to source control from the IDE the file filtering is managed for you.  If you are adding the project to source control from the file system you will not want to add the following files:

·         <ProjectName>.dbmdl – This file is the cached project model. It should not be source controlled.  You always need read/write access to this file while the project is open in the IDE. The cache should be rebuilt for each developer on the team.

·         <ProjectName>.dbproj.user – This file is the user project settings like deployment settings for isolated development environments. Each developer should have their own settings files.

·         Sql Folder – This folder under the database project will contain build outputs.  Files in this directory need to read/write on each build/deploy.

·         Obj Folder - This folder under the database project will contain temporary build outputs.  Files in this directory need to read/write on each build/deploy.

 

Setting up our Database for Rolling Builds/Continuous Integration

 

We now have our solution and projects setup and in source control.  Given that we will be often doing our database development in our local sandbox environments we need an integration server so that all our changes can be integrated into a single database instance that our team can reference. This integration environment is where we will also regularly run our TBH application and any automated build validation testing.  Our integration environment will be regularly updated with the latest code and changes integrated into database. This is often called Rolling Builds where a build is produced everyday or every check-in.  Any issues with incremental database deployment or application runtime failures can be communicated to the team immediately.  This allows the team to know early when a build break occurs as a result of integration.  It also builds the confidence of the team through the integration environment being upgraded many, many times during the iteration cycle. This is our continuous integration environment and is critical for any agile team. If possible, you want this environment to mimic the production environment even it is only on a small scale.

 

To create our integration environment I continue to configure our solution.  Specifically I will configure the Debug configuration to deploy and update our integration environment automatically as a result of code churn. For our Release configuration I will configure it to deploy to script so that the upgrade can be manually or semi-manually run. I will only be configuring the database projects for deployment in this post, but you can certainly automate the deployment of the other projects as well.

 

For the Debug Configuration I make the following changes:

1.       Right click on the TBHDB project and select properties

2.       Select the Deploy tab

3.       In the second combo box select “My project settings” for the Configure deployment settings for option.

4.       In the first combo box select “Create and deployment script and deploy…” for the Deployment Acton option

5.       In the Target Connection click edit.

6.       Add the server name as “.\SQLEXPRESS”

7.       For the database name, name it [TBHDB-INT]

8.       Save and close the Project Properties

9.       Do the same for the server project, but leave the database names as “master”

 

For the Release Configuration I make the following changes:

1.       Right click on the TBHDB project and select properties

2.       Select the Deploy tab

3.       In the second combo box select “My project settings” for the Configure deployment settings for option.

4.       In the first combo box select “Create deployment script” for the Deployment Acton option

5.       In the Target Connection click edit.

6.       Add the server name as “.”

7.       For the database name, name it [TheBeerHouse]

8.       Save and close the Project Properties

9.       Do the same for the server project, but leave the database names as “master”

 

I now need a build definition that will define my build and build process. I will create 2 build definitions. One will be my Debug build and the other my Release build. To create the Debug build definition See (Brian Randell’s MSDN article on this topic):

1.    Expand my project in Team Explorer.

2.    Right click on the Builds directory and select “New Build Definition”

3.    You will be presented with a dialog that will walk you through creating a TeamBuild Build Definition.

4.    On the fist tab [General] enter TBH-INT for the name of the definition.

5.    On the second tab [Workspace] specify “$/TheBeerHouse/MAIN” as the Source Control Folder.

6.    On the third tab [Project File] accept the default name of the MSBuild Project file and click Create.  On the subsequent screens select the solution from the Main directory in source control and specify Debug as the configuration.

7.    On the fifth tab [Build Defaults] click “New” for the agent. On the Build Agent Properties dialog just specify “BUILDBOT” and “TFSRTM08” and click Ok. For the build staging share specify “\\TFSRTM08\Drops”

8.    On the sixth tab [Trigger] select the Build each check-in option.

9.    Click Ok to close dialog. The new build definition should appear.

10. Click on Source Control in Team Explorer. Locate $/TheBeerHouse/TeamBuildTypes/TBH-INT/TFSBuild.proj and Get Latest Version of file.

11. Open file once you have it on disk and add the following Target right before the closing </Project> tag:

<Target Name="AfterDropBuild">

<MSBuild Projects="$(SolutionRoot)\TBHDBServer\TBHDBServer.dbproj" Properties="Configuration=Debug;OutDir=$(DropLocation)\$(BuildNumber)\Debug\"

Targets="Deploy"/>

<MSBuild Projects="$(SolutionRoot)\TBHDB\TBHDB.dbproj" Properties="Configuration=Debug;OutDir=$(DropLocation)\$(BuildNumber)\Debug\"

   Targets="Deploy"/>

</Target>

 

You have your Debug TeamBuild setup now. There are only a few more changes to make. The following changes make it where you can deploy the database projects from any path and to any database instance.

 

1.    Right click on the solution in Solution Explorer and select Add->New Solution Folder. Call it “RefDbschemas”.

2.    Copy the dbschema file from:

“C:\Program Files\Microsoft Visual Studio 9.0\VSTSDB\Extensions\SqlServer\2005\DBSchemas\master.dbschema” to: “C:\DasRoot\TheBeerHouse\TBH\RefDbschemas\master.dbschema”.

12. Reestablish the reference from the TBHDB project to the local copy of the master.dbschema. This will make sure the dbschema can always be resolved to a relative path.

13. Add this copy master.dbschema to the new solution folder by right clicking on the folder and selecting Add->Existing->Item.

14. Locate the ASPNETDB.MDF.sqlfile.sql and the ASPNETDB_log.LDF.sqlfile.sql under the TBHDB project->Schema Objects->Database Level Objects->Storage-> Files and delete both of these files.  Since we are not using any specific configuration for our SQL files and we plan to deploy this database multiple times to the same database instance we need the file configurations to be unique.  With the files removed any time a database is created it will be done using the server’s default configuration for SQL files.  Alternatively, we could have updated the file definitions to be unique to the target and kept them in the project, but for this database that would be unnecessary since it has only simple sql file configuration. 

15. Right Click the solution file in Solution Explorer and select check-in.

 

After you check-in you might notice your box starts chugging.  This is because TeamBuild just kicked off a build based on your last check-in.  You can watch the progress of the build by double clicking on the [TBH-INT] build definition under Builds in Team Explorer.  When the Build Explorer opens you can click on the current active build to see where it is at in the build process. When the build is complete you will have the TBH-INT deployed on your SQLEXPRESS instance.  You can also find the build outputs and scripts to create the database in your build drop folder similar to: C:\Drops\TBH-INT_2009XXXX.1

Here is what your build log will look like:

 

 

One thing you might notice is how the database projects are being listed twice in the team build log.  The is caused by building the solution which results in each database project to be built in the correct dependency order, but the solution will also emit additional builds for each database project in the solution.  Since the database project is already built, the build will short circuit since all the outputs are current.  Essentially it’s a no-op and does not impact your build. 

 

To setup your Release build definition is a breeze.  Most the work has already been done. To create the Release build definition:

1.    Expand my project in Team Explorer.

2.    Right click on the Builds directory and select “New Build Definition”

3.    You will be presented with a dialog that will walk you through creating a TeamBuild Build Definition.

4.    On the fist tab [General] enter TBH-RET for the name of the definition.

5.    On the second tab [Workspace] specify “$/TheBeerHouse/MAIN” as the Source Control Folder.

6.    On the third tab [Project File] accept the default name of the MSBuild Project file and click Create.  On the subsequent screens select the solution from the Main directory in source control and specify Release as the configuration.

7.    On the fifth tab [Build Defaults] select BUILDBOT as the Build Agent and “\\TFSRTM08\Drops” as the drop location.

8.    On the sixth tab [Trigger] select the Check-ins do not trigger builds option.

9.    Click Ok to close dialog. The new build definition should appear.

10. Open the Team Build project [C:\DasRoot\TheBeerHouse\Build Projects\TeamBuildTypes\TBH-RET] once you have it on disk and add the following Target right before the closing </Project> tag:

<Target Name="AfterDropBuild">

<MSBuild Projects="$(SolutionRoot)\TBHDBServer\TBHDBServer.dbproj" Properties="Configuration=Debug;OutDir=$(DropLocation)\$(BuildNumber)\Debug\"

Targets="Deploy"/>

<MSBuild Projects="$(SolutionRoot)\TBHDB\TBHDB.dbproj" Properties="Configuration=Debug;OutDir=$(DropLocation)\$(BuildNumber)\Debug\"

   Targets="Deploy"/>

</Target>

11. Right click on the TBH-RET build definition and select “Queue New Build”.

 

The RET build does not deploy the database, but will produce incremental deploy scripts whenever the TBH-RET build definition is run. Remember that we configure release to deploy to script. The deployment scripts and the build outputs can be found in the drops folder. Your first RET build will only contain the incremental scripts to modify the 2 stored procedures we adjust earlier in the walkthrough. Remember that?

 

Overriding a SQLCMD variable to Version Stamp DBs

 

With our rolling builds setup and deployment automated we can now focus on improving on the existing database schema. A simple and valuable way to monitor changes as they make their way through the code promotion paths is to add a version stamp.  For databases there is not an immutable version like assemblies, but we can add a version to the deployed database in the form of an extended property.  The version of the database will then be ambient and I can tie other scripts to the version of the database. 

 

I do this by first adding a variable to the Database.Sqlcmdvars file. Under the properties folder of the database project [TBHDB] open the Database.Sqlcmdvars file.  Add a variable called “DBVersion” and provide a value like “UNDEF”. This will look like the following in the IDE:

 

 

I then add an extended property to the database project by right clicking on the Schema Objects folder of the TBHDB database project and selecting Add->New Item->Extended  Property and call it “DBBUILD”. A file named DBBUILD.extendedproperty.sql will be added to the project.  You need only to add the variable to the value of the extended property.  This looks like:

 

  

You can now deploy to your local sandbox database and see the extended property.  Remember this is the [TBHDB] database on your SQLEXPRESS instance. If you open the TBH project properties you can simply change the Configure Deployment settings for “My isolated development environment” and you previous settings are there. Deploy the TBHDB project by right clicking on the TBHDB project in Solution Explorer and select “Deploy”. You will see the extended property is added to the database.  Here is what it looks like from SSMS:

 

 

Ok, so UNDEF is not that useful as a version.  You can manually change it from the Database.sqlcmdvars file or you can update it as a part of the deployment process. To set the version as a part of the deployment process you need to first edit the project file of the TBHDB project.  To do this:

 

1.    Right click on the TBHDB project in Solution Explorer and select “Unload Project”

2.    Right click on the TBHDB unloaded project in Solution Explorer and select “Edit”

3.    Add this XML to the Item Group that follows the Import Project element (approx line 57):

      <ItemGroup>

      <!--BEGIN SqlCommandVariableOverride-->

<SqlCommandVariableOverride Include="DBBuild=$(DBBuild)" Condition="'$(DBBuild)' != ''"/>

      <!--END SqlCommandVariableOverrides-->

4.  Right click on the TBHDB project in Solution Explorer and select “Reload”

5.  Go back to Source Control and edit the TBH-INT definition [C:\DasRoot\TheBeerHouse\Build Projects\TeamBuildTypes\TBH-INT\TeamBuild.proj]. Update the Deploy target call to include the variable as a property. This looks like:

<Target Name="AfterDropBuild">

<MSBuild Projects="$(SolutionRoot)\TBHDBServer\TBHDBServer.dbproj" Properties="Configuration=Debug;OutDir=$(DropLocation)\$(BuildNumber)\Debug\"

Targets="Deploy"/>

<MSBuild Projects="$(SolutionRoot)\TBHDB\TBHDB.dbproj" Properties="Configuration=Debug;OutDir=$(DropLocation)\$(BuildNumber)\Debug\;DBBuild=$(BuildNumber)"

    Targets="Deploy"/>

 </Target>

6.  Do this also for the TBH-RET build definition.

7.  Check-in your pending changes.

 

Once you check-in these changes your build will take off and update the integration database.  After it finishes the build and deploy we can check out the extended property in SSMS and see the DBBuild Extended Property looks like this now:

 

Also, if you Queue a RET build it include the T-SQL to add the extended property as well as the 2 stored procedures.  Since we never applied the incremental script it will continue to include all the changes we have made until we upgrade the production database to bring them in synch.

 

For those of you thinking, “wow!, that’s cool for folks with TeamBuild, but what about shops not running TeamBuild?” You can also pass the build version through MSBUILD.  Here is the MSBUILD command with variable specified: 

 

 

Deployment is now a snap! So whether I want to deploy a change to my sandbox, or check-in and have my changes deployed to integration or even generate a script to upgrade production I can focus on my code, not build and release mechanics. In addition, I can be assured that build breaks can be detected early and before the end of the iteration cycle/sprint where they are most costly.

 

That’s the end of the walkthrough.  Now you have a test bed to try out all kinds of changes to the schema and have the changes deployed for you. Once I have reached this point in the demo folks are interested to see how refactoring and other incremental changes are deployed. This is a good point to snapshot your VM so you can return to this point if you make a lot of changes and want to keep them.

 

Depending on reader interest, I may use this VM as the foundation for future posts. It will be hard not to now that we have a known starting place for everyone that follows along. Bonus, you can bypass the whole setup process by downloading the VM. Convinced yet? Besides, who doesn’t like talking about Beer even if it is not directly related to the subject matter?

 

Once you have your environment setup in a VM you can start trying out all kinds of things and easily revert back to a previous state. You can even jump forward to another snapshot. Go and experiment! I highly recommend folks that have the hardware and OS give Hyper-V a whirl. I have found it to be indispensible. Everything I have now runs on a Hyper-V VM besides my email box.  I better stop before this post turns into a Hyper-V love fest.

 

Let me know if this was helpful or any parts unclear.  It’s a long post and I may have glossed over some details here and there. It is such a long post I also attached it in PDF format.

Managing data motion during your deployments (Part 2)

This post finishes off a 2 part post. Sorry about the delay getting this second part out. I went on a vacation back in May and I am just now getting caught up.


In Part 1 we reviewed how to use the pre and post deployment scripts to manage data motion required to deploy some schema changes. In Part 2 we will look into how to implement your data motion scripts to only run when necessary. 

 

You typically will not want your data motion scripts to run on every deployment.  There could be various reasons including: the data motion is a one-time upgrade; a script may only be appropriate for a specific target; the script may only be applicable for a specific version of a database schema, etc.  Scripts in the pre and post deployment scripts of VSTS: DB are retained in the project after deployment and are checked into SCC code control.  These scripts continue to be a part of the project unless the developer removes them from the code base.  If you are maintaining multiple versions of an application you would want to keep these scripts to enable upgrade of earlier versions of the database schema.   Also, data motion scripts, while usually not reusable across versions of a schema, can be useful as a template if similar or same objects change in later versions of the database schema.

 

To enable scripts to only run when needed requires the developer to choose a strategy of how to detect when scripts are needed.  There are many out there, but there are a few that are common with others being a variant of these:

·         Expected schema conditions;

·         Database version history table;

·         Database version stamp;

·         and combinations of the previous.

 

At the heart of these strategies is the goal to make data motion scripts be idempotent or simply re--runnable. To keep these scripts in the project and a part of every database deployment they must be re-runnable.  If you have scripts that are one-shot DDL/DML scripts I would still enable them to be re-runnable since you will likely run the script multiple times during testing of trial deployments and database upgrade tests.  You may not choose to keep all scripts around for the life of the project in the latest source, but experience has taught me that a little up front work to enable scripts to be re-runnable will pay dividends before the end of the project.

 

Expected schema conditions is probably the most common strategy and most seasoned database developers include this as a standard part of their DDL scripts.  It’s just good practice and improves the reusability of scripts and achieves re-runnability.  This strategy is checking for an expected state of schema. If schema is in an expected state the script runs.  If the state of the schema is not in the expected state the script does nothing. I have sometimes heard database developers call these scripts non-destructible or non-invasive. I would steer clear of that terminology when talking about databases (especially when speaking to customers :)).  Using our example from Part 1 here is the pre-deployment data motion script that employs the Expected Schema Condition:

 

Note: You would need to make similar modifications in the post deployment script, but instead of looking for the existence of the column you check to see the xxxDATAMOTOxxxCustomersTable exists.

 

PRINT 'STARTING PREDEPLOYMENT SCRIPT...'

SET NOCOUNT ON

--Add IsKeyCustomer Column to Customer Table. Required version 2->3 upgrade.

IF NOT EXISTS(

      SELECT 1 FROM [sys].[syscolumns]

            WHERE [sys].[syscolumns].[id] = OBJECT_ID('[Customer].[Customers]')

                  AND [sys].[syscolumns].[name] = 'IsKeyCustomer')

BEGIN

      PRINT 'STARTING ADDIsKeyCustomer - PREDATAMOTO_CUSTOMERS_TABLE'  

 

Database version history tables are pretty common as well.  They became very common when it showed up as a part of the Adventure Works sample database SQL Server shipped back in 2000 (dbo.AWBuildVersion). The Database Version History strategy is an approach you use to record that a script has run or the database schema is of a particular version.  This approach typically includes a table that is persisted as a part of the maintained database schema and upgrade scripts that will query this table to determine if they need to run. When they do run they update the table with a new version or add a record that the script has been ran. These tables are usually very simple including only the bare essentials. Here is an example of a Database Version History Table.

 

CREATE TABLE [dbo].[DBBuildVersion](

      [Id] [tinyint]    IDENTITY(1,1) NOT NULL

CONSTRAINT PK_Id PRIMARY KEY,

      [DBversion]       [NVARCHAR](25) NOT NULL,

      [VersionDate]     [DATETIME] NOT NULL

CONSTRAINT DF_VersionDate DEFAULT GETDATE())

 

And here is our example using the database version history table:

 

PRINT 'STARTING PREDEPLOYMENT SCRIPT...'

SET NOCOUNT ON

--Add IsKeyCustomer Column to Customer Table. Required version 2->3 upgrade.

SELECT 1 FROM [dbo].[DBBuildVersion]

      WHERE [VersionDate] =

            (SELECT MAX([VersionDate]) FROM [dbo].[DBBuildVersion])

                  AND RTRIM(LTRIM([DBVersion])) = '2'

BEGIN

      PRINT 'STARTING ADDIsKeyCustomer - PREDATAMOTO_CUSTOMERS_TABLE'  

 

At the end of your deployment, in the last statement of your post deployment script, you add the new version to the DBBuildVersion table.

 

INSERT [dbo].[DBBuildVersion](DBversion)VALUES(3)

GO

 

Database version stamping is not very common, but is gaining popularity. This strategy includes objects as a part of the database schema that indicates the version of the database schema. Like the database version history table, the version stamp indicates the current version of the database schema, but instead of the version being persisted as data, it is persisted as an object. The object can be a Stored Procedure that returns the version of the database from a literal, a View that provides version history from a SELECT statement of literals, CLR object that reads information from registry, Extended Properties of the database or database objects. There are many ways you can implement this approach. This approach typically includes an object that will provide the current version of the database, maintained as a part of the database schema, and upgrade scripts that will query this version to determine if they need to run. Unlike the database version history table they do not persist any data to the database once an upgrade script has ran.  Here is an example of the database version stamp using an Extended Property.

 

Added CustomerDB.extendedproperties.sql to project which contains:

 

EXEC sp_addextendedproperty @name='DBBuildVersion',

    @value ='3',

    @level0type = NULL,

    @level0name = NULL,

    @level1type = NULL,

    @level1name = NULL,

    @level2type = NULL,

    @level2name = NULL

 

See how I specified ‘3’ as the value. Deployment will actually update the extended property with what is defined in the project during deployment if you are not ignoring extended properties.  It’s important to understand this since by the time the post deployment script executes the version will be what is defined in the project and not the original version value in the database. This means that in your post deployment script you use the state changed by the pre-deployment script to determine if your post deployment action needs to run.  This is actually a good thing since the post deployment action is dependent on the pre deployment action. Alternatively, you can manage the extended property and its value in the pre/post deployment scripts and have the targets original version value around until the very end.  I have another post that coming that uses an approach where the version value actually comes from the build process and not the project.

 

And here is our example using the database version extended property:

 

PRINT 'STARTING PREDEPLOYMENT SCRIPT...'

SET NOCOUNT ON

--Required for version 2->3 upgrade

IF EXISTS(

      SELECT 1 FROM sys.extended_properties

            WHERE class_desc = 'DATABASE'

                  AND name = 'DBBuildVersion'

                        AND (value = '2' OR value ='1'))

BEGIN

      PRINT 'STARTING ADDIsKeyCustomer - PREDATAMOTO_CUSTOMERS_TABLE'  

 

BTW, I know the table value function fn_listextendedproperty exists. I prefer to use the sys.extended_properties table. I am hoping the SQL Team adds T-SQL support for extended properties eventually. When I think about it the following just feels right.

 

CREATE PROPERTY 'DBBuildVersion' ON DATABASE WITH VALUE '?’

GO

SELECT * FROM DATABASE.PROPERTIES

GO

UPDATE DATABASE.PROPERTIES SET DBBuildVersion = ’3’

GO

 

Back to topic…

I have seen many customer implementations of data motion and upgrade scripts.  There are reasons why one strategy may be more appealing than another and each affords some benefits that the others do not.  I believe the strategies are most effective in combinations.  I often recommend customers consider the Expected Schema and Database Version Stamp strategies combination.  These 2 approaches work very well for teams using source code control and the database version is managed at design time.  This combination also does not rely on the existence of data which may or may not be there for a database target or might get stepped on by database users or other code running against the database. The last thing you want is the database history table to get tampered with and scripts run when you did not intend them to.  However, a database version history table can be useful for many things, but it should not be what ultimately controls the execution of your data motion or upgrade scripts. Using a persisted data to control execution actually will complicate your data motion scripts as you have to account for the non-existence of data.

 

Now, let’s update our example to use the Database Version Stamp and Expected Schema Conditions. First we will create our Extended Property in version 2 of the database schema hosted on my sandbox database instance.

 

EXEC sp_addextendedproperty @name='DBBuildVersion',

    @value ='2',

    @level0type = NULL,

    @level0name = NULL,

    @level1type = NULL,

    @level1name = NULL,

    @level2type = NULL,

    @level2name = NULL

GO

 

I also add the Extended Property to the database project like so:

 

[CustomerDB.extendedproperties.sql]

EXEC sp_addextendedproperty @name='DBBuildVersion',

    @value ='3',

    @level0type = NULL,

    @level0name = NULL,

    @level1type = NULL,

    @level1name = NULL,

    @level2type = NULL,

    @level2name = NULL

GO

 

I then update the pre and post deployment scripts to include expected schema conditions and version checks. In the pre-deployment script I move the version 3 upgrade into a new file in a sub-directory and include the file in the pre-deployment. These look like this:

 

[Script.PreDeployment.sql]

PRINT 'STARTING PREDEPLOYMENT SCRIPT...'

:r .\DataMotionUpgrades\Version3Upgrade.sql

PRINT 'PREDEPLOYMENT SCRIPT COMPLETE'

 

[.\Scripts\Pre-Deployment\DataMotionUpgrades\Version3Upgrade.sql]

--Version 3 Pre-Data Motion requred to upgrade version 1-2

IF EXISTS(

      SELECT 1 FROM sys.extended_properties

            WHERE class_desc = 'DATABASE'

                  AND name = 'DBBuildVersion'

                        AND (value = '2' OR value ='1'))   

BEGIN

      PRINT 'STARTING ADDIsKeyCustomer - PREDATAMOTO_CUSTOMERS_TABLE'  

      SET NOCOUNT ON

      IF NOT EXISTS(

            SELECT 1 FROM [sys].[syscolumns]

                  WHERE [sys].[syscolumns].[id] = OBJECT_ID('[Customer].[Customers]')

                        AND [sys].[syscolumns].[name] = 'IsKeyCustomer')

            AND EXISTS(

            SELECT * FROM [sys].[tables]

                  WHERE object_id  = OBJECT_ID('[Customer].[Customers]'))                      

      BEGIN

            IF (SELECT COUNT(*) FROM [Customer].[Customers]) > 0

            BEGIN

                  BEGIN TRY

                        BEGIN TRAN PREDATAMOTO_CUSTOMERS_TABLE

                              PRINT 'BACKING UP [Customer].[Customers]'

                              SELECT      [Id],[FName],[LName],[Email],[Created],[Updated],[Deleted]

                                    INTO [dbo].[v3xDATAMOTOxxxCustomers]

                              FROM [Customer].[Customers]

                              PRINT 'DELETING [Customer].[Customers]'

                              ALTER TABLE [Accounting].[CustomerInvoices] NOCHECK CONSTRAINT [FK_CustomerInvoices_Customer];

                              DELETE [Customer].[Customers]

                        COMMIT TRAN PREDATAMOTO_CUSTOMERS_TABLE;

                  END TRY

                  BEGIN CATCH

                        ROLLBACK TRAN PREDATAMOTO_CUSTOMERS_TABLE;

                        PRINT 'PREDATAMOTO_CUSTOMERS_TABLE TRANSACTION ROLLED BACK'

                        PRINT CAST(ERROR_NUMBER() AS CHAR(5)) + ERROR_MESSAGE()

                  END CATCH

            END

            ELSE

                  PRINT 'NO RECORDS TO WORRY ABOUT!!'

      END

      ELSE

            PRINT 'WARNING!!!!!: SCHEMA IS AN UNEXPECTED STATE FOR THIS UPGRADE SCRIPT AND DATABASE VERSION'

      SET NOCOUNT OFF

END

 

[Script.PostDeployment.sql]

PRINT 'STARTING POSTDEPLOYMENT SCRIPT...'

:r .\DataMotionUpgrades\Version3Upgrade.sql

PRINT 'POSTDEPLOYMENT SCRIPT COMLETE'

 

[.\Scripts\Post-Deployment\DataMotionUpgrades\Version3Upgrade.sql]

--Version 3 Pre-Data Motion requred to upgrade version 1-2

SET NOCOUNT ON

IF EXISTS(

      SELECT 1 FROM sys.extended_properties

            WHERE class_desc = 'DATABASE'

                  AND name = 'DBBuildVersion'

                        AND value ='3') 

BEGIN

      IF EXISTS(

            SELECT * FROM [sys].[tables]

                  WHERE object_id  = OBJECT_ID('[dbo].[v3xDATAMOTOxxxCustomers]'))

      BEGIN

      BEGIN TRAN POSTDATAMOTO_CUSTOMERS_TABLE;       

      BEGIN TRY

            PRINT 'RESTORING [Customer].[Customers]'

            IF (SELECT COUNT(*) FROM [dbo].[v3xDATAMOTOxxxCustomers]) > 0

            BEGIN

                  SET IDENTITY_INSERT [Customer].[Customers] ON

                  INSERT [Customer].[Customers]

                        ([Id],[FName],[LName],[Email],[IsKeyCustomer],[Created],[Updated],[Deleted])

                        SELECT

                              [Id],

                              [FName],

                              [LName],

                              [Email],

                                    CASE

                                          WHEN EXISTS

                                          (SELECT SUM(InvoiceTotal) FROM [Accounting].[CustomerInvoices]

                                                WHERE [Accounting].[CustomerInvoices].[CustomerId] =

                                                      [dbo].[v3xDATAMOTOxxxCustomers].[Id]

                                                            HAVING SUM(InvoiceTotal) >= 500000)

                                          THEN 1

                                          ELSE 0

                                    END,             

                              [Created],

                              [Updated],

                              [Deleted]

                        FROM [dbo].[v3xDATAMOTOxxxCustomers]

                              ORDER BY [dbo].[v3xDATAMOTOxxxCustomers].[Id]

                        SET IDENTITY_INSERT [Customer].[Customers] OFF

            END

            ELSE

            BEGIN

                        PRINT 'WARNING!!!!!: SCHEMA IS AN UNEXPECTED STATE FOR THIS UPGRADE SCRIPT'

            END

            PRINT 'DROPPING [dbo].[v3xDATAMOTOxxxCustomers]'           

            DROP TABLE [dbo].[v3xDATAMOTOxxxCustomers]                 

            COMMIT TRAN POSTDATAMOTO_CUSTOMERS_TABLE;

      END TRY

      BEGIN CATCH

            ROLLBACK TRAN POSTDATAMOTO_CUSTOMERS_TABLE;

            PRINT 'POSTDATAMOTO_CUSTOMERS_TABLE TRANSACTION ROLLED BACK'     

            PRINT CAST(ERROR_NUMBER() AS CHAR(5)) + ERROR_MESSAGE()

      END CATCH

      END

END

SET NOCOUNT OFF

 

Here is what the project system looks like now with these new pre and post deployment files:

Data Moto P2

 

Ok, I am ready now to test my updated data motion against a previous version of the database. To do this I pull previous version of the database project from source code control or from build drops if you have them around.  I do this for both version 2 and version 3 of the schema as described in a previous post.  I then deploy my latest database project against the previous versions of the database.   Here is the output from both deployments.

 

Deployment output of latest and greatest against version 2.  Our data motion runs…just what we want.

 

------ Deploy started: Project: CustomerDB, Configuration: Debug Any CPU ------

CustomerDB.dbschema(0,0)Warning TSD01269: The column [Customer].[Customers].[IsKeyCustomer] on table [Customer].[Customers] must be added, but the column has no default value and does not allow NULL values. If the table contains data, the ALTER script will not work. To avoid this issue, you must add a default value to the column or mark it as allowing NULL values.

    Deployment script generated to:

C:\Temp\Data Motion\CustomerDB\sql\debug\CustomerDB.sql

 

    STARTING PREDEPLOYMENT SCRIPT...

    STARTING ADDIsKeyCustomer - PREDATAMOTO_CUSTOMERS_TABLE

    BACKING UP [Customer].[Customers]

    DELETING [Customer].[Customers]

    PREDEPLOYMENT SCRIPT COMPLETE

    Dropping DBBuildVersion...

    Starting rebuilding table Customer.Customers...

    Caution: Changing any part of an object name could break scripts and stored procedures.

    Caution: Changing any part of an object name could break scripts and stored procedures.

    Creating DBBuildVersion...

    STARTING POSTDEPLOYMENT SCRIPT...

    RESTORING [Customer].[Customers]

    DROPPING [dbo].[v3xDATAMOTOxxxCustomers]

    POSTDEPLOYMENT SCRIPT COMLETE

========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ==========

========== Deploy: 1 succeeded, 0 failed, 0 skipped ==========

 

Deployment output against version 3.  No Mods.  Again, just what we want.

 

------ Deploy started: Project: CustomerDB, Configuration: Debug Any CPU ------

    Deployment script generated to:

C:\Temp\Data Motion\CustomerDB\sql\debug\CustomerDB.sql

 

    STARTING PREDEPLOYMENT SCRIPT...

    PREDEPLOYMENT SCRIPT COMPLETE

    STARTING POSTDEPLOYMENT SCRIPT...

    POSTDEPLOYMENT SCRIPT COMLETE

========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ==========

========== Deploy: 1 succeeded, 0 failed, 0 skipped ==========

 

Finally, I also need to test a clean deployment so I deploy to a new target. Here is the output.

 

------ Deploy started: Project: CustomerDB, Configuration: Debug Any CPU ------

    Deployment script generated to:

C:\Temp\Data Motion\CustomerDB\sql\debug\CustomerDB.sql

 

    Creating CustomerDB...

    STARTING PREDEPLOYMENT SCRIPT...

    PREDEPLOYMENT SCRIPT COMPLETE

    Creating Accounting...

    Creating Accounting.CustomerInvoices...

    Creating Customer...

    Creating Customer.Customers...

    STARTING POSTDEPLOYMENT SCRIPT...

    POSTDEPLOYMENT SCRIPT COMLETE

========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ==========

========== Deploy: 1 succeeded, 0 failed, 0 skipped ==========

 

As you can see, managing data motion takes up front planning and effort, but provides many benefits.  You can now deploy with more confidence and can keep your version upgrades scripts as a part of the latest source.  Deploying to earlier versions of your database is now a breeze. 

 

One thing I want to point out is that this is a very simple example.  More complicated schema changes against more sophisticated schemas work with the same approaches, but will take additional planning and consideration. I have gotten a lot of feedback and questions on the previous posts.  One question that I have gotten a few times is how to manage data motion when your schema changes involve tables with constraints between them and the modifications are to all tables within these dependencies.  This is where advance planning pays off.  For these types of deployments these approaches work just as well, but you will have more work to do upfront as the number of data motion and dependencies to manage during deployment increases.

 

In my next post I will show you how to setup your version stamp so that you can increment the version from a build process.

 

 

 

Posted by bahill | 2 Comments

Maintaining and synchronizing your reference data through the database project

For database application developers working on data driven applications their reference data is as important and meaningful as the database schema itself.  In fact, the database schema is incomplete when the reference data is absent.  When I say reference data, I am speaking of the non transactional data within the database.  This data goes by many different names in different organizations. Here are some of the more popular ones: Static Data, Domain Data, Seed Tables, Static Data, Meta Data, and Look Ups.

 

I have worked on many projects were we managed the reference data along with the rest of the application’s source code artifacts. It was always critical that these reference data were managed under the same configuration management process as the rest of the application.  The reasoning behind this was that I/we (depending on team and gig) typically implemented data driven applications with flexible data models. This often afforded the customer flexibility to comprehend business requirements down the road without expensive code modifications.  As a result, without the correct or appropriate reference data these applications would not be very functional or worse misbehave. I have also worked on projects where there was reference data, but in addition the application used hard coded values that controlled the execution of the application. Without predetermined reference data these applications would not run and often have runtime errors.  While not good design, I have seen it many times. This typically happens when apps go through a top down design, but I digress…

 

We often receive questions from the database development community as to how to best manage reference data.  Different strokes for different folks comes to mind, but I thought I would share an approach that works really well for average sized reference data tables. This approach is specific to SQL Server 2008, but can be modified to work with the previous versions of SQL Sever with some simple modifications.

 

Let's assume you have a table called products in your project like(Products.table.sql):

 

CREATE TABLE [dbo].[Products]

(

      Id INT IDENTITY(1,1) NOT NULL CONSTRAINT PkProductsId PRIMARY KEY,

      Name NVARCHAR(50) NOT NULL CONSTRAINT UqProductsName UNIQUE,

      Description NVARCHAR(250) NULL,

      Created DATETIME NOT NULL,

      Updated DATETIME NOT NULL,

      Deleted DATETIME NULL

     

)

 

Next, you then need a way to synchronize changes to this data. For this company we will sell beer. Guess where my mind is? So you create the following script file (SyncProducts.sql):

 

PRINT 'Starting [dbo].[Products] Syncronization'

GO

IF EXISTS(SELECT 1 FROM INFORMATION_SCHEMA.TABLES

                  WHERE TABLE_NAME = N'xxxSyncxxProducts')

      DROP TABLE [dbo].[xxxSyncxxProducts]

GO

CREATE TABLE [dbo].[xxxSyncxxProducts]

      (Name NVARCHAR(50) NOT NULL,

       Description NVARCHAR(250) NULL)

GO

SET NOCOUNT ON

INSERT [dbo].[xxxSyncxxProducts](Name, Description)

            VALUES           

                (N'Guinness', 'Irish-Style Stout')

               ,(N'Carlsberg','Danish Pilsner')

               ,(N'Scuttlebutt', 'India Pale Ale')

               ,(N'Corona','Mexican Lager')

             --,(N'Your', 'Favorite Beer')                             

SET NOCOUNT OFF

GO

MERGE [dbo].[Products] AS Target

USING [dbo].[xxxSyncxxProducts] AS Source ON (Target.Name = Source.Name)

            WHEN MATCHED

                        AND Target.Description <> Source.Description   

                  THEN

                        UPDATE     

                              SET   Target.Description = Source.Description,

                                    Target.Updated = GetDate(),

                                    Target.Deleted = NULL

            WHEN NOT MATCHED BY TARGET

                  THEN

                        INSERT      (Name, Description, Created, Updated)

                        VALUES      (Name, Description, GetDate(), GetDate())

            WHEN NOT MATCHED BY SOURCE THEN

                        UPDATE SET Target.Deleted = GetDate();                           

GO

DROP TABLE [dbo].[xxxSyncxxProducts];

GO

PRINT 'Done Syncronizing [dbo].[Products]'

GO

 

Finally, you then need a method to execute the synchronization as a part of deployment. This is done by including the script file in the post deployment script like so (Script.PostDeployment.sql):

 

:r .\RefDataSync\SyncProducts.sql

 

Here’s what it looks like when it is deployed.

 

  

 

During development, if marketing tells you they have a new beer to drink, I mean sell, you simply checkout the SyncProducts.sql file and make the necessary edits and check it back in. Every time you deploy your project, incremental changes to the database's reference data will be synchronized. also This includes fully populating the table when you deploy a new database. You can also run the script by the opening up the file in Visual Studio and clicking “Execute SQL”. This allows you to make changes out-of-band from the release yet still have the changes captured in SCC.

 

Note that I am using a logical delete approach which is a personal design preference .  You could also blast the records if you like, but then you have to worry about constraints that may get violated. The logical delete works well, because you can retire the row without deleting the data. This allows historical data to keep its relationship without denormalizing the table into an archive.

 

Let me know what you think about this approach. How could this be made easier and what is it missing?

Posted by bahill | 6 Comments
Filed under:

Managing data motion during your deployments (Part 1)

Continuing with the VSTS: DB Best Practices post series, let’s take a look at managing data motion in your deployments. This will be Part 1 of a 2 part post.  In the first post we will take a look at managing data motion necessary to make table schema modifications while hanging on to your existing data.  In Part 2 we will look at organizing your data motion scripts and managing data motion for projects where you may be maintaining multiple versions of a database schema.

 

There will be times when you deploy changes to your database that you will need to manage data motion to complete your deployment successfully.  For simple table changes VSTSDB handles the data motion for you.  An example of a simple change is when you add a new null-able column in the middle of a table.  When you deploy your database project against a database, a table with the modified schema is created in the database (CREATE), the data rows are copied from the old table to the new table (COPY), the old table is deleted from the database (DROP), and finally the table is renamed back to the original name (RENAME). This is called the CREATE-COPY-DROP-RENAME (CCDR) strategy.  Gert has a post from some time ago that reviews this strategy and why it is used. (I would also suggest the same post for folks needing a refresher as to how and why you use VIEWS to abstract tables.) The CCDR strategy is used when no other option is available to add a new column to the table. In SQL Server, the only column addition that can be made to an existing table without recreating the table is to add a column to the end of a table that is either NULLABLE, has a DEFAULT, or is of an IDENTITY or a TIMESTAMP column type.  While this covers the need to add a column tables in many scenarios, there are just as many or more that it does not. 

 

I don’t know about you, but back when I designed databases for a living the order of columns within a table was important to me.  It wouldn’t keep me up at night, but as I was developing an application I would never let the hurdle of reloading a table discourage me from reordering the columns within a table into nice sequence consistent across the rest of the database. Something about having the primary key be first column and mod fields the last few columns gives you a warm and fuzzy that all is well in the universe.

 

There are schema changes where data motion is the primary purpose of the change, for example, when a table is normalized resulting in the columns being split into 2 or more new tables.  While you want to minimize data motion on a database in “production” status as it can be risky and requires substantial resources to move large sets of data around, it tends to be inevitable process of database management. Due to the risk and complexity of data motion, well tested custom scripts is what is needed to complete the process with a high degree of confidence. 

 

We often receive questions about how this can be done using VSTSDB.  Customers often believe they have missed an elusive option hidden somewhere in the project system.  The fact of the matter is that data motion is challenging to automate and comprehend the intention of the user.  When a new non null-able column is added to a table there is often a motive for this change that entails specific logic or external data to populate the new values. This is best managed by the developer/DBA at the time the change is executed against the database.

 

With VSTSDB you manage data motion in the pre and post deployment scripts of your project. As you continue to use VSTSDB you will find the pre and post deployment scripts as indispensible to execute custom actions during the deployment process and data motion is a perfect example.  To manage data motion during deployment you add scripts to the pre deployment script to temporarily persist the data of the table that will be recreated into a temp table, table variable, or table in the database.  This allows the schema modifications to be made against the target table during  execution of the deployment plan without losing any data.  In the post deployment script you put the data back into the table after the schema modifications are complete and perform any necessary cleanup. That’s it in a nutshell.  Sounds simple, but there a handful of things to consider and each are unique from database to database. 

 

Before we get into an example of data motion, we should talk about the preventative measure enabled by default that protects your database against data loss during deployment.  The measure is a deployment option called the “Block incremental deployment if data loss might occur” and can be found in the Database.sqldeployment properties page as shown below.

 

 

This option will block changes made in deployment that would cause a possible data loss based on data motion, not necessarily schema changes.  That’s right, it does not block all data loss as some data loss is intentional. To illustrate this consider a changes made to our Customer.Customers table discussed in the last post <link>.  If you were to change the length of the Email column of the Customer.Customers table from 320 to 100 and the option is enable the change would be blocked in deployment. You will get an error message like:

X Rows were detected. The schema update is terminating because data loss might occur

========== Deploy: 0 succeeded, 1 failed, 0 skipped ==========

 

If you disable the option and deploy again you will get:

 

The type for column Email in table [Customer].[Customers] is currently  NVARCHAR (320) NOT NULL but is being changed to  NVARCHAR (100) NOT NULL. Data loss could occur

    Altering Customer.Customers...

========== Deploy: 1 succeeded, 0 failed, 0 skipped ==========

 

If you enable the option again and remove the Email column all together and deploy you will get (This assumes the UNIQUE Constraint on the Email column has been removed from the table in the database. If you only remove it from the project deployment will fail with a dependency failure. More about this in a forthcoming post):

 

    Altering Customer.Customers...

========== Deploy: 1 succeeded, 0 failed, 0 skipped ==========

 

So dropping a column from a table does not get blocked, but changes to modify the schema of the table that may introduce the risk of data loss to data that would exist after the deployment will get blocked.  Adding a NOT NULL column to the middle of the table will get blocked by the option because this change requires data motion since the table will have to be recreated.  What is import here is that you will usually want this option “ON” for your deployments, but you must comprehend this check when managing data motion.  Turing it “OFF” to deploy a schema change to only one table may be risky as it is possible that another change has snuck under the wire that may cause data loss. This option is for the whole deployment, not per object.

 

Let’s get into an example of data motion using our Customers.Customer table.  Let’s assume we are deploying version 3 of the schema against a database which currently has version 1 (the versioning of this schema is described in a previous post).  We are jumping to version 3 as deploying version 2 against version 1 does not require custom data motion scripts for the reasons noted above.  Version 1 of the Customer.Customers table looks like this:

 

CREATE TABLE [Customer].[Customers]

(

      [FName] NVARCHAR(50) NOT NULL,

      [LName] NVARCHAR(50) NOT NULL,

      [Email] NVARCHAR(320) NOT NULL,

      [Created] DATETIME NOT NULL DEFAULT GetDate(),

      [Updated] DATETIME NOT NULL DEFAULT GetDate(),

      [Deleted] DATETIME

);

 

Not let’s assume the target database contains existing customer data.  Since the new column, IsKeyCustomer, does not allow NULLs we will need to manage data motion so the deployment can be completed and the new Customer.Customers schema  effected.  To manage this the data motion we are going to start off by creating a script to make a copy of the existing data into a new table.  I prefer to use actual tables within transactions as opposed to table variables or temp tables in case deployment fails I can easily recover from the failure .  You could do recovery in script with temp tables, but that introduces more risk and is more effort than it is often worth. You may also need to disable any constraints that may depend on rows in the affected table.  Finally you will want to delete all rows from the original table so the  Block data loss.. option does not trigger a deployment failure.  I also tend to sprinkle a few PRINT statements around so we can see where we are in deployment later on.  We end up with a script that looks like:

 

PRINT 'STARTING PREDEPLOYMENT SCRIPT...'

SET NOCOUNT ON

BEGIN TRY

      BEGIN TRAN PREDATAMOTO_CUSTOMERS_TABLE

            PRINT 'BACKING UP [Customer].[Customers]'

            SELECT      [FName],[LName],[Email],[Created],[Updated],[Deleted]

                  INTO [dbo].[xxxDATAMOTOxxxCustomers]

            FROM [Customer].[Customers]

            PRINT 'DELETING [Customer].[Customers]'

            DELETE [Customer].[Customers]

      COMMIT TRAN PREDATAMOTO_CUSTOMERS_TABLE;

END TRY

BEGIN CATCH

      ROLLBACK TRAN PREDATAMOTO_CUSTOMERS_TABLE;

      PRINT 'PREDATAMOTO_CUSTOMERS_TABLE TRANSACTION ROLLED BACK'

      PRINT CAST(ERROR_NUMBER() AS CHAR(5)) + ERROR_MESSAGE()

END CATCH

SET NOCOUNT OFF

PRINT 'PREDEPLOYMENT SCRIPT COMPLETE'

 

We now have the pre deployment script that readies the table for schema modification.  Let’s put together the script to fix up the table after the deployment does its thing.  In the post deployment script we will simply put the data back into the Customer.Customers table, but also set the value of the new column with a sub query.  For our sample we will look up customers that are big spenders from the Accounting.CustomerInvoices table and use that to set the value of this new column on the customers table.  It should be based on paid invoices or payments received, but we are ok with just knowing the big spenders not necessarily the big payers. To wrap up the post deployment script we will clean up after ourselves by dropping our temporary storage. This leaves us with the script:

 

PRINT 'STARTING POSTDEPLOYMENT SCRIPT...'

SET NOCOUNT ON

BEGIN TRY

      BEGIN TRAN POSTDATAMOTO_CUSTOMERS_TABLE;

            PRINT 'RESTORING [Customer].[Customers]'

            INSERT [Customer].[Customers]

                  ([FName],[LName],[Email],[IsKeyCustomer],[Created],[Updated],[Deleted])

                  SELECT     

                        [FName],

                        [LName],

                        [Email],

                              CASE

                                    WHEN EXISTS

                                    (SELECT SUM(InvoiceTotal) FROM [Accounting].[CustomerInvoices]

                                          WHERE [Accounting].[CustomerInvoices].[CustomerEmail] =

                                                [dbo].[xxxDATAMOTOxxxCustomers].[Email]

                                                      HAVING SUM(InvoiceTotal) >= 500000)

                                     THEN 1

                                     ELSE 0

                              END,             

                        [Created],

                        [Updated],

                        [Deleted]

                  FROM [dbo].[xxxDATAMOTOxxxCustomers]

                        ORDER BY [dbo].[xxxDATAMOTOxxxCustomers].[Created]

            PRINT 'DROPPING [dbo].[xxxDATAMOTOxxxCustomers]'           

            DROP TABLE [dbo].[xxxDATAMOTOxxxCustomers]                 

      COMMIT TRAN POSTDATAMOTO_CUSTOMERS_TABLE;

END TRY

BEGIN CATCH

      ROLLBACK TRAN POSTDATAMOTO_CUSTOMERS_TABLE;

      PRINT 'POSTDATAMOTO_CUSTOMERS_TABLE TRANSACTION ROLLED BACK'     

      PRINT CAST(ERROR_NUMBER() AS CHAR(5)) + ERROR_MESSAGE()

END CATCH

SET NOCOUNT OFF

PRINT 'POSTDEPLOYMENT SCRIPT COMLETE'

 

We are now ready to deploy our project.  Below is the schema of the table we deployed and in the output window an indication of success.

 

 

We now have the database target containing version 3 of the Customer.Customers table. The table defined in your project and the target database now match.  The data is intact and we know who our key customers are.

 

Now let’s imagine the same scenario, but the Customer.Customers table is very large. It may have hundreds of thousands of rows and possibly even larger tables reference these rows through a foreign key constraint. The previous data motion approach may not be appropriate. An alternative would be to define the new non null-able column as the last column of the table and include a default to get it on to the table. The default will let you modify the table in place instead of having to recreate the table which would introduce the need for data motion. In the post deployment script you would only need to update the value of the new column and remove the default constraint on the table.  The downside to this approach is that you will need to go back and remove the unnecessary default constraint from your source code. To alter our data motion for approach for a very large table we would make the following  modifications:

 

With the Customer.Customers table in the project system, move the new columns to the end of the table and add a default constraint to the new non null-able column.

 

CREATE TABLE [Customer].[Customers]

(

      [FName] NVARCHAR(50) NOT NULL,

      [LName] NVARCHAR(50) NOT NULL,

      [Email] NVARCHAR(320) NOT NULL,

      [Created] DATETIME NOT NULL DEFAULT GetDate(),

      [Updated] DATETIME NOT NULL DEFAULT GetDate(),

      [Deleted] DATETIME,

      [Id]  INT NOT NULL IDENTITY(1,1)

            CONSTRAINT [PK_Customers] PRIMARY KEY,

      [IsKeyCustomer] BIT NOT NULL

            CONSTRAINT [DF_Customers_IsKeyCustomer] DEFAULT (0)

);

 

Remove the script from the pre deployment script file.  Update the post deployment script with the following script.

 

PRINT 'STARTING POSTDEPLOYMENT SCRIPT...'

SET NOCOUNT ON

BEGIN TRY

      BEGIN TRAN POSTDATAMOTO_CUSTOMERS_TABLE;

            PRINT 'UPDATE [Customer].[Customers].[IsKeyCustomer]'

            UPDATE [Customer].[Customers]

                  SET [IsKeyCustomer] =

                              CASE

                                    WHEN EXISTS

                                    (SELECT SUM(InvoiceTotal) FROM [Accounting].[CustomerInvoices]

                                          WHERE [Accounting].[CustomerInvoices].[CustomerEmail] =

                                                [Customer].[Customers].[Email]

                                                      HAVING SUM(InvoiceTotal) >= 500000)

                                     THEN 1

                                     ELSE 0

                              END

                  FROM [Customer].[Customers]

            PRINT 'DROPPING DEFAULT CONSTRAINT'      

            ALTER TABLE [Customer].[Customers]

                  DROP CONSTRAINT [DF_Customers_IsKeyCustomer]               

      COMMIT TRAN POSTDATAMOTO_CUSTOMERS_TABLE;

END TRY

BEGIN CATCH

      ROLLBACK TRAN POSTDATAMOTO_CUSTOMERS_TABLE;

      PRINT 'POSTDATAMOTO_CUSTOMERS_TABLE TRANSACTION ROLLED BACK'     

      PRINT CAST(ERROR_NUMBER() AS CHAR(5)) + ERROR_MESSAGE()

END CATCH

SET NOCOUNT OFF

PRINT 'POSTDEPLOYMENT SCRIPT COMLETE'

 

This is a good place to stop for part 1.  In part 2 we will look at how to organize your custom pre and post deployment scripts and configure them to execute only when necessary.  This is especially important if you are maintaining multiple versions of the same schema. 

 

 

Posted by bahill | 2 Comments

Right sizing the master.dbschema file for better design time performance

In the March edition of MSDN we provided an overview of Server Projects and how to reference the master.dbschema file to resolve references to system objects.  If you have a project that references the master.dbschema file, you may have noticed it takes a while to establish this reference. You may have also noticed the project may not load or deploy as quickly as it did before referencing the master.dbschema file.  This is because when referencing the master.dbschema file you are adding all the system objects found in a typical SQL Server instance to your database model in Visual Studio. The number of system objects defined in the master.dbschema file greatly outnumbers the user created objects for many database projects.  If you have multiple projects referencing the master.dbschema file this can be compounded as each projects reference to the master.dbschema has its own copy of the database model in memory. 

Master dbschema files do not contain objects you will deploy.  Their primary purpose is to resolve references to system objects and system catalogs. The dbschema file is actually a serialized version of the master database schema model.  It is an xml file that can be customized if you like.  Suppose you have a project that contains just a few stored procedures that reference an extended stored procedure.  You could reference one of the static master.dbschema files provided with the project, but that is a fair amount of overhead for just one extended stored procedure. You could also just suppress the warning to ignore the unresolved reference to the extended stored procedure, but this might not be ideal as you plan to work with the extended stored  procedure in many of your stored procedures and want it to be resolved.  Alternatively, you can customize your master.dbschema file to have only the system objects that are approved for referencing by the database development team. 

To customize the master dbschema file you simply remove the elements you do not need or reference in your project. Let's assume that our project only references the extended stored procedures that deal with extended properties. These include fn_listextendedproperty, sp_addextendedproperty, sp_dropextendedproperty and sp_updateextendedproperty.  You want to customize the master.dbschema file to include only these objects.

To accomplish this you:

  1. Make a copy of the master.dbschema. Be sure to copy the right version to match the version of SQL Server you are targeting in your project.
  2. Open the master.dbschema file with Visual Studio. Rename it to master.dbschema.txt before you edit it so VS will not parse the whole xml. This will speed up the edit process greatly.
  3. Copy the elements of each of the objects you want to keep on to the clipboard or another file.
  4. Remove all the elements within the model element.
  5. Add the selected elements back to your master.dbschema within the model element.
  6. Save the file. Name the file something other than master.dbschema so your team can easily recognize that it is the customized file and not the original.
  7. Add the customized file to SCC so the rest of the team can reference the same file. A solution folder is a good way to go about this.
  8. Remove the reference from your project(s) to the original master.dbschema.
  9. Add a new reference to your project for your customized master.dbschema.

Establishing the reference to your customized master.dbschema now only takes seconds. Loading your project should also be a little quicker now that the full model of the master.dbschema is not loaded each time for each referencing database project. In this example we have taken the master.dbschema file down from 8 MBs to 20k. This is roughly 2000 database objects down to the 4 we selected for our example.

Customized master.dbschema file in VS Editor 

The customized master.dbschema from the example is attached.

Posted by bahill | 6 Comments
Filed under:

Attachment(s): onlyextprops.master.dbschema

GDR doesn't validate what!?

Up to the release of Visual Studio Team System 2008 Database Edition GDR a design database was used to store and validate the database project's schema model.  The design database was dynamically created when you opened a database project (.dbroj).  The project system stored the schema model in this design database, 2005 SQL Express by default, behind the project system.  As you versioned your database schema, changes were validated against this live database instance and any errors would be reported immediately through the error list in Visual Studio. For example, if you tried to use a variable that was not defined you would get an error immediately in the error list. Here is an example of this error in Visual Studio 2008 Database Edition.

With the release of the GDR the product no longer uses a live design database to store and validate the database schema model from the project system.  Instead, the database schema model is stored in a SQL CE database and validation is performed by the product's validation engine against an in memory model of the database schema.  This is a substantial change in the product's architecture and enables many scenarios not possible with the previous architecture. Database schema models are implemented by Database Schema Providers (DSP) and are extensible. So if you are working in a SQL Server environment you may be using a SQL100 DSP or if you are working in a Oracle environment you may be using ORA11 DSP. This allows DSP models to be added and serviced without requiring a major release for the development environment. Moreover, the model can now be compiled into .dbschema file and used by other project features and shared with other development teams.

In this model based architecture you are working in a completely disconnected database development environment. Connection to a live database from the project system is limited and only available when you are intending to import, compare, or deploy changes to a target database instance. As you develop your database schema, validation is ran interactively based on changes occurring through the project system, on idle while you are not actively making schema changes, or depending on the nature of the validation to performed, at build time to make sure the model is fully validated. Schema validation in the GDR is not as granular as the previous architecture. Validating the schema is a resource intensive process and so in the GDR it is focused on validating all top level objects, any references between objects, and most dependencies between objects and their composing children objects.  There are additional validations that run to catch common design time mistakes, but not all mistakes. The most significant difference between the GDR and previous version in terms of validation is the lack of validation of programmability object bodies (functions and stored procedures). Within the body of programmability objects validation is kept to minimum to not impact the performance of the developer's interactive user experience. Most of validation that is performed within the bodies of statements is to insure all objects referenced and their children can be fully resolved. The code is parsed and interpreted, but not everything is validated 100%. In fact, our example above would not be caught at design time, but at deploy time. Here is a deployment failure of the same issue:

Given this change, you will want to do trial deployments to ensure errors that can only be found at deployment are caught before making production deployments. Typically you will do this many times during development as you implement unit tests for your database schema. Deploying to a sandbox database will help you build confidence in deployments to other environments. This is also done in many environments during integration. The integration database is created or updated at the end of each day or after check-ins trigger build and deploy when you have implemented continuous integration.  You can also implement a custom static code analysis rule to check for undefined variables at design time if you wish. They key is to deploy early and often to validate your deployments.

Thanks to Gokhan Caglar of the VSTSDB team for his contributions to this post.

Posted by bahill | 1 Comments
Filed under:

Deployment Failure: "An item with the same key has already been added"

We are receiving reports from customers that they are encountering a deployment failure of their database projects using the GDR release. This issue typically occurs when the project is copied to a new location on disk or when the solution configuration is changed with a specific sequence.  This deployment issue is caused by a bug we currently have in the product that will create duplicate references to .dbschema files.  The deployment error will look similar to:

 

The specific error that will be emitted during deployment caused by this issue is:
Error MSB4018: The "SqlDeployTask" task failed unexpectedly. System.ArgumentException: An item with the same key has already been added

 

The bug will be resolved in the upcoming GDR service release. Until then, you can work around this issue using the following steps below.

 

Use these steps when referencing a .dbschema file:
1. Close or unload project.
2. Edit project file and update the include and hint path to use the same relative path.
3. Delete .dbmdl file in the root of the project folder.
4. Open or reload project.
5. Clean project/solution or delete files in output and obj directories.
6. Build and Deploy.

 

Use these steps when referencing a Server Project:
1. Close or unload project.
2. Delete .dbmdl file (for each project).
3. Open or reload project.
4. Clean project/solution or delete files in output and obj directories.
5. Build and Deploy
 

Posted by bahill | 3 Comments
Filed under:

March MSDN - Introducing new features in the VSTSDB GDR

Jamie Laflen and I wrote an article for this month's issue of MSDN magazine. The article briefly introduces most of the new features of the VSTSDB GDR release. The article covers the following features:
  • Offline Schema Development
  • Product Architectural Changes
  • Server Projects and Server Project References
  • Project Upgrade
  • Database Project Build and Deploy
  • VSDBCmd.exe - Deployment Command-Line Facility
  • SQL CLR Project Support
  • and more...

Article: Introducing New Features In The VSTS Database Edition GDR

Posted by bahill | 1 Comments
Filed under:

Offline Schema Development

Continuing with the Best Practices post series, let's take a look at Offline Schema Development to set the stage for future topics. This posting is for DBAs and Database Application Developers that are not using the VSTSDB or have just started using VSTSDB. For those that are already using VSTSDB to manage their database development there also may be something in here for you as well.

Historically, database development follows a different process than other software development processes in many organizations. For example, when most of you have developed a database you make changes directly to a live development database and (hopefully) capture those changes either in script or in a document for later reference; the website that uses the database is iterated on by the ASP.NET developers with the source being checked into some source code control system.  Once the website is ready to move to a QA environment it is built and xcopy deployed to the QA environment.  The database changes must also be migrated so the DBA or developer uses the script they incrementally created or creates the change script based on the changes defined in their change document and begins stepping through all the changes to update the QA environment's database. The development and xcopy deployment of the website application are in sharp contrast to the manual approach taken to update the database. 

The differences in development and deployment techniques between application and database development is largely attributed to the existence of data and operational state that inherently resides in all live databases. The operational state includes configuration of the database, database schema, security settings, permissions and data. To change the operational state of the database, some knowledge of the existing state is required and the change is often additive in nature. The existence of data complicates the database change process because the data is often migrated, transformed, or reloaded when changes are introduced by application development efforts that affect the shape of the database's tables. Throughout this change process, production quality data and operational state must be protected against changes that may jeopardize its integrity, value, and usefulness to the organization.

Given the constraints that data introduces, the need to manage database state in general, and specific tools required to effect database change, organizations often struggle with integrating database development processes into their larger Application Lifecycle Management (ALM) strategies. This has given rise to segmented and often fragmented application development workflow processes which ultimately reduce the organization's collaboration, efficiency and agility.

These fragmented processes typically originate in the area of Software Configuration Management (SCM). If you compare database development and application development SCM practices, the key difference is the existence of state that must be managed for databases. To manage this state, change scripts must be developed that not only identify the desired schema and state of the database, but also any modifications and transformations necessary to move the database (and its data) from the existing state to the new state at deployment or release. Conversely, for application development in general, there is usually not a significant amount of existing state and the application is often simply replaced with the new version at the time of release.

Visual Studio Team System 2008 Database Edition (VSTSDB) provides tools necessary to effectively manage database change in a manner which easily integrates into their organization's existing SCM processes. VSTSDB provides a disconnected, declarative database development environment based on a model representation of the database where the source code defines the database, options, and schema. T-SQL script (source code) in VSTSDB projects is the primary artifact and is managed and versioned throughout the development cycle in the same respect as artifacts for other Visual Studio Projects. This is Offline Schema Development and enables a repeatable, flexible, process driven Database Development Life Cycle (DDLC) process.

Adoption of VSTSDB may require a mental shift (old habits are hard to break) for some developers or at least a change in process or workflows. Developers who have developed database applications where the production database represents the current version of the database will need to adopt a source code based approach where source code becomes the vehicle to which change is made to databases. In VSTSDB, the project and the source code is the "One Version of the Truth" for the database schema and is managed using SCM workflows likely already being used by the developer or organization for other portions of their application stack. For the data, the production database remains its "One Version of the Truth" as it should be.

Offline schema development is an approach to database development that focuses on creating and maintaining the database using source code in an environment that is disconnected from an actual database. The source code is developed without defining the implementation details of modifying an existing database. Instead, the schema is declared in source code and the source code is versioned throughout the development lifecycle. The database schema is modeled in the project system and includes the data object structures like tables and views as well as programmability objects such as stored procedures and functions.

Side Note: VSTSDB comprehends almost all objects and syntax.  Very few objects and syntax only meaningful to the database engine at runtime have been excluded.  

VSTSDB enables an Offline Schema Development environment by providing:

  • A database project (.dbproj) designed to manage database development;
  • Declarative T-SQL syntax support where the schema is defined in the domain language of the database;
  • Schema model representation of the database schema where source can be round tripped from source to model and back again;
  • Schema model interfaces providing programmatic interaction to database tooling and designers;
  • Interpretation and validation of T-SQL syntax and schema dependencies ensuring integrity of the source without executing it against a database;
  • Schema comparison which compares your source with a target database to generate an update script;
  • Compiled versions of database schema enabling deferred deployment and script generation(.dbschema file);
  • .dbschema files can be deployed to different environments with different configurations.

Ok, so enough with the big words and fancy bullets... Let's walk through a scenario that will hopefully demonstrate the points above.  In this scenario we will track a multitier application over 3 development iterations/release cycles without using VSTSDB.  In this scenario, a simple class represents the application tier and a table represents the data tier.

  • In version 1 the application developer (APPDEV) defines a simple class called Customer. The database developer (DBDEV) defines a simple table called Customer. Life is good.
  • In version 2, the team has realized that they will very likely have more than one customer named Tom Smith (Hey Tom! ;) ) and decide that a means to uniquely identify a customer would be a valuable addition. The APPDEV adds an Id member to their class. The DBDEV adds Id column and primary key to the table. This is where things start getting interesting. While the APPDEV can simply update their class and check it in, the DBDEV must write alter statements to add the new column and primary key. The DBDEVs code no longer represents the true shape of the Customer table. The true shape of the Customer table exists in the current development DB and is possibly spread over two scripts: Some in the original table create script and some in the database update script. The diligent DBDEV makes the changes checks the new file in. Side Note: For simplicity reasons in this scenario we will disregard data motion required to push the change out to the database. That's another post. ;)
  • By the time we hit version 3, business is good and management has rewarded the team with new dev boxes, lcds and shiny new tools. The team needs to differentiate their customers and decides to add a property to their customers to identify the customers they want to keep and expand business with. To implement this the APPDEV adds a new member to their class called IsKeyCustomer and checks in. The DBDEV adds another file and writes another alter statement to add the new column to the Customer table. The DBDEV updates the development database and the change script and checks in.

At the end of version 3 we end up with project artifacts that look similar to the following:

Customer.cs

Customer.table.sql

Take a look at the change script.  The change script are all the changes together in one SQL file. Change scripts typically validate an expected schema condition before each change.  You will often need data motion scripts before some changes can be completed on tables with eisting data.

There are a number of problems with maintaining a schema change script: It's a manual and time consuming; error prone; complexity increases as time goes on and number of version changes grow; development team agility increases the maintenance cost of change script; it's really not a scalable process. Moreover, you must insatiate your database schema by running the full set of scripts against a live database to view or reference your objects.  This introduces a dependency on the development database as the "truth" of the databases schema instead of the source code. This also requires making changes twice, once in the dev db and again in the change script.

Now let's go through the same scenario using VSTSDB.  There are no changes in behavior for the APPDEV so I will only call out the actions of the DBDEV.

  1. In version 1 the DBDEV defines a simple table called Customer2.
  2. In version 2 the DBDEV adds the Id column and primary key to the Customer Table definition. Life is grand!
  3. In version 3 the DBDEV adds the IsKeyCustomer column to the Customer table definition and openly mocks the APPDEV while checking in the change. ;)

At the end of version 3 we end up with database source that looks similar to the following:

Customer.table.sql

The DBDEV defines the shape of the object for the version of the application, not how to mutate the existing object in the database engine to the desired shape. You are probably thinking: yeah, rightThat will never deploy!  This is where the deployment engine comes in to play.  As mentioned previously, the deployment engine will take the compiled version of your schema and compare it against a database deployment target.  The differencing engine will produce the necessary DDL scripts to update the target schema to match the version you are deploying from the project.  Let's look at the example update scripts below using our previous scenario. 

When deploying to an empty database the deployment engine will produce the full script below (excerpt provided).

When deploying to an existing version of the database, let's say a production environment running version 1.0, it will produce the update script below.

You can deploy your project's schema to multiple databases with different schema versions to integrate the schema defined in your project. This is extremely powerful.  The graphic below depicts what happens at the time of deployment.

  

At the time of deployment the schema model defined by your project and the schema model defined by the target database are compared. The deployment engine then creates a deployment plan based on the difference of the model comparison. The deployment plan is final executed against the target database or the deployment plan is deployed to script. This demonstrates how the target database change script creation is deferred to the time of deployment.  This enables you to build your schema once and deploy it many times.  The source controlled version of your project represents the truth of the database schema and the "compiled" .dbschema file is a blueprint of your schema at a specific point in time.  The .dbschema file is also mobile can be included in the application release payload and deployed along with the rest of the application stack. Very cool!

It is recommended that you test your deployments during development and integration.  Testing the deployment ahead of time will provide you with an understanding of what the deployment engine will do against a target database instance.  An easy way to do this is to deploy to script, review, and then run it against a test environment.  You can also test deploying your project against a test database instance and schema compare the test instance with production or development.  Testing ahead of time will also provide you the confidence needed to eventually automate the execution of your deployments.  If you have multiple environments such as: DEV, TEST, INT, ACCEPT, PPE, or PROD, you will want to deploy to these environments to mimic the promotion of schema deployments before an actual deployment to production.

I hope this is helpful and provides a solid foundation for the future topics in the Best Practices series. Next time we will take a look at Data Motion which will be a nice segue given this overview.  We will dig into that topic and use our simple Customer Database exercise to demonstrate how to deploy schema changes when the target database has existing data and tables will be modified during the deployment process. Until next time, let me know what you think about this and any areas that you believe need to be addressed further.

Thanks to Jamie Laflen and Genevieve Orchard of the VSTSDB team for their contributions and help on finishing up this post.

Posted by bahill | 3 Comments
Filed under:

Deserialization has failed for project...

The Visual Studio Team System 2008 Database Edition GDR introduced a new model based architecture that removes the product's dependency on a design db. In previous versions of the product a local instance of the SQL Server was required.  This local SQL Server instance would provide the design db for the project system and within this design db is where most of the validation was performed.  The GDR version provides a true model representation of the database schema and validation works on top of the model  and not in a physical database instance.  The model is stored in a SQL CE database while the project is open.  When the project is closed the model database is serialized to a file in the root of the project system called <ProjectName>.dbmdl. When the project is reopened the model is rehydrated from serialized copy.  This speeds up opening database projects, especially when the database schema in your project is large.
 
When you open your database project you will see in the output window when the model  is deserialized.  This will look like:

...

Deserializing the project state for project 'MYPROJECT.dbproj'...

Detecting file changes for project 'MYPROJECT.dbproj'...

Deserialization has been completed for project 'MYPROJECT.dbproj'.

...

Deserialization may fail if the project is not shut down properly, you don’t have enough disk space, or another program prevents the file from being serialized properly at the time the project was previously closed. If it does fail you may notice the following in your output window:

...

Deserializing the project state for project 'MYPROJECT.dbproj'...

Deserialization has failed for project ‘MYPROJECT.dbproj’. Project ‘MYPROJECT.dbproj’ will be reanalyzed.

Deserializing the project state for project 'MYPROJECT.dbproj'.

...

If this happens it can take some time for the project to be reanalyzed and for the model to be recreated for large projects. 

The first place to look to resolve the issue are indexing services and antivirus programs on your local computer. You want to prevent these programs from holding locks on the .dbmdl files when the model is being serialized to disk. I have found that excluding the directory where the project is stored from indexing services usually solves the issue.

Posted by bahill | 1 Comments
Filed under:
More Posts Next page »
 
Page view tracker