Walkthrough: Using a Custom Action to Create a Database at Installation

Switch View :
ScriptFree
Visual Studio 2010
Walkthrough: Using a Custom Action to Create a Database at Installation

Updated: August 2011

The following walkthrough demonstrates the use of a custom action and the CustomActionData property to create a database and database table during installation.

NoteNote

This walkthrough requires SQL Server on the computer where you will deploy the application.

NoteNote

Your computer might show different names or locations for some of the Visual Studio user interface elements in the following instructions. The Visual Studio edition that you have and the settings that you use determine these elements. For more information, see Visual Studio Settings.

NoteNote

The following instructions demonstrate how to create a deployment project by using a Visual Basic project; the general principles apply to all Visual Studio language projects that support deploying Windows-based applications.

To create an installer class

  1. On the File menu, click New Project.

  2. In the New Project dialog box, select Visual Basic Projects in the Project Type pane, and then select Class Library in the Templates pane. In the Name box, type DBCustomAction.

  3. On the Project menu, click Add New Item.

  4. In the Add New Item dialog box, select Installer Class. In the Name box, type VbDeployInstaller.vb.

To create a data connection object

  1. In Server Explorer, select Data Connections. Right-click and click Add Connection.

  2. In the Choose Data Source dialog box, select Microsoft SQL Server.

  3. In the Add Connection dialog box, do the following:

    1. In the Server name list, type or select a server name.

    2. Select Use Windows Authentication.

    3. In the database box, type master.

    4. Click OK to close the dialog box.

  4. From the Data menu, click Add New Data Source, and then use the wizard to add the connection that you established in the previous steps. To verify that the data source is in the project, click Show Data Sources on the Data menu.

To create a text file that contains a SQL statement to create a database

  1. In Solution Explorer, select the DBCustomAction project. On the Project menu, click Add New Item.

  2. In the Add New Item dialog box, click Text File. In the Name box, type sql.txt (must be in lowercase).

  3. Add the following to the sql.txt file:

    CREATE TABLE [dbo].[Employees] (
    [Name] [char] (30) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
    [Rsvp] [int] NULL ,
    [Requests] [nvarchar] (4000) COLLATE SQL_Latin1_General_CP1_CI_AS NULL 
    ) ON [PRIMARY];
    
    ALTER TABLE [dbo].[Employees] WITH NOCHECK ADD 
    CONSTRAINT [PK_Employees] PRIMARY KEY CLUSTERED 
    (
    [Name]
    ) ON [PRIMARY];
    
  4. In Solution Explorer, select sql.txt. In the Properties window, set the BuildAction property to Embedded Resource.

