在DacFx API中查找某些对象的数据类型信息

时间:2016-11-22 16:34:50

标签: sql-server sql-server-data-tools dac

我正在编写针对Sql Server数据工具的代码生成工具,我需要能够获取以下数据类型:

  1. 查看列
  2. 表格上的计算列
  3. 这些信息在哪里?对于表格(计算列除外),它在这里:

    TSqlObject table;  //retrieved elsewhere
    TSqlObject column = table.GetReferenced(Table.Columns).First(); //first column
    TSqlObject dataType = column.GetReferenced(Column.DataType).FirstOrDefault();
    

    对于计算列,上面的dataType为空。

    对于观点,我试过了:

    TSqlObject view;  //retrieved elsewhere
    TSqlObject column = view.GetReferenced(View.Columns).First(); //first column
    TSqlObject dataType = column.GetReferenced(Column.DataType).FirstOrDefault();//null
    

    此信息是否?除了将源.DACPAC发布到数据库之外,还有其他任何方法可以获取此信息吗?

    编辑:回应下面的Ed Elliot(关于使用强类型DacFx模型)

    以下代码无法恢复视图的类型信息:

    TSqlTypedModel model = new TSqlTypedModel(@"path.dacpac");
    var view = model.GetObjects<TSqlView>(Microsoft.SqlServer.Dac.Model.DacQueryScopes.UserDefined).FirstOrDefault();
    var viewcolumns = view.Columns;
    
    //false
    bool viewHasTypeInformation = viewcolumns.Any(c => c.DataType.Count() > 0); 
    
    var table = model.GetObjects<TSqlTable>(Microsoft.SqlServer.Dac.Model.DacQueryScopes.UserDefined).FirstOrDefault();
    var tablecolumns = table.Columns;
    
    //true
    bool tableHasTypeInformation = tablecolumns.Any(c => c.DataType.Count() > 0);
    

    我开始认为这是DAC模型本身的限制。

1 个答案:

答案 0 :(得分:3)

哦很棒的主题:)

使用可用的DacFxStronglyTypedModel对象进行查询的最简单方法:

https://github.com/Microsoft/DACExtensions

这有点奇怪,因为它是您构建的一个示例,然后它使您可以轻松访问查询DacFx:

https://github.com/Microsoft/DACExtensions/tree/master/DacFxStronglyTypedModel

要获得强类型模型,请执行以下操作:

var model = new TSqlTypedModel("dacpacPath");

然后,当您查询所有视图(或其他任何视图)时,您会得到一个很多类型的对象列表,这些对象很多&#34; saner&#34;而不是DacFx。

您获取视图的界面:

ISql120TSqlView(将版本号更改为您的版本号)有一个IEnumerable列:

IEnumerable<Microsoft.SqlServer.Dac.Extensions.Prototype.ISql120TSqlColumn> Columns { get; }

然后列接口有一个IEnumerable的DataTypes:

IEnumerable<Microsoft.SqlServer.Dac.Extensions.Prototype.ISqlDataType> DataType { get; }

我现在还没有一台Windows机器来为你提供一个完整的演示,但如果你不这样做,那就足够了。得到你需要的评论,明天我会得到一个样本(如果没有其他人同时做的话)。

要获取视图上的列列表,请执行以下操作:

var views = model.GetObjects<TSqlView>(DacQueryScopes.UserDefined); foreach (var v in views) { Console.WriteLine(v.Columns.Count()); }

这适用于我使用130版本的Dac dll。

要了解计算列的详细信息,您需要查看&#34; ExpressionDependencies&#34;在列上(参见v.Columns),与表格相同。

修改

所以我一直在玩游戏,有些东西在运行之前就无法确定,所以DacFx无法找出那些类型,而且我只知道它们的类型它实际上生成了记录集并检查了你得到的内容,但是如果我们采用这个例子,我们可以对计算列做些事情:

create table [dbo].[the_table] ( [Id] INT not null primary key, [StringCol] varchar(234) not null, [a] int, [b] decimal, [a_and_b] as [a] + [b] )

对于列,Id,StringCol,a和b,当我们使用强类型dacfx时,我们可以通过这样做获取列类型:

