O bibliotece DbUp pisałem tutaj:
DBUP – AKTUALIZACJA BAZ DANYCH
Z kolei o szablonach T4 stworzyłem cały mini-cykl, który można znaleźć tutaj.
Problem z DbUp jest taki, że wywołuje on po prostu kolejne skrypty. Następnie sami za pomocą np. Dapper, musimy podać nazwę procedury czy tabeli na której chcemy operować. DbUp + Dapper to częsta kombinacja narzędzi. Jeśli korzystamy z rozbudowanego ORM, takiego jak EntitityFramework, wtedy wszystkie tabele czy nazwy procedur i tak są silnie typowane ponieważ framework je wygeneruje. Za pomocą EF Migrations, można uzyskać to samo co robi DbUp.
Jeśli jednak korzystamy tylko z Dapper+DbUp, wciąż możemy mieć silnie typowane nazw. Załóżmy że w projekcie DbUp mamy następującą strukturę skryptów:
-> StoredProcs
—>1. GetFirstNameById
—>2. GetData
—>3. UpdateData
->Functions:
—>1. Split
—>2. MaxData
->Types
—>1. CustomType1
—>2. CustomType2
Wtedy można pokusić się o następujący szablon T4:
<#@ template debug="false" hostspecific="true" language="C#" #> <#@ assembly name="System.Core" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.IO" #> <#@ import namespace="System.Text" #> <#@ import namespace="System.Collections.Generic" #> <#@ output extension=".cs" #> namespace Data.Sql { <# string[] foldersToGenerate = new [] { "StoreProcs", "Functions","Types" }; string scriptsPath = Host.ResolvePath(@"..\Database\Scripts"); foreach(var folderName in foldersToGenerate) { WriteLine(string.Format("\tinternal static class {0}\n\t{{",folderName)); foreach(var directory in System.IO.Directory.GetDirectories(scriptsPath, "*" + folderName, SearchOption.AllDirectories)) { foreach(var fileName in Directory.GetFiles(directory)) { string name = Path.GetFileNameWithoutExtension(fileName).Split(' ').Last(); string declaration = string.Format("\t\tinternal const string {0} = \"{0}\";", name); WriteLine(declaration); } } WriteLine("\t}"); } #> }
Szablon przejdzie automatycznie po wszystkich plikach i wygeneruje statyczną klasę zawierającą stałe, np.:
internal static class StoreProcs { internal const string GetFirstNameById="GetFirstNameById"; internal const string GetData="GetData"; internal const string UpdateData="UpdateData"; }
Rozwiązanie zakłada, że nazwa pliku pokrywą się z nazwą zasobu (czyli procedury, funkcji czy typu). Analogicznie sprawa wygląda z tabelami. Myślę, że jest to lepsze rozwiązanie niż hardcodowanie za każdym razem procedury.