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

时间:2015-02-16 22:58:28

标签: c# visual-studio visual-studio-extensions sql-server-data-tools envdte

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

这样的事情有可能吗?如果是这样,怎么做?

谢谢你, 米甲

2 个答案:

答案 0 :(得分:2)

数据库项目目前不提供可访问模型的Visual Studio服务。但是,您可以通过挂钩项目系统事件并使用API​​来拥有自己的模型来模拟这一点。 Dave Ballantyne在this blog post中展示了一种静态方法。如果您要使其更加动态,您可以连接项目事件,例如项目/文件添加,删除,更改等,并根据这些事件更新您的模型。这就是数据库项目内部发生的事情。请注意,您还应该使用AddOrUpdateObjects API调用而不是他使用的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);
            }
        }
    }

注意:此功能没有内置支持的原因是,为了正确执行,项目应提供只读模型(无法添加/更新脚本)或确保添加/更新对象调用实际上会影响项目中的相关脚本文件。如果您要对更改事件进行处理(就像项目系统支持一样),它也应该支持事件通知。如果这对你有用(正如我可以想象的那样),那么打开Connect bug或者在Visual Studio用户语音上提高它就是在产品团队雷达上实现这一点的方法。

答案 1 :(得分:0)

如何访问SSDT构建的项目内存模型。我们正在使用它来生成日志记录触发器。 首先做助手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;
    }
#>