var tables = model.GetObjects(DacQueryScopes.UserDefined); foreach (var t in tables) { switch (c.ColumnType) { case ColumnType.Column: ShowType(c.DataType); break; } }

ShowType如下所示:

`void ShowType(IEnumerable类型)         {             var builder = new StringBuilder();

        foreach (var type in types)
        {
            var t = new TSqlDataType(type.Element);
            builder.Append($"{t.SqlDataType.ToString()} ");
        }

        Console.Write(builder);
    }`

我们所做的是为每列提供一个数据类型列表,它可能只是int或类似的东西,但它是一个列表。

现在因为我们有一个计算列,而不仅仅是数据类型,我们引用了基础列,我们可以从中获取数据类型:

void ShowDependencies(IEnumerable<ISqlModelElementReference> dependencies) { foreach (var dependency in dependencies) { if (dependency is TSqlColumnReference) { var column = new TSqlColumn(dependency.Element); Console.Write(column.Name + " "); ShowType(column.DataType); } } }

知道何时调用此版本:

`var tables = model.GetObjects(DacQueryScopes.UserDefined);

        foreach (var t in tables)
        {
            Console.WriteLine($"table - {t.Name}");
            foreach (var c in t.Columns)
            {
                Console.Write("\r\n" + c.Name.ToString() + " ");
                switch (c.ColumnType)
                {
                    case ColumnType.Column:
                        ShowType(c.DataType);
                        break;
                    case ColumnType.ComputedColumn:
                        Console.Write($"({c.Expression}) ");
                        ShowDependencies(c.ExpressionDependencies);
                        break;

`

关于我们得到此输出的示例表:

`table - [dbo]。[the_table]

[dbo]。[the_table]。[Id] Int [dbo]。[the_table]。[StringCol] VarChar [dbo]。[the_table]。[a] Int [dbo]。[the_table]。[b]十进制 [dbo]。[the_table]。[a_and_b]([a] + [b])[dbo]。[the_table]。[a] Int [dbo]。[the_table]。[b]十进制视图 - [dbo]。 [mutli_type]`

然后我们需要确定类型是什么,因为猜测sql将隐式转换为运行时到小数,但在编译时我不认为它是已知的(很高兴在这里得到纠正!)

如果我们以视图为例:

create view the_view as select *, object_name(4) some_name, 123 as an_int from the_table

我们有基表中的列可以简单地枚举但是object_name和123稍微有点难度,使用上面相同的代码,但对于我们得到的视图:

[dbo].[the_view].[Id] [dbo].[the_table].[Id] Int [dbo].[the_view].[StringCol] [dbo].[the_table].[StringCol] VarChar [dbo].[the_view].[a] [dbo].[the_table].[a] Int [dbo].[the_view].[b] [dbo].[the_table].[b] Decimal [dbo].[the_view].[a_and_b] [dbo].[the_table].[a_and_b] [dbo].[the_view].[some_name] some_name = an_int = [dbo].[the_view].[an_int] some_name = an_int =

因此,对于计算列没有类型加上获取a_and_b的值,我们需要再次进一步枚举以获得上面的类型。

此时您所拥有的是一个带有指向函数/表达式的列的视图,这是开始变得更难的地方:)如果您采用上面的示例,您可能可以确定哪个object_name返回并确定但是当你会得到一个对数据或数据类型不确定的视图你会做什么?

如果我们采取:

create view mutli_type as select case datepart(day, getdate()) when 1 then 100 when 2 then 'hello' else getdate() end as multitype

取决于我们返回不同数据类型的日期 - 双ouch。

如果你真的需要知道视图返回的是什么,你可以在视图中获取select元素并使用TSqlScript Dom将它们解析为部分并尝试推断每个部分,我已经模拟了一个找到getdate的示例( )在这个视图中的功能是为了让你了解你需要做什么,但它并不简单,我甚至不想考虑你可以传递动态sql的存储过程:

完整样本:

`create table [dbo]。[the_table] (     [Id] INT not null主键,     [StringCol] varchar(234)not null,     [a] int,     [b]十进制,     [a_and_b]为[a] + [b] ) 走 创建视图the_view 如 从the_table中选择*,object_name(4)some_name,123作为an_int 走 创建视图mutli_type 如     select case datepart(day,getdate())         当1             那么100         当2             然后&#39;你好&#39;         其他             GETDATE()         结束多种类 去

`

