Kdybych to byl já (když jsem to já...):
Nechcete se nijak zvlášť snažit, aby databázové soubory fungovaly tak, že je zkopírujete a připojíte – existují důvody, proč byste to mohli chtít, ale věřím, že to jsou spíše výjimky než pravidla.
V souladu s tím, co musíte udělat, je vytvořit skript pro vytvoření databáze, tj. použít SQL DDL k vytvoření databáze a tabulek a všech ostatních věcí ve vašem schématu.
V podstatě vše, co potřebujete, abyste to mohli udělat, jsou příslušná práva k instanci serveru a poté připojovací řetězec (který pravděpodobně můžete sestavit mimo název serveru/instance).
Odtud:
- Existuje databáze? Pokud ne, vytvořte jej.
- Pokud existuje databáze, je to správná verze schématu? Pokud je příliš nízká, buď ji aktualizujte, nebo informujte uživatele a elegantně vycouvejte v závislosti na tom, jak chcete, aby věci fungovaly. Pokud je příliš vysoká, stačí vycouvat a upozornit, že je vyžadována aktualizovaná verze aplikace
- Všechno je, jak má být, pokračujte.
Z hlediska kódu:metoda k určení, zda databáze existuje; způsob vytvoření standardní "prázdné" databáze s tabulkou verzí a číslem verze 0; metody pro převedení schématu na aktuální verzi spuštěním příslušného DDL (ten náš zakódujeme do C#, protože poskytuje větší flexibilitu, ale stejně tak můžete spouštět DDL skripty v sekvenci).
Existuje:
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;
}
Vytvořte novou databázi:
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();
}
}
Aktualizační kód je o něco složitější, ale v podstatě spouští věci jako toto:
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
);
Vše zabalené do transakce na aktualizaci – takže pokud se aktualizace nezdaří, měli byste opustit databázi, je známo, že dobrý stav.
Proč to dělat tímto způsobem (v kódu, který se neobejde bez jeho zkoušek?) Konečným výsledkem je vysoký stupeň jistoty, že schéma, se kterým vaše aplikace mluví, je schéma, se kterým vaše aplikace očekává, že bude mluvit... správné tabulky, pravé sloupce (ve správném pořadí, které mají správný typ a správnou délku), atd., atd. a že tomu tak bude i nadále.
Omlouvám se, jestli je to trochu dlouhé - ale tohle je něco, co mě docela zajímá...