如何从 Visual Studio 扩展中获取对 SSDT 数据库项目内存模型的引用?

How to get reference to SSDT Database Project memory model from a Visual Studio Extension?

我正在尝试为 Visual Studio 2013 编写一个简单的扩展,它应该能够列出扩展用户选择的 SQL 服务器数据库项目中的所有表。为此,我需要访问数据库项目的内存模型。但我发现的唯一方法是将 DB 项目生成的已编译 .dacpac 文件加载到新的 TSqlModel 实例并查询它,但我宁愿能够使用 "live" 内存表示。我找到了 SSDT API 的教程:http://blogs.msdn.com/b/ssdt/archive/2013/12/23/dacfx-public-model-tutorial.aspx,但没有示例如何连接到 Visual Studio 中的内存模型。我还找到了一个解决此问题的教程,方法是从当前解决方案中收集所有带有 .sql 扩展名的文件并将它们的内容添加到 TSqlModel 实例中,但这不是我喜欢的方式。

这种事可能吗?如果可以,该怎么做?

谢谢, 米甲

数据库项目目前不提供可以访问模型的 Visual Studio 服务。但是,您可以通过挂接到项目系统事件并使用 API 来拥有自己的模型来模拟这一点。 Dave Ballantyne 在 this blog post 中展示了执行此操作的静态方法。如果您想让它更具动态性,您可以连接到项目事件,例如 project/file 添加、删除、更改等,并根据这些事件更新您的模型。这就是数据库项目内部发生的事情。请注意,您还应该使用 AddOrUpdateObjects API 调用而不是他使用的调用,因为这允许您在文件更改时更新它:

    public void ProcessProjectModels()
    {
        var hostServiceProvider = (IServiceProvider)this.Host;
        var dte = (DTE)hostServiceProvider.GetService(typeof(DTE));

        using (TSqlModel model = new TSqlModel(SqlServerVersion.Sql110, new TSqlModelOptions { }))
        {
            foreach (Project project in dte.Solution)
            {
                IterateThroughProject(project.ProjectItems, model);
            }

            List<TSqlObject> allTables = GetAllTables(model);
            foreach (var table in allTables)
            {
                // Do processing 
            }
        }
    }

    public List<TSqlObject> GetAllTables(TSqlModel model)
    {
        List<TSqlObject> allTables = new List<TSqlObject>();

        var tables = model.GetObjects(DacQueryScopes.All, ModelSchema.Table);
        if (tables != null)
        {
            allTables.AddRange(tables);
        }
        return allTables;
    }

    private void IterateThroughProject(ProjectItems PrjItems, TSqlModel model)
    {
        foreach (ProjectItem PrjItem in PrjItems)
        {
            if (PrjItem.ProjectItems != null)
            {
                IterateThroughProject(PrjItem.ProjectItems, model);
            }
            if (//PrjItem.Object.GetType().ToString() == "Microsoft.VisualStudio.Data.Tools.Package.Project.DatabaseFileNode" && 
                PrjItem.Name.EndsWith(".sql", StringComparison.OrdinalIgnoreCase))
            {
                // This is a sql file and will be processed
                // Note: There should be a separate API to read the live contents of this item, to avoid the need to save it
                if (!PrjItem.Saved)
                {
                    PrjItem.Save();
                }
                StreamReader Reader = new StreamReader(PrjItem.FileNames[0]);

                string Script = Reader.ReadToEnd();
                model.AddOrUpdateObjects(Script, PrjItem.Name, null);
            }
        }
    }

注意:此功能没有内置支持的原因是,要正确执行此操作,项目应提供只读模型(无法 add/update 脚本)或确保 add/update 对象调用实际上影响了项目中的相关脚本文件。如果您要对更改事件进行处理(就像项目系统支持的那样),它还应该支持事件通知。如果这对您有用(我可以想象在这种情况下会如此),那么打开一个 Connect 错误或在 Visual Studio 用户声音上提出它是让产品团队关注它的方法。

如何访问 SSDT 构建的项目内存模型。我们正在使用它来生成日志记录触发器。 先做 helper t4:

<#@ import namespace="Microsoft.SqlServer.Dac.Model" #>
<#@ assembly name="Microsoft.SqlServer.Dac.Extensions" #>
<#+
    public TSqlModel GetModel()
    {
        return Model;
    }
#>

主要 t4:

<#@ SqlModelDirective processor="SqlModelDirectiveProcessor" #>
<#@ import namespace="Microsoft.SqlServer.Dac.Model" #>
<#@ assembly name="Microsoft.SqlServer.Dac.Extensions" #>
<#@ include file="..\Helper.tt" #>
<#
blyablyablya

var allTables = GetAllTables();

blyablyablya
#>
<#+
    // Example method.
    public List<TSqlObject> GetAllTables()
    {
        List<TSqlObject> allTables = new List<TSqlObject>();

        var model = GetModel();
        if (model == null) 
            return allTables;

        var tables = model.GetObjects(DacQueryScopes.All, ModelSchema.Table);
        if (tables != null)
        {
            allTables.AddRange(tables);
        }
        return allTables;
    }
#>