To add code to the installer class to read the text file

  1. In Solution Explorer, select VbDeployInstaller.vb. On the View menu, click Code.

  2. Add the following Imports statement at the top of the module:

    Visual Basic
    
    Imports System.IO
    Imports System.Reflection
    Imports System.Data.SqlClient
    
    
    
    C#
    using System.IO;
    using System.Reflection;
    using System.Data.SqlClient;
    
  3. Add the following code to the VbDeployInstaller class, after the declaration of MyBase.New:

    Visual Basic
    
    Public Class VbDeployInstaller
    
        Dim masterConnection As New System.Data.SqlClient.SqlConnection
    
        Public Sub New()
            MyBase.New()
    
            'This call is required by the Component Designer.
            InitializeComponent()
    
            'Add initialization code after the call to InitializeComponent
    
        End Sub
    
        Private Function GetSql(ByVal Name As String) As String
            Try
    
                ' Gets the current assembly.
                Dim Asm As [Assembly] = [Assembly].GetExecutingAssembly()
    
                ' Resources are named using a fully qualified name.
                Dim strm As Stream = Asm.GetManifestResourceStream(
                  Asm.GetName().Name + "." + Name)
    
                ' Reads the contents of the embedded file.
                Dim reader As StreamReader = New StreamReader(strm)
                Return reader.ReadToEnd()
    
            Catch ex As Exception
                MsgBox("In GetSQL: " & ex.Message)
                Throw ex
            End Try
        End Function
    
        Private Sub ExecuteSql(ByVal DatabaseName As String, ByVal Sql As String)
            Dim Command As New SqlClient.SqlCommand(Sql, masterConnection)
    
            ' Initialize the connection, open it, and set it to the "master" database
            masterConnection.ConnectionString = My.Settings.masterConnectionString
            Command.Connection.Open()
            Command.Connection.ChangeDatabase(DatabaseName)
            Try
                Command.ExecuteNonQuery()
            Finally
                ' Closing the connection should be done in a Finally block
                Command.Connection.Close()
            End Try
        End Sub
    
        Protected Sub AddDBTable(ByVal strDBName As String)
            Try
                ' Creates the database.
                ExecuteSql("master", "CREATE DATABASE " + strDBName)
    
                ' Creates the tables.
                ExecuteSql(strDBName, GetSql("sql.txt"))
    
            Catch ex As Exception
                ' Reports any errors and abort.
                MsgBox("In exception handler: " & ex.Message)
                Throw ex
            End Try
        End Sub
    
        Public Overrides Sub Install(
            ByVal stateSaver As System.Collections.IDictionary)
    
            MyBase.Install(stateSaver)
            AddDBTable(Me.Context.Parameters.Item("dbname"))
        End Sub
    
    End Class
    
    
    
    C#
    public class VbDeployInstaller
    {
    
    
    System.Data.SqlClient.SqlConnection masterConnection = new System.Data.SqlClient.SqlConnection();
    public VbDeployInstaller() : base()
    {
    
    //This call is required by the Component Designer.
    InitializeComponent();
    
    //Add initialization code after the call to InitializeComponent
    
    }
    
    private string GetSql(string Name)
    {
    
    try {
    // Gets the current assembly.
    Assembly Asm = Assembly.GetExecutingAssembly();
    
    // Resources are named using a fully qualified name.
    Stream strm = Asm.GetManifestResourceStream(Asm.GetName().Name + "." + Name);
    
    // Reads the contents of the embedded file.
    StreamReader reader = new StreamReader(strm);
    return reader.ReadToEnd();
    
    } catch (Exception ex) {
    Interaction.MsgBox("In GetSQL: " + ex.Message);
    throw ex;
    }
    }
    
    private void ExecuteSql(string DatabaseName, string Sql)
    {
    System.Data.SqlClient.SqlCommand Command = new System.Data.SqlClient.SqlCommand(Sql, masterConnection);
    
    // Initialize the connection, open it, and set it to the "master" database
    masterConnection.ConnectionString = My.Settings.masterConnectionString;
    Command.Connection.Open();
    Command.Connection.ChangeDatabase(DatabaseName);
    try {
    Command.ExecuteNonQuery();
    } finally {
    // Closing the connection should be done in a Finally block
    Command.Connection.Close();
    }
    }
    
    protected void AddDBTable(string strDBName)
    {
    try {
    // Creates the database.
    ExecuteSql("master", "CREATE DATABASE " + strDBName);
    
    // Creates the tables.
    ExecuteSql(strDBName, GetSql("sql.txt"));
    
    } catch (Exception ex) {
    // Reports any errors and abort.
    Interaction.MsgBox("In exception handler: " + ex.Message);
    throw ex;
    }
    }
    
    
    public override void Install(System.Collections.IDictionary stateSaver)
    {
    base.Install(stateSaver);
    AddDBTable(this.Context.Parameters.Item("dbname"));
    }
    
    }
    
  4. On the Build menu, click Build DBCustomAction.

To create a deployment project

  1. On the File menu, click Add, and then click New Project.

  2. In the Add New Project dialog box, open the Other Project Types node and select Setup and Deployment Projects in the Project Type pane. Then select Setup Project in the Templates pane. In the Name box, type DBCustomAction_Setup.

  3. In the Properties window, select the ProductName property and type DB Installer.

  4. In the File System Editor, select the Application Folder. On the Action menu, click Add, and then click Project Output.

  5. In the Add Project Output Group dialog box, select Primary output for the DBCustomAction project.

To create a custom installation dialog box

  1. Select the DBCustomAction_Setup project in Solution Explorer. On the View menu, point to Editor, and click User Interface.

  2. In the User Interface Editor, select the Start node under Install. On the Action menu, click Add Dialog.

  3. In the Add Dialog dialog box, select the Textboxes (A) dialog box, and then click OK.

  4. On the Action menu, click Move Up. Repeat until the Textboxes (A) dialog box is above the Installation Folder node.

  5. In the Properties window, select the BannerText property and type Specify Database Name.

  6. Select the BodyText property and type This dialog box enables you to specify the name of the database to be created on the database server.

  7. Select the Edit1Label property and type Name of database:.

  8. Select the Edit1Property property and type CUSTOMTEXTA1.

  9. Select the Edit2Visible, Edit3Visible, and Edit4Visible properties and set them to False.

To create a custom action

  1. Select the DBCustomAction_Setup project in Solution Explorer. On the View menu, point to Editor, and then click Custom Actions.

  2. In the Custom Actions Editor, select the Install node. On the Action menu, click Add Custom Action.

  3. In the Select item in project dialog box, double-click the Application Folder.

  4. Select Primary output from DBCustomAction (Active), then click OK to close the dialog box.

  5. Make sure that Primary output from DBCustomAction (Active) item is selected in the Custom Actions Editor. In the Properties window, select the CustomActionData property and type /dbname=[CUSTOMTEXTA1]. This data is passed to the custom action that is run at the end of the installation. For more information, see CustomActionData Property.

  6. On the Build menu, click Build DBCustomAction_Setup.

