Enum T4Czasami w bazie danych mamy tabelkę z prostymi wartościami, które są później wykorzystywane przez ich klucze główne.

Po stronie kodu często tworzymy typy wyliczeniowe, które mapują zawartości takiej tabelki. Później zmieniając zawartość tabelki musimy także zmienić "typy wyliczeniowy" w C#.

Używając T4 jesteśmy w stanie generować automatycznie taki typ wyliczeniowy. Zmieniając zawartość tabelki zmieniam równocześnie zawartość naszego typu wyliczeniowego.

Istnieje gotowy template T4 do wykonania tego zadania.

https://code.google.com/p/enum-generator-t4/

Jest on jednak nieuzupełniony. Dlatego szybko stworze projekt, aby pokazać jak go użyć.

Projekt i baza danych SQL

Do prezentacji będzie potrzebna baza danych z przykładową tabelką. Do projektu dodać plik bazy o rozszerzeniu „.mdf”. Później kliknij projekt dwa razy na plik bazy. W „Data Connections” z menu kontekstowe w folderze „Tables” wybierz „Add New Table”. Uzupełnij tabelkę tak jak jest to pokazane na rysunku. Ważne, aby tabelka i kolumny nazywały się tak samo.

 

image

Możesz do utworzenia tabelki wykorzystać także to zapytanie.

CREATE TABLE [dbo].[DataType]
(
    [DataTypeId] INT NOT NULL PRIMARY KEY IDENTITY, 
    [Name] VARCHAR(50) NOT NULL, 
    [Description] VARCHAR(256) NULL
)

Następnie uzupełnimy tabelkę wartościami.

image

Możesz skorzystać z tego zapytania lub z kreatora.

SET IDENTITY_INSERT [dbo].[DataType] ON
INSERT INTO [dbo].[DataType] ([DataTypeId], [Name], [Description]) VALUES (1, N'Text', N'Kolumna przechowuje typ string')
INSERT INTO [dbo].[DataType] ([DataTypeId], [Name], [Description]) VALUES (2, N'Decimal', N'Kolumna przechowuje typ decimal')
INSERT INTO [dbo].[DataType] ([DataTypeId], [Name], [Description]) VALUES (3, N'Alert', N'Kolumna przechowuje typ Alert')
INSERT INTO [dbo].[DataType] ([DataTypeId], [Name], [Description]) VALUES (4, N'Date', N'Kolumna przechowuje typ daty')
SET IDENTITY_INSERT [dbo].[DataType] OFF

Tabelka „DataType” posiada następujące rekordy.

image

T4 do akcji

Jeśli jest to twoja pierwsza przygoda z T4 polecam swój wpis, który omawia od podstaw funkcję plików T4.

T4

Do projektu dodaj „Blank T4 Template”. Ważne by plik nazywał się dokładnie tak samo jak tabelka. Plik więc nazywa się „DataTypes.tt”.

image

Teraz przejdźmy do przerabiania pliku tt ze strony https://code.google.com/p/enum-generator-t4/

W Visual Studio 2012 i dalej jest problem z biblioteką „Envdte.dll”. Prawie wszystkie pliki T4 korzystają z tej biblioteki więc ta rada przyda się tobie także na przyszłość. Aby rozwiązać ten problem musisz dodać pełną ścieżkę do tej biblioteki.

C:\Program Files (x86)\Common Files\Microsoft Shared\MSEnv\PublicAssemblies\envdte.dll"

Teraz ważne są następujące zmienne.

Zmienna „tableName” określa nazwę tabelki, z której ma być wygenerowany typ wyliczeniowy. Wyrażenie pod tą zmienną pobiera nazwę pliku „.tt”. Zmienna ta więc przechowuje wartość „DataType”.

Zmienna „ColumnId” określa nazwę kolumny z kluczem głównym. Zakładamy, że kolumna nazywa się jak tabelka plus „ID”.

Zmienna „ConnectionString”. String określający połączenie z bazą danych. Połączenie te odwołuje się do wygenerowanej bazy. Ścieżka do folderu, w którym znajduje się baza będzie u ciebie inna.

<#@ template debug="true" hostSpecific="true" #>
<#@ output extension=".generated.cs" #>
<#@ Assembly Name="System.Data" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="System.Data" #>
<#@ import namespace="System.Data.SqlClient" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ Assembly Name="C:\Program Files (x86)\Common Files\Microsoft Shared\MSEnv\PublicAssemblies\envdte.dll" #>
<#
    string tableName =  Path.GetFileNameWithoutExtension(Host.TemplateFile);
    string path = Path.GetDirectoryName(Host.TemplateFile);
    string columnId = tableName + "ID";
    string columnName = "Name";
    string columnDescription = "Description";
    string connectionString = @"Data Source=(LocalDB)\v11.0;AttachDbFilename= ""d:\PanNiebieski\Documents\Visual Studio 2013\Projects\T4EnumFromDataBaseBlog\T4EnumFromDataBaseBlog\ExampleDataBase.mdf"" ;Integrated Security=True";
    string dataType = " typy danych w kolumnie";
    string shema = "[dbo].";
    // Get containing project
    IServiceProvider serviceProvider = (IServiceProvider)Host;
    DTE dte = (DTE)serviceProvider.GetService(typeof(DTE));
    Project project = dte.Solution.FindProjectItem(Host.TemplateFile).ContainingProject;
