Se fosse eu (quando sou eu...):
Você não quer tentar fazer com que os arquivos de banco de dados funcionem copiando-os e anexando-os - há razões pelas quais você pode querer, mas acredito que sejam exceções e não regras.
Assim, o que você precisa fazer é criar um script do banco de dados, ou seja, usar SQL DDL para criar o banco de dados e as tabelas e todas as outras coisas em seu esquema.
Praticamente tudo o que você precisa para permitir que você faça isso são direitos apropriados para a instância do servidor e, em seguida, uma string de conexão (que você provavelmente pode criar além do nome do servidor/instância).
Daqui:
- Existe um banco de dados? Caso contrário, crie-o.
- Se houver um banco de dados, é a versão correta do esquema? Se estiver muito baixo, atualize-o ou aconselhe o usuário e volte normalmente, dependendo de como você deseja que as coisas funcionem. Se estiver muito alto, volte e avise que é necessária uma versão atualizada do aplicativo
- Tudo está como deveria, continue.
Do ponto de vista do código:método para determinar se existe um banco de dados; método para criar um banco de dados "vazio" padrão com uma tabela de versão e um número de versão 0; métodos para trazer o esquema para a versão atual executando o DDL apropriado (codificamos o nosso em C# porque ele fornece mais flexibilidade, mas você também pode executar scripts DDL em sequência).
Isto existe:
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;
}
Crie um novo banco de dados:
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();
}
}
O código de atualização é um pouco mais complexo, mas basicamente executa coisas assim:
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
);
Tudo embrulhado em uma transação por atualização - de modo que, se a atualização falhar, você deve deixar o banco de dados em um estado bom conhecido.
Por que fazer dessa maneira (no código, que não é sem seus testes?) bem, o resultado final é um alto grau de confiança de que o esquema com o qual seu aplicativo está falando é o esquema com o qual seu aplicativo espera conversar... tabelas corretas, colunas certas (na ordem certa, que são do tipo certo e do comprimento certo), etc, etc. e que este continuará a ser o caso ao longo do tempo.
Peço desculpas se isso é um pouco longo - mas isso é algo que eu estou bastante interessado ...