To install the application on your development computer

  • Select the DBCustomAction_Setup project in Solution Explorer. On the Project menu, click Install.

    This will run the installer on your development computer.

    NoteNote

    You must have install permissions on the computer in order to run the installer.

To deploy the application to another computer

  1. In Windows Explorer, navigate to your project directory and find the built installer. The default path will be \documents and settings\yourloginname\DBCustomAction_Setup\project configuration\ DBCustomAction_Setup.msi. The default project configuration is Debug.

  2. Copy DBCustomAction_Setup.msi and all other files and subdirectories in the directory to another computer.

    NoteNote

    To install the application on a computer that is not on a network, copy the files to traditional media such as CD-ROM.

    On the target computer, double-click the Setup.exe file to run the installer.

    NoteNote

    You must have install permissions on the computer in order to run the installer.

To uninstall the application

  1. In Windows Control Panel, double-click Add or Remove Programs.

  2. In the Add or Remove Programs dialog box, select DBCustomAction_Setup, click Remove, and then click OK to close the dialog box.

    TipTip

    To uninstall the application from your development computer, on the Project menu, click Uninstall.

See Also

Reference

Concepts

Other Resources

Change History

Date

History

Reason

August 2011

Added C# code

Customer feedback.

Community Content

Barrie K
Don't use [SERVER] or [DATABASE] for your project properties!
I modified this installer slightly for my own purposes so that the user has to specify a Server Name as well as a Database Name.
In my innocence, I changed the  Edit1Property property to be SERVER and the Edit2Property property to be DATABASE (instead of the suggested CUSTOMTEXTA1).
Now I'm sure that some of you are throwing your hands up with cries of "idiot; you can't use DATABASE  because it is a reserved property!".
Anyway, if you do use these properties (I don't know which one is causing the issue) then the installation hangs: Actually the installer UI disappears and just leaves msiexec.exe hanging around in the process list.
If you call the properties SERVER_NAME and DATABASE_NAME instead, then all will be well.

SquireDude
Error on MasterConnection String

Hi I have a C# solution that I  am adding this solution for the Installer BUT I am getting an error when I try the first BUILD...

"master ConnectionString" is not a member  of "DBCustomAction.My.MySettings"

ALSO

A Warning on...

property "Events" shadows an ocerloadable member declared in the base class .MachByValueComponent'.  If you want to everload the base method, this method must be declared "Overloads".

Since it is just a copy and paste how could I have screwed such a simple thing up???


Jas_ghai
Error 1001. Incorrect syntax near 'DATABASE'
I am getting a error "Error 1001. Incorrect syntax near 'DATABASE'" while installing a DBCustomAction_Setup file....as I am writen a create and Insert commands for creating few tables and inserting one value each in tables in sql.txt file....kindly help me out

thewhotwo
Error creating DB
Hello there.
I am following your tutorial but I am having problems.
I am getting an error: Cannot open database <dbname> requested by login sa and the database is not created.
if I login using sa I can create the database and if I run the installer it detects that the database is already created which means that the login credentials is working.
any ideas?

Kalle Johansson
Concerning C# code
By all means, include the contributed C# code in the walktrough, it works.

Dastan Kojomuratov
Here is the code in C#
$0 $0$0 $0$0 $0
[RunInstaller(true)]
    public partial class CSInstaller : System.Configuration.Install.Installer
    {
        SqlConnection sqlConnection = new SqlConnection();
        public CSInstaller()
        {
            InitializeComponent();
        }

        private string GetSql(string name)
        {
            try
            {
                Assembly asm = Assembly.GetExecutingAssembly();

                Stream stm = asm.GetManifestResourceStream(asm.GetName().Name + "." + name);

                StreamReader reader = new StreamReader(stm);
                return reader.ReadToEnd();
            }
            catch (Exception e)
            {
                MessageBox.Show("In GetSQL: " + e.Message);
                throw;
            }
        }
        private void ExecuteSql(string dbName, string sql)
        {
            SqlCommand command = new SqlCommand(sql, sqlConnection);

            sqlConnection.ConnectionString = Properties.Settings.Default.masterConnectionString;
            command.Connection.Open();
            command.Connection.ChangeDatabase(dbName);
            try
            {
                command.ExecuteNonQuery();
            }
            finally
            {
                command.Connection.Close();
            }
        }

        protected void AddDBTable(string dbName)
        {
            try
            {
                ExecuteSql("master", "CREATE DATABASE " + dbName);

                ExecuteSql(dbName, GetSql("sql.txt"));
            }
            catch (Exception e)
            {
                MessageBox.Show("In exception handler: " + e.Message);
                throw;
            }
        }

        public override void Install(System.Collections.IDictionary statesaver)
        {
            base.Install(statesaver);
            AddDBTable(this.Context.Parameters["dbname"]);
        }
    }
$0
$0 $0$0 $0 $0