2016-11-22 8 views
2

Я пишу инструмент генерации кода против Tools Sql Server Data и мне нужно, чтобы иметь возможность получить тип данных для:Нахождение определенных объектов в DacFx API

  1. Просмотр столбцов
  2. Вычисляемые столбцы на таблице

Где эта информация? Для таблиц (за исключением вычисляемых столбцов), это здесь:

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 в базе данных?


EDIT: в ответ на Ed Эллиот ниже (относительно использования сильно типизированных модели 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); 

Я начинаю думать, что это ограничение самой модели ЦАП.

ответ

1

ооо большая тема :)

Самый простой способ запроса с помощью объекта DacFxStronglyTypedModel, который доступен:

https://github.com/Microsoft/DACExtensions

Это немного странно, что это образец, который вы строите и то это дает вам легкий доступ для запроса DacFx:

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

Для того, чтобы получить сильно типизированных модель сделать:

var model = new TSqlTypedModel("dacpacPath");

Затем, когда вы запрашиваете его для всех видов (или любой другой), вы получите список типизированных объектов, которые много «здравомыслящим», чем DacFx.

интерфейс, который вы получите обратно для просмотров:

ISql120TSqlView (изменить номер версии на ваш номер версии) имеет IEnumerable столбцов:

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

Интерфейс колонки затем IEnumerable из Datatypes:

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

Я не получил оконный компьютер прямо сейчас, чтобы получить полную демоверсию, но этого должно быть достаточно, если вы не получите то, что вам нужно, добавьте комментарий, и я получу образец завтра (если никто еще не сделает это в то же время).

Чтобы получить список столбцов на вид сделать:

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

Это работает для меня с версией Дак DLL файлы 130.

Чтобы получить информацию о вычисляемых столбцах, вам необходимо просмотреть «ExpressionDependencies» в столбце (см. V.Columns), который является таким же, как и для таблиц.

EDIT

Так что я был с игрой, и есть некоторые вещи, которые вы просто не можете определить только во время выполнения, поэтому 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, а и b, когда мы используем строго типизированный dacfx, мы можем получить типы столбцов, выполнив следующее:

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

ShowType выглядит следующим образом:

`ничтожной ShowType (IEnumerable типов) { вар строитель = новый 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.Определяемые пользователем);

 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; 

`

для образца таблицы о мы получаем следующий результат:...

` таблицы - [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] Decima l view - [dbo]. [mutli_type] `

Нам нужно было бы решить, что это за тип, поскольку предположение sql будет делать неявное преобразование в качестве среды выполнения в десятичную, но во время компиляции я не думаю, что это известен (рад исправить здесь!)

Если затем принять вид в качестве примера:

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

мы имеем столбцы из базовой таблицы, которые могут быть перечислены просто, но в имя_объект и 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

в зависимости от дня мы получаем другой тип данных, возвращаемый - двойной Уч.

Если вам действительно нужно знать, что получилось, вы можете получить элементы выбора в представлении и использовать TSqlScript Dom для анализа их на части и попробовать и сделать вывод о каждом из них, я издевался над образцом, который находит getdate() в этом представлении, чтобы дать вам представление о том, что вам нужно сделать, но это не просто, и я даже не хочу рассматривать хранимые процедуры, в которых вы можете перейти в динамическом sql:

Полный образец:

`create table [dbo].[The_table] ( [Id] INT не нулевой первичный ключ, [StringCol] VARCHAR (234) не равно нулю, [а] Int, [Ь] десятичную, [a_and_b], как [а] + [Ь] ) пойти создать вид the_view в выберите *, object_name (4) некоторое_имя, 123, как an_int из the_table пойти создать вид mutli_type как выберите случай DATEPART (день, GetDate()) когда 1 затем 100 , когда 2 , затем 'hello' else GETDATE() конец, как многотипные идти

`

` с помощью системы; с использованием 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 { класс Программа { статической силы ShowType (IEnumerable типов) { вар строитель = новый 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); 
    } 
} 

}

`

Я надеюсь, что это помогает, я знаю, что это не просто сделать это, но мы надеемся, объясняет некоторые из проблем :)

Ed

+0

I на самом деле смотрел прямо на исходный код для сильно типизированной модели ... если вы копаете немного, оказывается, что они используют точный метод для получения информации о типе (за исключением того, что они используют GetReferencedRelationsh ipInstances вместо GetReferenced, который я также пробовал). Я попробую это через минуту, но я убежден, что их код также не будет содержать информацию о типе данных. – user1935361

+0

Я обновил свой вопрос выше, не купил :( – user1935361

+0

Я добавил к ответу - если вы не получите его работу, можете ли вы дать образец t-sql, который вы пытаетесь выяснить, и я должен уметь дайте вам образец для этого –