#>

Mając ten krok za sobą, zobaczy jak kod się generuje.

Łączymy się z bazą i z tabelki „DataTypes” wyciągamy wszystkie jej rekordy. Później w pętli każdy rekord tworzy wartość typu wyliczeniowego.

Kod także zawiera metodę „Pascalize”, która ma na celu usunięcie znaków specjalnych. Możesz ją zmodyfikować do własnych potrzeb.

using System;
using System.CodeDom.Compiler;
 
namespace <#= project.Properties.Item("DefaultNamespace").Value #><#= Path.GetDirectoryName(Host.TemplateFile).Remove(0, Path.GetDirectoryName(project.FileName).Length).Replace("\\", ".") #>
{
    /// <summary>
    /// <#= tableName + dataType #> 
    ///Uwaga: Auto generated enumeration
    /// </summary>
    [GeneratedCode("TextTemplatingFileGenerator", "10")]
    public enum <#= tableName #>
    {
<#
    SqlConnection conn = new SqlConnection(connectionString);
    string command = string.Format("select {0}, {1},{2}  from {3} order by {0}"
        , columnId, columnName,columnDescription, shema+tableName);
    SqlCommand comm = new SqlCommand(command, conn);
 
    conn.Open();
 
    SqlDataReader reader = comm.ExecuteReader();
    bool loop = reader.Read();
 
    while(loop)
    {
#>    /// <summary>
    /// <#= reader[columnName] +  dataType #>
    /// <#= reader[columnDescription] #>
    /// </summary>
        <#= Pascalize(reader[columnName]) #> = <#= reader[columnId] #><# loop = reader.Read(); #><#= loop ? ",\r\n" : string.Empty #>
<#
    }
#>  }
}
<#+
    private string Pascalize(object value)
    {
        Regex rx = new Regex(@"(?:[^a-zA-Z0-9]*)(?<first>[a-zA-Z0-9])(?<reminder>[a-zA-Z0-9]*)(?:[^a-zA-Z0-9]*)");

        return rx.Replace(value.ToString(),
             m => m.Groups["first"].ToString().ToUpper() +
             m.Groups["reminder"].ToString().ToLower());
    }
#>

Oto jak wygląda wygenerowany kod.

using System;
using System.CodeDom.Compiler;
 
namespace T4EnumFromDataBaseBlog
{
    /// <summary>
    /// DataType typy danych w kolumnie 
    ///Uwaga: Auto generated enumeration
    /// </summary>
    [GeneratedCode("TextTemplatingFileGenerator", "10")]
    public enum DataType
    {
    /// <summary>
    /// Text typy danych w kolumnie
    /// Kolumna przechowuje typ string
    /// </summary>
        Text = 1,

    /// <summary>
    /// Decimal typy danych w kolumnie
    /// Kolumna przechowuje typ decimal
    /// </summary>
        Decimal = 2,

    /// <summary>
    /// Alert typy danych w kolumnie
    /// Kolumna przechowuje typ Alert
    /// </summary>
        Alert = 3,

    /// <summary>
    /// Date typy danych w kolumnie
    /// Kolumna przechowuje typ daty
    /// </summary>
        Date = 4
  }
}

Jeśli chcesz utworzyć kolejny typ wyliczeniowy w ten sposób. Utwórz kolejną tabelkę o podobnej konstrukcji.

CREATE TABLE [dbo].[PersonType]
(
    [PersonTypeId] INT NOT NULL PRIMARY KEY IDENTITY, 
    [Name] VARCHAR(50) NOT NULL, 
    [Description] VARCHAR(256) NULL
)

Do pliku, który będzie nazywał się jak twoja tabelka dopisz tylko tę linijkę kodu (całą resztę możesz skasować).

<#@ include file="DataType.tt" #>

Właśnie wygenerowałeś kolejny typ wyliczeniowy.

namespace T4EnumFromDataBaseBlog
{
    /// <summary>
    /// PersonType typy danych w kolumnie 
    ///Uwaga: Auto generated enumeration
    /// </summary>
    [GeneratedCode("TextTemplatingFileGenerator", "10")]
    public enum PersonType
    {
  }
}

Pamiętaj, tylko aby najpierw uzupełnić tabelkę.