15

For some integration tests I want to connect to the database and run a .sql file that has the schema needed for the tests to actually run, including GO statements. How can I execute the .sql file? (or is this totally the wrong way to go?)

I've found a post in the MSDN forum showing this code:

using System.Data.SqlClient;
using System.IO;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string sqlConnectionString = "Data Source=(local);Initial Catalog=AdventureWorks;Integrated Security=True";
            FileInfo file = new FileInfo("C:\\myscript.sql");
            string script = file.OpenText().ReadToEnd();
            SqlConnection conn = new SqlConnection(sqlConnectionString);
            Server server = new Server(new ServerConnection(conn));
            server.ConnectionContext.ExecuteNonQuery(script);
        }
    }
}

but on the last line I'm getting this error:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.TypeInitializationException: The type initializer for '' threw an exception. ---> .ModuleLoadException: The C++ module failed to load during appdomain initialization. ---> System.DllNotFoundException: Unable to load DLL 'MSVCR80.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E).

I was told to go and download that DLL from somewhere, but that sounds very hacky. Is there a cleaner way to? Is there another way to do it? What am I doing wrong?

I'm doing this with Visual Studio 2008, SQL Server 2008, .Net 3.5SP1 and C# 3.0.

Pablo Fernandez
  • 279,434
  • 135
  • 377
  • 622

6 Answers6

29
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;

You shouldn't need SMO to execute queries. Try using the SqlCommand object instead. Remove these using statements. Use this code to execute the query:

 SqlConnection conn = new SqlConnection(sqlConnectionString);
 SqlCommand cmd = new SqlCommand(script, conn);
 cmd.ExecuteNonQuery();

Also, remove the project reference to SMO. Note: you will want to clean up resources properly.

Update:

The ADO.NET libraries do not support the 'GO' keyword. It looks like your options are:

  1. Parse the script. Remove the 'GO' keywords and split the script into separate batches. Execute each batch as its own SqlCommand.
  2. Send the script to SQLCMD in the shell (David Andres's answer).
  3. Use SMO like the code from the blog post.

Actually, in this case, I think that SMO may be the best option, but you will need to track down why the dll wasn't found.

Matt Brunell
  • 10,141
  • 3
  • 34
  • 46
  • 1
    Matt, pretty solid. On a project I'm working on now, we have code that uses Smo in exactly the same way described in the OP. I can't figure out why it wasn't already obvious that SqlCommand was a better fit...that is until I read your answer. – David Andres Sep 19 '09 at 22:33
  • Does that work if the SQL file contain "GO" statement? My understanding was that it didn't. – Pablo Fernandez Sep 19 '09 at 22:56
  • I haven't tested that. I believe it does. – Matt Brunell Sep 20 '09 at 01:27
  • 1
    It doesn't. There are many things in a schema as exported from SQL server that fail with this approach: Test method IsItScienceFiction.Tests.UserTest.TestMethod1 threw exception: System.Data.SqlClient.SqlException: Incorrect syntax near 'GO'. Incorrect syntax near the keyword 'SET'. Incorrect syntax near the keyword 'ALTER'. – Pablo Fernandez Sep 20 '09 at 09:11
  • GO is not a SQL Command. GO is a keyword used in SQL Server stored procedures that tells the server to execute the SQL that you gave it. You don't need to "GO" when you are executing SQL using SQLCommand; you just need to Execute() it. – Robert Harvey Sep 20 '09 at 16:45
  • Not "stored procedures" that use GO but .sql scripts to denote when to execute a chunk of the script. Its unique to SSMS. –  Mar 11 '10 at 16:10
  • SMO is probably the right choice in any case - you ought to be using to backup the database first, apply your changes, and then revert to your backup if the change did not apply successfully. – Case Feb 25 '13 at 22:10
  • ExecuteNonQuerey only works on an open SqlConnection. An SqlConnection can only be opened on an existing database. So If you use your script to create the database this method doesn't work – Harald Coppoolse Mar 04 '15 at 14:03
9

MSVCR80 is the Visual C++ 2005 runtime. You may need to install the runtime package. See http://www.microsoft.com/downloads/details.aspx?FamilyID=200b2fd9-ae1a-4a14-984d-389c36f85647&displaylang=en for more details.

In addition to resolving the DLL issue and Matt Brunell's answer (which I feel is more appropriate for what you're trying to do), you can use the SQLCMD command line tool (from the SQL Client tools installation) to execute these SQL scripts. Just be sure it's on your path so you don't struggle with path locations.

This would play out like so:

Actual command:

SQLCMD -S myServer -D myDatabase -U myUser -P myPassword -i myfile.sql

Parameters (case matters):

S: server
d: database
U: User name, only necessary if you don't want to use Windows authentication
P: Password, only necessary if you don't want to use Windows authentication
i: File to run