`使用System; 使用System.Collections.Generic; 使用System.IO; 使用System.Linq; 使用System.Text; 使用System.Threading.Tasks; 使用Microsoft.SqlServer.Dac.Extensions.Prototype; 使用Microsoft.SqlServer.Dac.Model; 使用Microsoft.SqlServer.TransactSql.ScriptDom; 使用ColumnType = Microsoft.SqlServer.Dac.Model.ColumnType;

命名空间ConsoleApplication1 {     课程     {         static void ShowType(IEnumerable类型)         {             var builder = new StringBuilder();

        foreach (var type in types)
        {
            var t = new TSqlDataType(type.Element);
            builder.Append($"{t.SqlDataType.ToString()} ");
        }

        Console.Write(builder);
    }

    static void ShowDependencies(IEnumerable<ISqlModelElementReference> dependencies)
    {
        foreach (var dependency in dependencies)
        {
            if (dependency is TSqlColumnReference)
            {
                var column = new TSqlColumn(dependency.Element);
                Console.Write(column.Name + " ");
                ShowType(column.DataType);
            }
        }
    }

    static void Main(string[] args)
    {

        var model = new TSqlTypedModel(@"path\Da.dacpac");

        var views = model.GetObjects<TSqlView>(DacQueryScopes.UserDefined);

        var tables = model.GetObjects<TSqlTable>(DacQueryScopes.UserDefined);

        foreach (var t in tables)
        {
            Console.WriteLine($"table - {t.Name}");
            foreach (var c in t.Columns)
            {
                Console.Write("\r\n" + c.Name.ToString() + " ");
                switch (c.ColumnType)
                {
                    case ColumnType.Column:
                        ShowType(c.DataType);
                        break;
                    case ColumnType.ComputedColumn:
                        Console.Write($"({c.Expression}) ");
                        ShowDependencies(c.ExpressionDependencies);
                        break;
                    case ColumnType.ColumnSet:
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }
        }


        foreach (var v in views)
        {
            Console.WriteLine($"view - {v.Name}");

            foreach (var c in v.Columns)
            {
                Console.Write("\r\n" + c.Name.ToString() + " ");

                var needDomParse = false;

                switch (c.ColumnType)
                {
                    case ColumnType.Column:

                        ShowType(c.DataType);
                        ShowDependencies(c.ExpressionDependencies);

                        break;
                    case ColumnType.ComputedColumn:

                        ShowType(c.DataType);
                        ShowDependencies(c.ExpressionDependencies);

                        if (!c.DataType.Any() && !c.ExpressionDependencies.Any())
                        {
                            needDomParse = true;
                        }

                        break;
                    case ColumnType.ColumnSet:
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }

                if (needDomParse)
                {
                    //ouch

                    var create = new CreateViewStatement();
                    var parser = new TSql130Parser(false);
                    IList<ParseError> errors;
                    var fragment = parser.Parse(new StringReader(v.GetScript()), out errors);
                    var selectVisitor = new SelectVisitor();
                    fragment.Accept(selectVisitor);

                    foreach (var s in selectVisitor.Selects)
                    {
                        var spec = s.QueryExpression as QuerySpecification;
                        foreach (var element in spec.SelectElements)
                        {
                            var select = element as SelectScalarExpression;
                            if (select != null)
                            {
                                Console.Write(select.ColumnName.Value + " = ");
                                var caseExpression = select.Expression as SimpleCaseExpression;
                                if (caseExpression != null)
                                {
                                    var func = caseExpression.ElseExpression as FunctionCall;
                                    Console.WriteLine(func.FunctionName.Value);
                                }

                            }
                        }
                    }
                }
            }
        }
    }
}

internal class SelectVisitor : TSqlConcreteFragmentVisitor
{
    public List<SelectStatement> Selects = new List<SelectStatement>();
    public override void Visit(SelectStatement node)
    {
        Selects.Add(node);
        base.Visit(node);
    }
}

}

`

我希望它有所帮助,我知道它不仅仅是这样做,但希望能够解释一些挑战:)