Featured image of post Własny szablon projektu .Net

Własny szablon projektu .Net

Dziś o tym, co powtarzałem dotychczas najczęściej, czyli jak utworzyć projekt i mieć od razu zainstalowane wszystkie ulubione biblioteki. Inaczej mówiąc: o tym, jak zrobić własny szablon projektu, który możemy wykorzystać wraz z komendą dotnet new lub w Visual Studio

Spis treści

Jeśli szukasz konkretnych przykładów lub bardziej zaawansowanych rozwiązań to zapraszam do tego repozytorium Microsoftu: https://github.com/dotnet/dotnet-template-samples.

Dwa sposoby tworzenia szablonów

Wyróżniam dwa sposoby na tworzenie szablonów:

  • prosty - gdzie wrzucamy do katalogu pliki, których chcemy używać, a polecenie dotnet new po prostu przekopiuje pliki w nowe miejsce i ewentualnie zmieni ich zawartosć,

  • Nugetowy - gdzie możemy utworzyć własną paczkę z szablonami i swobodnie ja dystrybuować w postaci nuget’a. Do tego oferuje ona możliwość załadowania wielu szablonów w jedną paczkę.

Prosty

Aby utworzyć prosty szablon należy:

  1. Utworzyć katalog, w którym znajdą się rzeczy, które trafią do szablonu – nazwijmy go simplest – będzie to nasz bieżący katalog roboczy.

  2. Wewnątrz katalogu roboczego utwórz następny o nazwie .template.config, a wewnątrz katalogu .template.config utwórz plik o nazwie template.json

    Po wykonaniu tych dwóch kroków na pulpicie powinniśmy otrzymać taką strukturę katalogów

    Szablonowa struktura plików
    Pulpit/
    └── simplest/
        ├── Installer.cs
        └── PersonComponent.Core.csproj
  3. Do pliku template.json wklej zawartość:

    Plik konfigurujący nasz szablon
    {
        "$schema": "http://json.schemastore.org/template",
        "author": "Zaprogramuj To Raz!",
        "classifications": [ "Common", "Console" ],
        "identity": "ZTR.CoreTemplate.CSharp",
        "name": "Program It Once core template",
        "shortName": "ztr-core",
        "sourceName": "PersonComponent"
      }
  4. Do katalogu roboczego dodaj dowolny plik, na przykład plik projektu PersonComponent.Core.csproj oraz plik klasy C# o nazwie Installer.cs o treści:

    Przykładowy plik projektu PersonComponent.Core.csproj
    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <TargetFramework>net5.0</TargetFramework>
      </PropertyGroup>
      <ItemGroup>
        <PackageReference Include="FluentValidation" Version="10.3.3" />
        <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.2" />
      </ItemGroup>
    </Project>
    Przykładowy plik klasy C# Installer.cs
    using System;
    using Microsoft.Extensions.DependencyInjection;
    
    namespace PersonComponent.Core
    {
        public static class Installer
        {
            public static void InstallPersonComponentCore(this IServiceCollection services)
            {
            }
        }
    }
  5. Teraz przejdź w konsoli do katalogu nadrzędnego dla katalogu roboczego i wywołaj tam polecenie dotnet new -i .\simplest\. Powinieneś na końcu zobaczyć wypis:

    Nazwa szablonu                 Skrócona nazwa  Język  Tagi
    -----------------------------  --------------  -----  --------------
    Program It Once core template  ztr-core               Common/Console

    Zwróć uwagę jak z tym, co wyświetla się na ekranie, korelują wartości shortName, name oraz classifications z pliku template.json. Szczegółowo rozpiszę się o tym później.

  6. Teraz największa magia. Utwórz gdzieś indziej nowy katalog o nazwie MójProjekt i przejdź do niego w konsoli, gdzie wywołaj komendę dotnet new ztr-core.

    Zobaczysz, że do katalogu została skopiowana zawartość szablonu, ale ze zmienionymi nazwami!

    Wynikowa struktura plików
    Pulpit/
    └── MójProjekt/
        ├── Installer.cs
        └── MójProjekt.Core.csproj

    Dodatkowo zawartość pliku Installer.cs też została zmieniona! Jak to się stało? Otóż odpowiada za to linijka "sourceName": "PersonComponent" z pliku konfiguracyjnego, która wskazuje jaki ciąg znaków ma zostać zamieniony na nazwę projektu, którą podajemy podczas używania szablonu w komendzie dotnet new ztr-core -n "MójProjekt". Ale my nie podaliśmy końcówki -n MójProjekt – ta została domyślnie wzięta z nazwy katalogu.

    Silnik szablonów samodzielnie przeszuka wszystkie pliki, które zawiera i zamieni ciąg znaków PersonComponent na nazwę projektu. Nawet jeśli występuje ona w nazwie pliku. Możliwe jest jeszcze więcej modyfikacji, ale one wykraczają poza ten artykuł, a przykłady ich użycia znajdziesz w repozytorium podanym na samym początku.

  7. Wadą tego rozwiązania jest to, że cały czas polega ono na katalogu źródłowym szablonu. Dodaj do katalogu simplest nowy plik dumb.txt nawet bez zawartości, a zobaczysz, że przy ponownym wywołaniu metody dotnet new ztr-core ten również zostanie przekopiowany.

  8. Teraz usuń lub zmień nazwę katalogu źródłowego (simplest) i spróbuj ponownie utworzyć nowy projekt (dotnet new ztr-core)

    PS> dotnet new ztr-core
    Nie można zlokalizować określonej zawartości szablonu, ponieważ pamięć podręczna szablonu może być uszkodzona. Spróbuj uruchomić polecenie "dotnet Microsoft.TemplateEngine.Cli.CommandParsing.BaseCommandInput--debug:reinit", aby rozwiązać problem.

    I wszystko przestaje działać. Dlatego takie podejście jest dobre na początek, gdy poznajemy silniki oraz ewentualnie dla jakiś specyficznych lokalnych szablonów.

    Jeszcze raz podkreślę, co zostało napisane powyżej. Szablonem prostym jest wszystko, co znajdzie się w katalogu źródłowym! A przeniesienie katalogu źródłowego powoduje, że przestanie on działać.
  9. Aby odinstalować szablon użyj polecenia dotnet new -u «pełna ścieżka do szablonu»

    Co jeśli nie pamiętasz, gdzie jest twój szablon? Użyj polecenia dotnet new -u bez żadnych dodatkowych elementów. Wyświetli Ci się lista szablonów, które możesz odinstalować wraz z odpowiednim poleceniem:

    Widok pomocy jak odinstalować szablony
    PS> dotnet new -u
        C:\Users\md\Desktop\simplest
          Szablony:
             Program It Once core template (ztr-core)
          Polecenie odinstalowania:
       dotnet new --uninstall C:\Users\md\Desktop\simplest

    Po czym wystarczy skopiować ostatnią linijkę wpisu dotyczącą szablonu, który chcemy usunąć i voilà – z powrotem mamy czysto.

