注:作者原创,转载请注明出处。
下载工程和测试代码:SqlCmdHelper[2009-02-12修改]
摘要
本文说明了使用sqlcmd进行数据库维护的帮助类SqlCmdHelper的设计与实现。SqlCmdHelper通过对SQLCMD的调用,并不直接使用ADO.NET接口,但实现了用ADO.NET接口不方便或不可能实现的数据库维护功能。
SQLCMD的用法
usage: Sqlcmd
[-U login id]
登录名,就是连接字符串中的 user id
[-P password]
登录密码,连接字符串中的 Password
[-S server]
SqlServer服务器,就是连接字符串中的DataSource
[-E trusted connection]
表示信任的连接
[-d use database name]
使用的数据库,就是连接字符串中的Database或Initial Catalog
[-Q "cmdline query" and exit]
表示执行SQL语句,-Q后面的双引号中放要执行的SQL语句
[-i inputfile]
输入要执行的外部SQL脚本
[-b On error batch abort]
表示如果有语句运行出错,则退出,而不再往下执行了。注意,这个退出不会导致先前执行的语句回滚。
再来看在SqlCmdHelper使用到的一些参数的构造
1. 在Master数据库中执行SQL语句。参数如下
private const string MasterDbCmdParameter = " -E -b -S {0} -d master -Q\"{1}\"";
提示:如果要执行多条SQL语句,可以将各条语句用分号(;)隔开,请不要在SQL语句中使用换行符。
2. 执行外部SQL脚本文件。参数如下
private const string RunSqlScriptTemplate = "-E -S {0} -i \"{1}\"";
考虑到Sqlserver有不同的验证方式,SqlCmdHelper不使用上面的常量来构造参数,而使用相应的静态方式来实现这个功能。这些静态方法将通过传入的DatabaseInfo的IsSqlServerAuthenticateUsed属性来判断使用何种验证方式,从而构造不同的参数。
数据库维护的SQL语句
1. 删除数据库
private const string DropDbSqlTemplate = @"IF EXISTS (SELECT * FROM sys.databases WHERE [name] = '{0}') Drop database {1};";
提示:我在删除数据库时,经常有类似 “因为数据库正在使用,不能删除”的错误提示。为了删除数据库,可以重启数据库以后再删除。SqlCmdHelper已经提供了NetStart和NetStop接口来实现这个功能。
2. 附加数据库
private const string AttachDbSqlTemplate = @"EXEC sp_attach_db @dbname='{0}', @filename1='{1}', @filename2='{2}'";
3. 分离数据库
private const string DetachDbSqlTemplate = "IF EXISTS (SELECT * FROM sys.databases WHERE [name] = '{0}') EXEC sp_detach_db '{1}';";
提示:在分离数据库时,也会经常出现和删除数据库类似的提示。解决方法相同。
运行SqlCmd
在SqlCmdHelper类中,通过创建一个新的进程来运行sqlcmd,在运行sqlcmd时,给它相应合适的参数。
创建sqlcmd进程的方法实现如下
Code
/**//// <summary>
/// Run process
/// </summary>
/// <param name="fileName"></param>
/// <param name="argument"></param>
/// <param name="visible"></param>
/// <returns></returns>
internal static int RunProcess(string fileName, string argument, bool visible)
{
if (string.IsNullOrEmpty(fileName))
{
throw new ArgumentNullException("fileName");
}
using (Process process = new Process())
{
process.StartInfo = new ProcessStartInfo(fileName, argument);
process.StartInfo.WindowStyle = visible ? ProcessWindowStyle.Normal : ProcessWindowStyle.Hidden;
process.Start();
process.WaitForExit();
return process.ExitCode;
}
}
这个方法返回了进程的ExitCode,如果进程正常退出,则会返回0,否则就是非0.
启动和停止SqlServer服务
这是通过net start 和 net stop 命令来进行的。命令的格式为
Net start|stop [serviceName]
一般SQLSERVER进程的ServicesName 是 Sql Server (MSSQLSERVER)。如果不确定的话,可以运行cmd,输入net start查看进程名。
SqlCmdHelper提供了NetStart和NetStop两个方法来帮助用户启动和停止服务。它们的实现如下。
Code
/**//// <summary>
/// start a service.
/// <example>
/// If you want to start a local sqlserver services. use:
/// NetStart("SQL Server (MSSQLSERVER)");
/// </example>
/// </summary>
/// <param name="service"></param>
/// <returns></returns>
public static bool NetStart(string service)
{
if (string.IsNullOrEmpty(service))
throw new ArgumentNullException();
return (0== RunProcess("net", "start \"" + service + "\"", true));
}
/**//// <summary>
/// stop a service
/// </summary>
/// <example>
/// If you want to start a local sqlserver services. use:
/// NetStop("SQL Server (MSSQLSERVER)");
/// </example>
/// <param name="service"></param>
/// <returns></returns>
public static bool NetStop(string service)
{
if (string.IsNullOrEmpty(service))
throw new ArgumentNullException();
return (0 == RunProcess("net", "stop \"" + service + "\"", true));
}
数据库维护
SqlCmdHelper提供了下面的维护接口:
AttachDatabase:附加数据库
DetachDatabase:分离数据库
DropDatabase:删除数据库
RunSqlScript:执行外部SQL脚本
ExecuteSqlInMaster:在Master数据库中执行Sql语句
代码如下:
Code:数据库操作
/**//// <summary>
/// Attach database
/// </summary>
/// <param name="dbInfo"></param>
/// <returns></returns>
public static bool AttachDatabase(DatabaseInfo dbInfo)
{
string attachCmd = string.Format(
CultureInfo.InvariantCulture,
AttachDbSqlTemplate,
dbInfo.Database,
dbInfo.DataFilePath,
dbInfo.LogFilePath);
return ExecuteSqlInMaster(dbInfo, attachCmd);
}
/**//// <summary>
/// Detach database
/// </summary>
/// <param name="dbInfo"></param>
/// <returns></returns>
public static bool DetachDatabase(DatabaseInfo dbInfo)
{
string detachCmd = string.Format(
DetachDbSqlTemplate,
dbInfo.Database,
dbInfo.Database);
return ExecuteSqlInMaster(dbInfo, detachCmd);
}
/**//// <summary>
/// Drop database
/// </summary>
/// <param name="dbInfo"></param>
/// <returns></returns>
public static bool DropDatabase(DatabaseInfo dbInfo)
{
string dropSql = string.Format(
CultureInfo.InvariantCulture,
DropDbSqlTemplate,
dbInfo.Database,
dbInfo.Database);
return ExecuteSqlInMaster(dbInfo, dropSql);
}
/**//// <summary>
/// Create database from the script.
/// The content database is master.
/// </summary>
/// <param name="dbInfo"></param>
/// <returns></returns>
public static bool CreateDbFromScript(DatabaseInfo dbInfo, string scriptPath)
{
string arg = string.Format(
CultureInfo.InvariantCulture,
// CreateDbFromScriptArgumentTemplate,
GetCreateDbFromScriptArgumentTemplate(dbInfo),
dbInfo.DataSource,
scriptPath,
dbInfo.DataFilePath,
dbInfo.LogFilePath);
return (0 == RunProcess(SqlCmd, arg, true));
}
/**//// <summary>
/// Run sql script.
/// The content database is not specified, so you must use "USE *** " to specify it in the script.
/// </summary>
/// <param name="dbInfo"></param>
/// <param name="scriptPath"></param>
/// <returns></returns>
public static bool RunSqlScript(DatabaseInfo dbInfo, string scriptPath)
{
string argument = string.Format(
CultureInfo.InvariantCulture,
// RunSqlScriptTemplate,
GetRunSqlScriptTemplate(dbInfo),
dbInfo.DataSource,
scriptPath);
return (0 == RunProcess(SqlCmd, argument, true));
}
/**//// <summary>
/// Run sql. The content database is master.
/// </summary>
/// <param name="dbInfo"></param>
/// <param name="sql"></param>
/// <returns></returns>
public static bool ExecuteSqlInMaster(DatabaseInfo dbInfo, string sql)
{
string commandArg = string.Format(
CultureInfo.InvariantCulture,
//MasterDbCmdParameter,
GetMasterDbCmdParameter(dbInfo),
dbInfo.DataSource,
sql);
return (0 == RunProcess(SqlCmd, commandArg, true));
}