Gdybym to był ja (kiedy to ja...):
Nie chcesz szczególnie starać się, aby pliki bazy danych działały, kopiując je i dołączając - są powody, dla których możesz chcieć, ale uważam, że są to wyjątki, a nie reguły.
W związku z tym, co musisz zrobić, to stworzyć skrypt bazy danych, tj. użyć SQL DDL do stworzenia bazy danych, tabel i wszystkich innych rzeczy w twoim schemacie.
Prawie wszystko, czego potrzebujesz, aby to zrobić, to odpowiednie prawa do instancji serwera, a następnie ciąg połączenia (który prawdopodobnie możesz zbudować niezależnie od nazwy serwera/instancji).
Stąd:
- Czy istnieje baza danych? Jeśli nie, utwórz go.
- Jeśli istnieje baza danych, czy jest to właściwa wersja schematu? Jeśli jest zbyt niski, zaktualizuj go lub poinformuj użytkownika i wycofaj się z wdziękiem, w zależności od tego, jak chcesz, aby wszystko działało. Jeśli jest zbyt wysoki, po prostu wycofaj się i poinformuj, że wymagana jest zaktualizowana wersja aplikacji
- Wszystko jest tak, jak powinno, kontynuuj.
Z punktu widzenia kodu:metoda określenia, czy baza danych istnieje; metoda tworzenia standardowej „pustej” bazy danych z tabelą wersji i numerem wersji 0; metody doprowadzenia schematu do aktualnej wersji przez uruchomienie odpowiedniego DDL (nasze kodujemy do C#, ponieważ zapewnia to większą elastyczność, ale równie dobrze można uruchamiać po kolei skrypty DDL).
Czy istnieje:
public virtual bool Exists()
{
bool exists = false;
string masterConnectionString = this.CreateConnectionString(this.Server, this.FailoverServer, "master");
this.DBConnection.ConnectionString = masterConnectionString;
this.DBConnection.Open();
try
{
SqlCommand cmd = new SqlCommand();
cmd.Connection = this.DBConnection;
cmd.CommandText = "SELECT COUNT(name) FROM sysdatabases WHERE name = @DBName";
cmd.Parameters.AddWithValue("@DBName", this.DBName);
exists = (Convert.ToInt32(cmd.ExecuteScalar()) == 1);
}
finally
{
this.DBConnection.Close();
}
return exists;
}
Utwórz nową bazę danych:
public virtual void CreateNew()
{
string createDDL = @"CREATE DATABASE [" + this.DBName + "]";
this.BuildMasterConnectionString();
this.DBConnection.Open();
try
{
this.ExecuteSQLStmt(createDDL, this.DefaultSQLTimeout, null);
}
finally
{
this.DBConnection.Close();
}
createDDL = @"
CREATE TABLE AAASchemaVersion
(
Version int NOT NULL,
DateCreated datetime NOT NULL,
Author nvarchar(30) NOT NULL,
Notes nvarchar(MAX) NULL
);
ALTER TABLE AAASchemaVersion ADD CONSTRAINT PK_Version PRIMARY KEY CLUSTERED
(
Version
);
INSERT INTO AAASchemaVersion
(Version, DateCreated, Author, Notes)
VALUES
(0, GETDATE(), 'James Murphy', 'Empty Database')
";
this.BuildConnectionString();
this.ConnectionString += ";pooling=false";
this.DBConnection.Open();
try
{
this.ExecuteSQLStmt(createDDL, this.DefaultSQLTimeout, null);
}
catch (Exception ex)
{
throw new Exception("Exception while creating / initialising AAASchemaVersion", ex);
}
finally
{
this.DBConnection.Close();
}
}
Kod aktualizacji jest nieco bardziej złożony, ale zasadniczo uruchamia takie rzeczy:
CREATE TABLE AuditUser
(
ID int IDENTITY(1,1) NOT NULL,
UserSourceTypeID tinyint NOT NULL,
DateCreated smalldatetime NOT NULL,
UserName nvarchar(100) NOT NULL
);
ALTER TABLE AuditUser
ADD CONSTRAINT
PK_AuditUser PRIMARY KEY CLUSTERED
(
ID
),
CONSTRAINT [FK_AuditUser_UserSourceType] FOREIGN KEY
(
UserSourceTypeID
) REFERENCES UserSourceType (
ID
);
Wszystko opakowane w transakcję na aktualizację - więc jeśli aktualizacja się nie powiedzie, powinieneś opuścić bazę danych, jest to znany dobry stan.
Dlaczego robisz to w ten sposób (w kodzie, który nie jest pozbawiony prób?) Cóż, rezultatem końcowym jest wysoki stopień pewności, że schemat, z którym rozmawia Twoja aplikacja, jest schematem, z którym Twoja aplikacja oczekuje rozmawiać... właściwe tabele, właściwe kolumny (we właściwej kolejności, które mają właściwy typ i właściwą długość) itp., itd. i tak będzie z czasem.
Przepraszamy, jeśli to trochę za dużo - ale to jest coś, co bardzo mi się podoba...