2016-11-22 1 views
2

Ich bin ein Codegenerierungstool für SQL Server-Datentools zu schreiben, und ich brauche für Lage sein, den Datentyp zu erhalten:Finding Datentypinformationen für bestimmte Objekte in DacFx API

  1. Ansicht Spalten
  2. Berechnete Spalten auf einer Tabelle

Wo ist diese Information? Für Tabellen (mit Ausnahme der berechneten Spalten), dann ist es hier:

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

Für berechnete Spalten, dataType oben ist null.

Für Ansichten, ich habe versucht:

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

Ist diese Information überall? Gibt es andere Möglichkeiten, diese Informationen zu erhalten, als die Quelle .DACPAC in einer Datenbank zu veröffentlichen?


EDIT: als Antwort auf Ed Elliot unten (in Bezug auf die Verwendung des stark typisierte DacFx Modell)

Der folgende Code nicht für die Ansicht Typinformationen zurück zu bringen:

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); 

Ich fange an zu denken, dass dies eine Einschränkung des DAC-Modells selbst ist.

Antwort

1

oooh großes Thema :)

Der einfachste Weg, um das DacFxStronglyTypedModel Objekt abzufragen verwenden, die verfügbar ist:

https://github.com/Microsoft/DACExtensions

Es ist ein wenig seltsam, dass es sich um eine Probe, die Sie bauen und dann gibt es Ihnen einen einfachen Zugriff auf den DacFx abfragen:

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

Um 210 eine stark typisierte Modell tun:

var model = new TSqlTypedModel("dacpacPath");

Dann, wenn Sie es für alle Ansichten abfragen (oder was auch immer) eine Liste von typisierte Objekte erhalten, die eine Menge „vernünftigere“ als die DacFx sind.

Die Schnittstelle, die Sie immer wieder Ansichten erhalten:

ISql120TSqlView (die Versionsnummer auf Ihre Versionsnummer ändern) eine IEnumerable von Spalten:

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

Die Säule Schnittstelle hat dann eine IEnumerable von Datatypes:

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

ich habe eine nicht bekam Windows-Maschine jetzt, um Ihnen eine vollständige Demo zu bekommen, aber das sollte genug sein, wenn Sie nicht bekommen, was Sie brauchen, setzen Sie einen Kommentar und ich werde morgen eine Probe bekommen (wenn niemand sonst in der Zwischenzeit tut).

Um die Liste der Spalten auf einem Blick zu erhalten tun:

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

Dies funktioniert für mich mit der 130-Version der Dac DLL.

Um zu den Details für berechnete Spalten zu gelangen, müssen Sie die "ExpressionDependencies" für die Spalte (siehe v.Columns) anzeigen, die mit der für Tabellen übereinstimmt.

EDIT

So habe Ich habe ich die Art ein Spiel und es gibt einige Dinge, die kann man einfach nicht erst zur Laufzeit festzustellen, so wird nicht der DacFx der Lage sein, um herauszufinden, für diejenigen, und sie nur so weiß es tatsächlich den Datensatz zu erzeugen und untersuchen, was man bekommt zurück, aber es gibt einige Dinge, die wir mit berechneten Spalten tun können, wenn wir dieses Beispiel nehmen:

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] )

für Spalten, Id, StringCol, a und b Wenn wir den stark typisierten Dacfx verwenden, können wir die Spaltentypen erhalten, indem wir folgendes tun:

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

ShowType sieht wie folgt aus:

`Leere ShowType (IEnumerable Typen) { var builder = new Stringbuilder();

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

     Console.Write(builder); 
    }` 

Was wir tun, ist für jede Spalte eine Liste von Datentypen haben, könnte es einfach so int oder etwas sein, aber es ist eine Liste.

Nun, da haben wir eine berechnete Spalte, anstatt nur den Datentyp (en) wir Hinweise auf die zugrunde liegenden Spalten haben, die wir den Datentyp erhalten können (n) aus:

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); } } }

zu weiß, wann diese Version aufgerufen werden muss:

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

 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; 

`

für den Probentisch zu bekommen wir diese Ausgabe:...

` Tabelle - [dbo] [the_table]

