好吧,我找到了一个解决方案,即使用Script
每个对象的方法来生成模式,并使用EnumScript
方法 (with scriptSchema=false
) 来生成表格内容的插入。
foreach (Table tb in db.Tables)
{
if (tb.IsSystemObject == false)
{
foreach (var s in tb.Script(schemaOptions))
strings.Add(s);
if (scriptData)
{
foreach (var i in tb.EnumScript(insertOptions))
strings.Add(i);
}
}
}
我承认这个解决方案感觉有点空洞,因为我从来没有发现为什么原来的方法不起作用。这是没有诊断的修复,但仍然是修复。
至于我为什么首先写这个东西,我的数据库位于共享服务器上,没有任何方法可以获取可以离线或其他地方使用的自动备份。所以这是我的备份方案。
上面的解决方案遵循微软在此处给出的代码示例:Scripting。这种方法的问题是表是按无特定顺序编写的,但需要按照它们的依赖顺序排列,以便定义约束和插入行。无法引用尚不存在的表中的外键。
到目前为止,我最好的解决方案是使用DependencyWalker.DiscoverDependencies()
获取依赖树,DependencyWalker.WalkDependencies()
获取线性列表并遍历该列表,如下所示:
var urns = new List<Urn>();
Scripter schemaScripter = new Scripter(srv) { Options = schemaOptions };
Scripter insertScripter = new Scripter(srv) { Options = insertOptions };
var dw = new DependencyWalker(srv);
foreach (Table t in db.Tables)
if (t.IsSystemObject == false)
urns.Add(t.Urn);
DependencyTree dTree = dw.DiscoverDependencies(urns.ToArray(), true);
DependencyCollection dColl = dw.WalkDependencies(dTree);
foreach (var d in dColl)
{
foreach (var s in schemaScripter.Script(new Urn[] { d.Urn }))
strings.Add(s);
strings.Add("GO");
if (scriptData)
{
int n = 0;
foreach (var i in insertScripter.EnumScript(new Urn[] {d.Urn}))
{
strings.Add(i);
if ((++n) % 100 == 0)
strings.Add("GO");
}
}
}
...
File.WriteAllLines(path, strings);
每隔一段时间添加一个“GO”可以保持批量较小,这样 SSMS 就不会耗尽内存。
为了完成这个例子,数据库被编写了这样的脚本:
foreach (var s in db.Script(new ScriptingOptions { ScriptSchema = true }))
strings.Add(s);
strings.Add("GO");
strings.Add("use " + dbName);
strings.Add("GO");
用户、视图、存储过程的脚本如下:
foreach (User u in db.Users)
{
if (u.IsSystemObject == false)
{
foreach (var s in u.Script(new ScriptingOptions { ScriptSchema = true }))
strings.Add(s);
}
}
此代码生成的文件可用于重新创建数据库。我在一台旧笔记本电脑上设置了它,以每小时提取一次我的在线数据库的快照。穷人的日志传送/备份/镜像。