Om det var jag (när det är jag...):
Du vill inte särskilt försöka få databasfiler att fungera genom att kopiera dem och bifoga dem - det finns anledningar till att du kanske vill det, men jag tror att det är undantag snarare än regler.
Det du behöver göra är att skapa skript för databasen, dvs att använda SQL DDL för att skapa databasen och tabellerna och alla andra saker i ditt schema.
I stort sett allt du behöver för att du ska kunna göra detta är lämpliga rättigheter till serverinstansen och sedan en anslutningssträng (som du förmodligen kan bygga förutom serverns/instansens namn).
Härifrån:
- Finns det en databas? Om inte, skapa den.
- Om det finns en databas, är det rätt schemaversion? Om det är för lågt, antingen uppdatera det eller ge användaren råd och backa graciöst beroende på hur du vill att saker och ting ska fungera. Om det är för högt, gå bara tillbaka och meddela att en uppdaterad version av programmet krävs
- Allt är som det ska, fortsätt.
Ur kodsynpunkt:metod för att avgöra om en databas existerar; metod för att skapa en standard "tom" databas med en versionstabell och ett versionsnummer 0; metoder för att få upp schemat till den aktuella versionen genom att köra lämplig DDL (vi kodar vår till C# eftersom det ger mer flexibilitet men du kan lika gärna köra DDL-skript i sekvens).
Finns det:
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;
}
Skapa en ny databas:
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();
}
}
Uppdateringskoden är lite mer komplex men kör i princip saker som detta:
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
);
Allt samlat i en transaktion per uppdatering - så att om uppdateringen misslyckas bör du lämna databasen i ett känt tillstånd.
Varför göra det på detta sätt (i kod, som inte är utan tester?) och slutresultatet är en hög grad av förtroende för att schemat som din app pratar med är det schema som din app förväntar sig att prata med... rätt tabeller, högerkolumner (i rätt ordning, som är rätt typ och rätt längd), etc, etc. och att så kommer att fortsätta att vara fallet med tiden.
Ber om ursäkt om det här är lite långt - men det här är något jag är ganska sugen på...