[dbo] [the_table] [Id ] Int [dbo]. [Die_tabelle]. [StringCol] VarChar [dbo]. [Die_tabelle]. [A] Int [dbo]. [Die_tabelle]. [B] Dezimal [dbo]. [The_table]. [a_and_b] ([a] + [b]) [dbo]. [Die_Tabelle]. [a] Int [dbo]. [Die_Tabelle]. [b] Decima l view - [dbo]. [mutli_type] `

wir müssten dann entscheiden, was der Typ ist, als eine rate sql wird eine implizite Umwandlung als Laufzeit in eine Dezimalzahl tun, aber zur Kompilierzeit denke ich nicht ist bekannt (glücklich, hier korrigiert werden!)

Wenn wir dann eine Ansicht als Beispiel:

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

wir die Spalten aus der Basistabelle, die einfach aufgezählt werden kann, aber die Objekt_Name und 123 sind etwas härter, verwenden den gleichen Code oben, aber für Ansichten erhalten wir:

[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 =

Also kein Typ für die berechneten Spalten und auf den Wert für a_and_b bekommen wir weiter brauchen würde wieder aufzuzählen, die Arten erhalten wir oben hatten.

An diesem Punkt haben Sie eine Ansicht mit Spalten, die auf Funktionen/Ausdrücke zeigen und an dieser Stelle wird es schwieriger :) Wenn Sie das obige Beispiel nehmen, könnten Sie wahrscheinlich herausfinden, was object_name zurückgibt und das aber bestimmen Wenn Sie eine nicht-deterministische Ansicht für den Daten- oder Datentyp erhalten, was tun Sie?

Wenn wir:

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

je nach Tag wir einen anderen Datentyp erhalten zurückgegeben - Doppel autsch.

Wenn Sie wirklich wissen wollten, was die Ansicht zurückgab, könnten Sie die SELECT-Elemente in der Ansicht und verwenden Sie die TSqlScript Dom, um sie in Teile zu analysieren und versuchen Sie, jedes zu ermitteln, habe ich ein Beispiel verspottet, die das getdate findet() Funktion in dieser Ansicht, um Ihnen eine Vorstellung davon zu geben, was Sie tun müssen, aber es ist nicht einfach und ich möchte nicht einmal gespeicherte Prozeduren in Betracht ziehen, in denen Sie dynamische sql übergeben können:

Volles Beispiel:

`create table [dbo].[The_table] ( [Id] INT nicht null Primärschlüssel, [StringCol] VARCHAR (234), nicht null, [a] int, [b] dezimal, [a_and_b] als [a] + [b] ) gehen Ansicht the_view als select * erstellen, object_name (4) some_name, 123 als an_int von the_table gehen Ansicht mutli_type als wählen Fall Datumsteil (Tag, getdate (create)) wenn 1 dann 100 wenn 2 dann 'Hallo' sonst getdate() Ende als Mehrfachtyp

`

` using System gehen ; mit System.Collections.Generic; mit System.IO; mit System.Linq; mit System.Text; mit System.Threading.Tasks; mithilfe von Microsoft.SqlServer.Dac.Extensions.Prototype; mithilfe von Microsoft.SqlServer.Dac.Model; mithilfe von Microsoft.SqlServer.TransactSql.ScriptDom; mit ColumnType = Microsoft.SqlServer.Dac.Model.ColumnType;

Namespace ConsoleApplication1 { class Program { static void ShowType (IEnumerable Typen) { 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); 
    } 
} 

}

`

Ich hoffe, es hilft, ich weiß, es ist kein dies nur tun, aber hoffentlich einige der Herausforderungen, erklärt :)

Ed

+0

I sah eigentlich direkt auf den Quellcode für das stark typisierte Modell ... wenn Sie ein wenig graben, stellt sich heraus, dass sie die genaue Methode verwenden, die ich bin, um die Typinformationen zu erhalten (außer dass sie GetReferencedRelationsh verwenden) ipInstances statt GetReferenced, die ich auch versucht habe). Ich werde es hier in einer Minute ausprobieren, aber ich bin überzeugt, dass ihr Code auch keine Datentypeninformationen auftauchen wird. – user1935361

+0

Ich habe meine Frage oben, keine Würfel aktualisiert :( – user1935361

+0

Ich habe zu der Antwort hinzugefügt - wenn Sie es nicht funktionieren können Sie geben einige Probe t-sql Sie versuchen, herauszufinden, und ich sollte in der Lage sein Geben Sie ein Beispiel, um es zu tun –