Nugetowy

Nugetowy szablon ma dwie zasadnicze zalety:

  • Może być spakowany do postaci nuget’a,

  • Może być paczką szablonów, czyli zawierać w sobie wiele pojedynczych, całkowicie różnych szablonów dostarczanych w jednym pliku.

    1. Aby utworzyć taką paczkę, utwórz drzewo katalogów z plikami jak poniżej:

      Struktura katalogów szablonów do spakowania w formie nugetowej
      MojaPaczkaSzablonów/
      ├── ZtrDotnetTemplates.csproj (1)
      └── templates/
          ├── ConsoleTemplate/
          │   └── .template.config/
          │       └── template.json (2)
          └── CoreTemplate/
              └── .template.config/
              │   └── template.json (2)
              ├── Installer.cs (3)
              └── PersonComponent.Core.csproj (3)
      1 To plik projektu, który jest naszą paczką szablonów,
      2 to pliki opisujące dane szablon. Na każdy szablon musi przypadać jeden taki plik,
      3 pliki, które będą dołączone do naszych szablonów.

      Katalog ConsoleTemplate zostawiam pusty (poza plikiem konfiguracyjnym) w celu ograniczenia rozmiaru tego przykładu.

    2. Uzupełnij plik projektu ZtrDotnetTemplates.csproj:

      Zawartość pliku ZtrDotnetTemplates.csproj
      <Project Sdk="Microsoft.NET.Sdk">
        <PropertyGroup>
          <PackageType>Template</PackageType> (1)
      
          <PackageVersion>1.0</PackageVersion> (2)
          <PackageId>ZTR.Utilities.Templates</PackageId>
          <Title>Zaprogramuj to raz! Core template</Title>
          <Authors>Zaprogramuj to raz!</Authors>
          <Description>Template for creating core projects</Description>
          <PackageTags>dotnet-new;ztr;templates</PackageTags>
          
          <IncludeContentInPack>true</IncludeContentInPack>
          <IncludeBuildOutput>false</IncludeBuildOutput>
          <ContentTargetFolders>content</ContentTargetFolders>
      
          <TargetFramework>net5.0</TargetFramework> (3)
        </PropertyGroup>
      
        <ItemGroup>
          <Content Include="templates\**\*" Exclude="templates\**\bin\**;templates\**\obj\**" /> (4)
          <Compile Remove="**\*" />
        </ItemGroup>
      </Project>
      1 Zwróć uwagę na specyficzny typ projektu Template. Jest on niezbędny do poprawnego utworzenia paczki.
      2 W tym miejscu zaczynają się standardowe pola do opisu pakietu Nuget.
      3 TargetFramework w tym przypadku określa wersję .Net używaną do budowania paczki nugetowej z szablonami.
      4 Zwróć uwagę, że załączone będą wszystkie szablony znajdujące się w katalogu templates z wyjątkiem plików znajdujących się w katalogach bin i obj.
    3. Uzupełnij plik konfiguracyjny szablonu konsolowego templates/console/.template.config/template.json

      Zawartość pliku console/.template.config/template.json
      {
          "$schema": "http://json.schemastore.org/template",
          "author": "Zaprogramuj To Raz!",
          "classifications": [ "Common", "Console" ], 
          "identity": "ZTR.ConsoleTemplate.CSharp",
          "name": "Program It Once Console template", 
          "shortName": "ztr-console", 
          "tags": {
            "language": "C#", 
            "type": "project"
          }
        }

      Nie różni się on zbytnio od tego, co widzieliśmy w poprzednim przykładzie, z tym że tutaj dodałem informację na temat języka wykorzystywanego w szablonie.

    4. Uzupełnij pliki templates/core/.template.config/template.json, templates/core/Installer.cs oraz templates/core/PersonComponent.Core.csproj

      Pliki te są identyczne z tymi samymi, które zostały wypisane powyżej, dlatego tylko je tu wkleję bez dalszego omówienia.

      Plik templates/core/.template.config/template.json
      {
          "$schema": "http://json.schemastore.org/template",
          "author": "Zaprogramuj To Raz!",
          "classifications": [ "Common",  "Console", "Library" ],
          "identity": "ZTR.CoreTemplate.CSharp",
          "name": "Program It Once core template",
          "shortName": "ztr-core",
          "tags": {
            "language": "C#",
            "type": "project"
          },
          "sourceName": "PersonComponent"
        }
      Plik templates/core/PersonComponent.Core.csproj
      <Project Sdk="Microsoft.NET.Sdk">
      
        <PropertyGroup>
          <TargetFramework>net5.0</TargetFramework>
        </PropertyGroup>
      
        <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
          <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
          <WarningsAsErrors />
        </PropertyGroup>
      
        <ItemGroup>
          <PackageReference Include="FluentValidation" Version="10.3.3" />
          <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.2" />
        </ItemGroup>
      
      </Project>
      Plik templates/core/Installer.cs
      using System;
      using System.Collections.Generic;
      using System.ComponentModel.Design;
      using System.Linq;
      using System.Text;
      using System.Threading.Tasks;
      using Microsoft.Extensions.DependencyInjection;
      
      namespace PersonComponent.Core
      {
          public static class Installer
          {
              public static void InstallPersonComponentCore(this IServiceCollection services)
              {
                  // Here add your services from core
                  // services.AddTransient<IPersonService, PersonService>();
                  // services.AddTransient<IValidator<PersonModel>, PersonValidator>();
              }
          }
      }
    5. Zbuduj i zainstaluj nugeta!

      W katalogu MojaPaczkaSzablonów uruchom polecenie dotnet pack. Przejdź następnie do folderu ./bin/debug/, a tam zobaczysz plik o nazwie ZTR.Utilities.Templates.1.0.0.nupkg. Aby zainstalować tę paczkę, skorzystaj z polecenia dotnet new -i ZTR.Utilities.Templates.1.0.0.nupkg.

      Proces budowania i instalacji paczki
      ~\MojaPaczkaSzalonów> dotnet pack
      ...
      Pomyślnie utworzono pakiet "~\MojaPaczkaSzalonów\bin\Debug\ZTR.Utilities.Templates.1.0.0.nupkg".
      ~\MojaPaczkaSzalonów> cd .\bin\Debug\
      ~\MojaPaczkaSzalonów> dotnet new -i .\ZTR.Utilities.Templates.1.0.0.nupkg
      Sukces: ZTR.Utilities.Templates::1.0.0 zainstalował następujące szablony:
      Nazwa szablonu                    Skrócona nazwa  Język  Tagi
      --------------------------------- --------------- -----  ----------------------
      Program It Once Console template  ztr-console     [C#]   Common/Console
      Program It Once core template     ztr-core        [C#]   Common/Console/Library
    6. Używaj!

      Zainstalowanych pakietów używa się tak samo (np. poleceniem dotnet new ztr-console), z tym że teraz zmiana katalogu źródłowego nie psuje nam naszego szablonu! Jeśli chcemy odinstalować paczkę, robimy to poleceniem dotnet new -u .\ZTR.Utilities.Templates.1.0.0.nupkg.

Podsumowanie

  • Myślę, że własny szablon projektu może być bardzo przydatnym narzędziem, nie tylko w przypadku projektów .Netowych, ale także w innych rozwiązaniach, niezwiązanych z żadnym językiem programowania.

  • Pamiętaj, że ten artykuł to dopiero zalążek, a cały silnik szablonów ma jeszcze wiele możliwości, które możesz odkryć w linkach poniżej – znajdują się tam, między innymi, adresy repozytoriów związane z tym projektem.

  • Zwróć uwagę, że całość będzie jeszcze przyjemniejsza, gdy własne szablony połączysz CI/CD (np. na Githubie) co da Ci dostęp do twoich ulubionych wzorców niemalże wszędzie!

Odnośniki

comments powered by Disqus
Proszę pamiętaj, że blog jest na ten moment w wersji poglądowej i może zawierać wiele błędów (ale nie merytoycznych!). Mimo to mam nadzieję, że blog Ci się podoba! Wiele ilustracji pojawiło się na blogu dzięki unsplash.com!
Zbudowano z Hugo
Motyw Stack zaprojektowany przez Jimmy