| 

.NET C# Java Javascript Exception

1
Hallo zusammen,

ich habe folgendes Problem:
Für einen UnitTest in NUnit muss ich eine externe Applikation automisiert in einem definierten Zustand starten. Die Applikation verwendet als Datenquelle eine SQL Server Datenbank.
Für die Tests muss ich die Datenbank der Applikation in einen definierten Zustand bringen bzw. eine ganz bestimmte Version der Datenbank (Schema inkl. Daten) einspielen. Die externe Applikation wird ebenfalls von unserem Team entwickelt und so kenne ich die Struktur der Datenbank bzw. komme an die Datenbank auch ohne Probleme ran.

Ich habe nun mit SQL Server Management Studio Express ein Skript einer Referenz-Datenbank erzeugen lassen...so weit so gut...allerdings erzeugt das Skript nur die Tabellen selber...der Inhalt der Referenzdatenbank wird durch das Skript nicht wieder hergestellt.
Ich habe auch schon an die Verwendung einer *.bak Datei gedacht...das Problem ist, ist das ich die Datenbank als Vorbereitung für den Unit Test im Code (C#) einspielen muss...leider habe ich keinen Hinweis im Internet gefunden, wie man eine *.bak Datei mit der SQL Server API zurückspielen kann.

Wäre super, wenn mir jemand sagen könnte, wie ich automatisch ein Skript erzeugen lassen kann, dass eine komplette Datenbank oder zumindest eine einzelnen Tabelle mit Schema UND Inhalt abbilden bzw. wieder herstellen kann - am Besten mit SQL Server Express oder einem anderen Tool.
Für den konkreten Fall sind aber auch andere Lösungsansätze zum Generieren einer Datenbank aus einer Datei willkommen.

Danke !
01.04.2011
gehaschu 23 1 5
3 Antworten
0
Ich würde per SQL ein komplettes Backup der Datenbank machen und auch per SQL wieder einspielen. Die SQL-Skripte könntest Du per osql.exe in einer Batchdatei ausführen (z.B. nach dem Buildprozess).

Hier die Stored Procedure zum Sichern der Datenbank:

CREATE PROC [dbo].[BackupDatabase] (@IN_Database VARCHAR(255), @IN_File VARCHAR(1000))
AS

SET NOCOUNT ON

EXEC ('BACKUP DATABASE '[ + @IN_Database + '] TO DISK= ''' + @IN_File + '''')


Und hier die Stored Procedure zum Einspielen der DB.
Wichtig ist, dass die SP nicht als Prozess auf der zu wiederherstellenden Datenbank ausgeführt werden darf.

CREATE PROC [dbo].[RestoreDatabase] (@IN_DATABASE VARCHAR(255), @IN_FILENAME VARCHAR(1000)) 
AS

SET NOCOUNT ON

-- WICHTIG: Diese SP muss von einer anderen Datenbank, als der Zieldatenbank ausgefuehrt werden:
USE master

-- Alle Prozesse auf der Zieldatenbank killen (ein DROP DATABASE waere auch moeglich):
DECLARE @pid INT
DECLARE procs CURSOR FAST_FORWARD FOR
SELECT p.spid AS [Process ID]
FROM master.dbo.sysprocesses p INNER JOIN master.dbo.sysdatabases d ON d.dbid = p.dbid
WHERE (d.name = @IN_Database)
OPEN procs

FETCH NEXT FROM procs INTO @pid
WHILE @@FETCH_STATUS = 0 BEGIN

EXEC (' USE [' + @IN_DATABASE + '] ' + ' KILL ' + @pid)

FETCH NEXT FROM procs INTO @pid
END

CLOSE procs
DEALLOCATE procs

-- und jetzt die Datenbank wieder einspielen:
RESTORE DATABASE @IN_DATABASE FROM DISK=@IN_FILENAME
01.04.2011
mblaess 1,2k 1 9
mblaess 1,2k 1 9
Hi...vielen Dank für die Antwort...so ähnlich habe ich es dann schlussendlich auch gemacht...allerdings nicht mit einer Stored Procedure, sondern mit einem SQL-Script, welches ich dann über die API in C# zurückgespielt habe. Das Problem ist, dass ich das Rücksichern ja vom Code aus vor dem Unit Test machen muss...aber bestimmt gibt es in der SQL Server API auch ein Kommando zum Ausführen von Stored Procedures. Ich poste aber auch mal meine Lösung.
gehaschu 07.04.2011
2
Hallo,

es kommt darauf an, welche SQL Server Version du verwendest. Ist es ein Server ab 2008 dann kannst du im "Generate Scripts"-Wizard unter "Advanced" einstellen, dass er nicht nur das Schema, sondern auch die enthaltenen Daten in das Skript mit aufnimmt.

Also im Wizard nach Auswahl der Objekte => Advanced => Types of data to script => Schema and data.

Hilft das?
01.04.2011
Axel 170 1 6
Hi...danke für die Antwort...wir verwenden leider eine Express Version des SQL Server 2005 aus Gründen der Kombatibilität bzw. Ausführbarkeit mit der Hardware...alter Industrie-PC...deshalb habe ich diese Möglichkeit leider nicht...unter MySQL ging dies ja auch ziemlich einfach...habe auch kein Konsolen-Commando für SQL Server gefunden, der so ein Skript erzeugt.Aber ich habe ne andere Lösung, die ich noch poste...
gehaschu 07.04.2011
1
Ich habe ja geschrieben, dass ich meine Lösung posten werde...
es wird vorrausgesetzt, dass die BAK-Datei schon erzeugt wurde - ggf. mit der SQL Server Management Console oder per Script.

Es gibt in SQL Server übrigens bereits viele Vorlagen für SQL-Skripte für die gängigsten Aufgaben...zu finden unter "Ansicht->Vorlagen-Explorer" oder STRG-ALT-T.
Dort ist unter dem Ordner "Backup" u.a. ein Script für das Backup einer DB in eine BAK-Datei zu finden. Unter "Restore" z.B. Scripte für das Zurücksichern von einer BAK-Datei - dies habe ich z.B. als Grundlage für mein Script hier verwendet.


Teil 1 - SQL Script:

hier ist der erste Teil...das SQL Script zum Erzeugen bzw. Wiederherstellen einer Datenbank aus einer BAK-Datei.

-- =======================================================================================================================================
-- SQL Script for creating a database - named "MyDatabase" in this example - from a BAK file.
-- Please note that you need to replace some text items in the script with names and paths of your "real world" solution :-).
-- =======================================================================================================================================

-- =======================================================================================================================================
-- Drop the database first if it exists
-- "MyDatabase" has to be replaced with the name of the database to be dropped.
-- =======================================================================================================================================
USE master
GO

IF EXISTS (
SELECT name
FROM sys.databases
WHERE name = 'MyDatabase'
)

DROP DATABASE MyDatabase
GO

-- =======================================================================================================================================
-- Now create the database
-- "MyDatabase" has to be replaced with the name of the database to be created.
-- =======================================================================================================================================
CREATE DATABASE MyDatabase


-- =======================================================================================================================================
-- Restore Database from BAK file
-- PathOfMyBAKFile has to be replaced by the path of the BAK file to be used as source for restoring the DB,
-- e.g. "c:\Database\Backups\MyDatabaseBackupFile.bak"
-- =======================================================================================================================================
USE master
GO

RESTORE FILELISTONLY
FROM DISK=N'PathOfMyBAKFile'
GO

-- =======================================================================================================================================
-- Make Database to single user Mode
-- "MyDatabase" has to be replaced with the name of the database for which to set the user mode.
-- =======================================================================================================================================
ALTER DATABASE MyDatabase
SET SINGLE_USER WITH
ROLLBACK IMMEDIATE

-- =======================================================================================================================================
-- Restore Database from BAK file
-- "PathOfMyBAKFile" has to be replaced by the path of the BAK file to be used as source for restoring the DB,
-- e.g. "c:\Database\Backups\MyDatabaseBackupFile.bak"

-- "BackupSourceDatabase" has to be replaced with the name of the source database from which the BAK file has been created.

-- "LocalMsSQLServerPath" has to be replaced with the path to your MS SQL Server installation folder,
-- e.g. "C:\Programme\Microsoft SQL Server\MSSQL.1\MSSQL" in a German version of Windows or
-- "C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL" in an English version of Windows.

-- What are the MOVE commands good for ?
-- If you want to restore from a BAK file which has been created for a database named differently than the database you want to
-- use as target, you have to use the MOVE commands to be able to restore to your new named database.
-- e.g. you have created your BAK file from a database named "A" and you want to use the BAK file to restore into a new database named "B"
-- you have to use the MOVE command
-- "MOVE 'A' TO N'<LocalMsSQLServerPath>\Data\B_Data.mdf'" for the data and
-- "MOVE 'A' TO N'<LocalMsSQLServerPath>\Data\B_Log.mdf'" for the logs.
-- =======================================================================================================================================
RESTORE DATABASE MyDatabase FROM DISK=N'PathOfMyBAKFile'
WITH
FILE = 1,
NOUNLOAD,
REPLACE,
STATS = 10,
MOVE 'BackupSourceDatabase' TO N'LocalMsSQLServerPath\Data\MyDatabase_Data.mdf',
MOVE 'BackupSourceDatabase_Log' TO N'LocalMsSQLServerPath\Data\MyDatabase_Log.ldf'
GO


Teil 2 - Verwenden der SQL Server API in C# zum Ausführen des Scripts

In
InitializeTestDataBase
wird die Test Datenbank initialisiert - und zwar durch Ausführen des SQL-Scripts aus Teil 1. Da das Script auf der Master Datenbank ausgeführt wird, verbinde ich mich mit der "master" DB. Die zu erstellende DB ist ja ohnehin noch nicht existent, also kann man sich logischerweise auch nicht damit verbinden :-).

ApplyChangesToDbScript
rufe ich auf, um einige Texte des Scripts dynamisch durch andere Dinge zu ersetzen - Da wir sowohl englisch, als auch deutsche Windows-Versionen einsetzen, gäbe es beim Zurücksichern der Datenbank einen Fehler, wenn wir fix "C:\Programme\Microsoft SQL Server\MSSQL.1" oder "C:\Program Files\Microsoft SQL Server\MSSQL.1" im Script verwenden würden.

Deshalb ersetze ich diese Angaben bzw. deren Platzhalter-Texte im Skript dynamisch durch den richtigen Pfad - je nachdem, welches Verzeichnis auf dem Zielsystem existiert.

Dies kann man auch mit den übrigen Angaben so machen - je nachdem, was man dynamisch ersetzen muss, damit das Script läuft.
In der Produktiv-Version des Scripts habe ich alle Platzhalter in "< >" geschrieben - wie z.B. "<MyDataBaseName>"...so weiß ich beim Lesen des Scripts später, welche Dinge ich dynamisch ersetze und die "< >" machen den zu ersetzenden Text noch eindeutiger -
ist aber Geschmacksache, denke ich.

private void InitializeTestDataBase()
{
string sqlConnectionString = @"Server=.\SQLEXPRESS; Database=master; Integrated Security=true; Connection Timeout=30";
FileInfo file = new FileInfo(DbScriptFileName);
if (file.Exists)
{
string script = file.OpenText().ReadToEnd();
// Optional: Apply changes to db script if needed - such as replacing file paths if your solution is executed from a different working directory, e.g. when running in a unit test.
script = ApplyChangesToDbScript(script);
SqlConnection conn = new SqlConnection(sqlConnectionString);
Server server = new Server(new ServerConnection(conn));
server.ConnectionContext.ExecuteNonQuery(script);
conn.Close();
}
}

private static string ApplyChangesToDbScript(string scriptFileContent)
{
const string germanOsSqlServerPath = @"C:\Programme\Microsoft SQL Server\MSSQL.1\MSSQL";
const string englishOsSqlServerPath = @"C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL";

scriptFileContent = scriptFileContent.Replace("PathOfMyBAKFile", BackupFilePath);

if (Directory.Exists(germanOsSqlServerPath))
{
scriptFileContent = scriptFileContent.Replace("LocalMsSQLServerPath", germanOsSqlServerPath);
}
else if (Directory.Exists(englishOsSqlServerPath))
{
scriptFileContent = scriptFileContent.Replace("LocalMsSQLServerPath", englishOsSqlServerPath);
}

return scriptFileContent;

}
07.04.2011
gehaschu 23 1 5

Stelle deine .net-Frage jetzt!