Code to execute SQL files:

var startInfo = new ProcessStartInfo();
startInfo.FileName = "SQLCMD.EXE";
startInfo.Arguments = String.Format("-S {0} -d {1}, -U {2} -P {3} -i {4}",
                                    server,
                                    database,
                                    user,
                                    password,
                                    file);
Process.Start(startInfo);

See http://msdn.microsoft.com/en-us/library/ms162773.aspx for more information on the SQLCMD tool.

David Andres
  • 31,351
  • 7
  • 46
  • 36
2

Having the same need to automatically run a generated database script from code, I set out to parse the SQL script to remove GO statements and split the script into separate commands (as suggested by @MattBrunell). Removing the GO statements was easy, but splitting the statements on "\r\n" did not work since that screwed up the multiline-statements. Testing a few different approaches, I was quite surprised to find out that the script doesn't have to be split into separate commands at all. I just removed all "GO" statements and sent the whole script (including comments) into SqlCommand:

  using System.Data.SqlClient;

  using(SqlConnection connection = new SqlConnection(connectionString))
  using(SqlCommand command = connection.CreateCommand())
  {
    string script = File.ReadAllText("script.sql");
    command.CommandText = script.Replace("GO", "");
    connection.Open();
    int affectedRows = command.ExecuteNonQuery();
  }

This code has been tested with SQL Server 2008 R2 and the script generated by "Database -> Tasks -> Generate Scripts...". Below are some examples of the commands in the script:

USE [MyDatabase]

ALTER TABLE [MySchema].[MyTable] DROP CONSTRAINT [FK_MyTable_OtherTable]
DROP TABLE [MySchema].[MyTable]

SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON

/****** Object:  Table [MySchema].[MyTable]    Script Date: 01/23/2013 13:39:29 ******/
CREATE TABLE [MySchema].[MyTable](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Subject] [nvarchar](50) NOT NULL,
    [Body] [nvarchar](max) NOT NULL,
 CONSTRAINT [PK_MyTable] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

SET IDENTITY_INSERT [MySchema].[MyTable] ON
INSERT [MySchema].[MyTable] ([Id], [Subject], [Body]) VALUES (1, N'MySubject', N'Line 1
Line 2
Line 3
Multi-line strings are also OK.
')
SET IDENTITY_INSERT [MySchema].[MyTable] OFF

I guess there might be some maximum length for a single SqlCommand, above which the script have to be split. My script, which execute without problems, contains around 1800 statements and is 520 kB.

Anlo
  • 3,228
  • 4
  • 26
  • 33
  • 6
    Whether you can simply delete all the "GO"s depends on what's in your script. There are certain statements, like `CREATE PROCEDURE`, that have to be the first statement in the batch (i.e. they must either be on their own, or preceded by a "GO"). – Rory MacLeod Feb 21 '13 at 22:36
  • This may not be a great solution. The reason for "GO" in the scripts is that people want the rest of the script to continue excecution if the part before "GO" failed. Stripping the GO means that your script will not run as its writers intended. – Case Feb 25 '13 at 22:09
  • 1
    The answer is incorrect! Just removing "GO" is not enough. You must SPLIT your script into batches and execute them separately. Because that's what the GO command is for. Otherwise you can get problems, as Rory MacLeod said. – C-F Jul 14 '15 at 21:30
0

Have you tried running this with a very, very basic script in the .sql file? Maybe something that just inserts one row or creates an arbitrary table? Something that is very easy to verify? Essentially, this code is like hard coding the sql, except you're reading it from a file. If you can get it to work with a very simple file, then I would say that there is likely something wrong with the file structure itself. The post alluded to the fact that there are some stipulations regarding what can and cannot be in the file. If nothing else, it's a good place to start troubleshooting.

Chris Thompson
  • 35,167
  • 12
  • 80
  • 109
  • The file was an export from SQL Server itself, nothing generated manually, so it should be valid. The post said that if the file had a go statement, this was the only way to run it, and that's why I'm doing it. I've replaced the content of the file with select(1 + 1) and I get exactly the same error. I don't think that's a SQL error. – Pablo Fernandez Sep 19 '09 at 22:15
0

You may be interested in this: Link

It presents a general-purpose 'test fixture' to automatically execute sql-scripts. There is also sample code available, and there are no dependencies to any unusual assemblies whatsoever...

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Thomas Weller
  • 11,631
  • 3
  • 26
  • 34
0

If you add following references in your project, then original code will work fine.

I use SQL 2008 Express.

Path: C:\Program Files (x86)\Microsoft SQL Server\100\SDK\Assemblies\

Files: microsoft.sqlserver.smo.dll, microsoft.sqlserver.connectioninfo.dll and Microsoft.SqlServer.Management.Sdk.Sfc.dll

Miguel
  • 61
  • 1
  • 2