.NET Core Tutorial: Using the ServiceCollection Extension Pattern

If you have worked on applications (in .NET Core) you should know how Dependency Injection (DI) is supported and is applied for your services. This is straight forward and not so hard at all. Just add them to the IServiceCollection in the ConfigureServices() pipeline in the startup.cs file. Considering a N-Tier architrecture, where our Startup.cs is in the Project.Host project and mall example:

// This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<IService, Service>();
            services.AddScoped<IProcessor, Processor>();
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        }

Now, in the code snippet above, we just add a service, a processor and the IHttpContextAccessor to the DI container. As the application grows and grows, more and more services will be added here and on a long term, this will just bloat up the startup.cs file.

For example, there is a new project added to the solution Project.Reporting which hold services like IPdfReportGeneratorService and a IPdfReportConverterService, and maybe a few more for demonstration purposes. What you would normally do is just add a reference to the reporting project and just add those services to the container. You can see already how this is adding up to the pipeline.

using Solution.Project.Reporting.Services;

// This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<IPdfReportGeneratorService , PdfReportGeneratorService >();
            services.AddTransient<IPdfReportConverterService, PdfReportConverterService>();
            services.AddTransient<IServiceThis, ServiceThis>();
            services.AddTransient<IServiceThat, ServiceThat>();
            services.AddTransient<IServiceOther, ServiceOther>();

            services.AddTransient<IService, Service>();
            services.AddScoped<IProcessor, Processor>();
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        }

We can clean this up a bit. The Service Collection Extension Pattern is exactly what it says. We add our services in a extension method in its own project. The goal is to keep this pipeline as clear as possible. Let’s do this for the Project.Reporting library in the aplication.

Inside the reporting project, we add a new static class called ReportingServiceCollectionExtensions at the root of the project. Inside we create a extension method where we extend the IServiceCollection and add the services. We also return the services – because the service registration implements the Chain Pattern.

using Microsoft.Extensions.DependencyInjection;

public static class ReportingServiceCollectionExtensions
{
    public static IServiceCollection AddReporting(this IServiceCollection services)
    {
        services.AddTransient<IPdfReportGeneratorService , PdfReportGeneratorService >();
            services.AddTransient<IPdfReportConverterService, PdfReportConverterService>();
            services.AddTransient<IServiceThis, ServiceThis>();
            services.AddTransient<IServiceThat, ServiceThat>();
            services.AddTransient<IServiceOther, ServiceOther>();

        return services;
    }
}

Now we can easily call this extension in our RegisterServices pipeline in the startup.cs file in the host project by calling services.AddReporting();. If you do this for each project/assembly/layer in your solution, this keeps the startup file nice and clean.

using Solution.Project.Reporting.Services;

// This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddReporting();

            services.AddTransient<IService, Service>();
            services.AddScoped<IProcessor, Processor>();
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        }

I hope you understand what the Service Collection Extension Pattern is now and how to implement it. If you have any feedback or questions, do not hesitate to comment.

AutoTerminal – A VS2019 extension.

A friend of me had a little rant while developing an Angular app on a .NET Core backend. Whenever you had to run the npm run E2E command or any other npm or ng command, you had to right click on the folder node in solution explorer, open with file explorere, open cmd and maybe navigate to the right directory (cd ..).

This made me think of another fun little project to work on. Figuring out a way to open the terminal directly in the directory you want, straight from Visual Studio’s solution explorer. Just right click a folder, click ‘Open Terminal’ and there it is.

You can get it from the Marketplace & the source code is at GitHub.

Migrate your ASP.NET Core 2.2 application to ASP.NET Core 3.0 : Basic steps

The newest version of ASP.NET Core has released, and for for a lot of running applications this update will be somehow mandatory. This article will address important steps when upgrading your ASP.NET Core application from 2.2 to 2.3.

The project (.csproj) file

First thing first is to update the Target Framework to the netcoreapp3.0. There’s a long list of packages that are no longer being produced which can be removed from the csproj referenced packages, they are no longer available through the Microsoft.AspNetCore.App framework.

<TargetFramework>netcoreapp3.0</TargetFramework>

<!-- Microsoft.NET.Sdk.Web SDK implicitly references the Microsoft.AspNetCore.App -->

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.0</TargetFramework>
  </PropertyGroup>
    ...
</Project>

If your project targets the Razor SDK should add the framework reference to Microsoft.AspNetCore.App

<ItemGroup>
    <FrameworkReference Include="Microsoft.AspNetCore.App" />
  </ItemGroup>
    ...

For more info about what package references are removed from the Microsoft.AspNetCore.App and what to do to continue using features provided by there packages, check out the official docs.

Kestrel

In Program.cs, migrate the Kestrel confguration to the WebHostBuilder.

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.ConfigureKestrel(serverOptions =>
            {
                // Set properties and call methods on options
            })
            .UseStartup<Startup>();
        });

JSON!

Json.NET has been removed from the ASP.NET Core shared framework. In 3.0 we get the System.Text.Json namespace build in ASP.NET Core 3.0 ! Note that if you target .NET Standard or NET Framework install the System.Text.Json NuGet package. Below is a simple and clear example.


using System;
using System.Text.Json;
using System.Text.Json.Serialization;
 
namespace Project.Component.Service.Utils
{
    class Thing
    {
        public string Name { get; set; }
        public int Count{ get; set; }
    }
     
    class Program
    {
        static void Main(string[] args)
        {
            var thingy = new Thing() 
                { 
                    Name = "Tim", 
                    Count = 30,
                };

            Console.WriteLine(JsonSerializer.Serialize<Thing>(thingy));
        }
    }
}

Startup.cs

Some changes must be or can be made to the Startup.cs configuration and services pipelines.

MVC Service Registration

In Startup.ConfigureServices there are some new options to register MVC scenarios. For example, there are new extensions on IServiceCollection available, these use new methods other than UseMvc(). However, you can still use UseMvc().

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(); // Controller & API features
    services.AddControllersWithViews();
    services.AddRazorPages(); // Razor pages and minimal controller support
}

Routing

Best practice in ASP.NET Core 3.0 when using SignalR is to migrate to Endpoint Routing. This -in most cases- only takes some adjustments to Startup.cs.

public void Configure(IApplicationBuilder app)
{
    app.UseStaticFiles();

    app.UseRouting();

    app.UseCors();

    app.UseAuthentication();
    app.UseAuthorization();
    

    // App.UseSignalR and App.UseMvc is now configured on app.UseEndPoints.
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapHub<ChatHub>("/chat");
        endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
    });

For most apps, calls to UseAuthentication, UseAuthorization, and UseCors must appear between the calls to UseRouting and UseEndpoints to be effective. – Docs

Most of these changes do not create any breaking changes to business logic in your application, at least it didn’ when I tested. Let me know if this helped you or if you have any questions.

Tutorial: Generate seperate files from a T4 Template

When you run the custom tool of a T4 Template, it generates code in a child node of that template. On StackOverflow I have seen a a lot of devs asking how to write to separate files, or even to a different folder. This is actually easy to achieve, and there are multiple possible solutions. In this article, I will address how I achieved this in my TexTran projects.

Generation environment

In order to have control of the output, we must know how to fetch it. You can access the currently output by calling this.GenerationEnvironment inside <# Control blocks #> . But what is GenerationEnvironment ?

TextTemplate.GenerationEnvironment is the string builder that generation-time code is using to assemble generated output

MSDN

Write to separate file

So, this.GenerationEnvironment is basically a stringBuilder holding the generated string that is our output. We have to write this a new file. For this, just add this simple method to your T4 <#+ Class Feature Block #> .

public void SaveFile(string folder, string fileName, string content)
{
	using (FileStream fs = new FileStream(Path.Combine(folder, fileName.Trim() + ".cs"), FileMode.Create))
    {
        using (StreamWriter str = new StreamWriter(fs))
        {
            str.WriteLine(content);
            str.Flush();
        }
    }
}

This is a simple method that takes the desired path, file name and the filename and uses a StreamWriter object to write your files to a specific location.

Inside your T4 Template, at the bottom (under the last output block) you can add the following lines

<#
	SaveFile(path, fileName, this.GenerationEnvironment.ToString());
	this.GenerationEnvironment.Remove(0, this.GenerationEnvironment.Length);
#>

What this does, is it writes the string content of the GenerationEnvironment StringBuilder to the path specified. Then we remove the full string value from the GenerationEnvironment so that there is no output left for the transformer to write to the child node.

Generate multiple files

One template can generate multiple files. In my TexTran example, I generate multiple Entities from a definition file.

Entities.tt:

... //imports 
<#
//Get current directory
var directory = Path.GetDirectoryName(this.Host.TemplateFile);
var entitiesPath = @"\ProjectName\FolderName";
var regExFilter = "(?<=src).+$";
var entitiesFolder = Regex.Replace(directory, regExFilter, entitiesPath);

// GenerateEntities method is in TransforManagers.ttinclude
List<EntityDefinition> entities = GenerateEntities(entityLines); 

if(entities != null)
{
	foreach (var entity in entities)
	{
#>
// This file is auto generated. Changes to this file will be lost!
using System;
using System.Collections.Generic;
using TextTran.Transformations.Enums;
	
namespace TexTran.Data.Abstractions.Entities
{
<#
	if (!string.IsNullOrEmpty(entity.Summary))
	{
#>
	/// <summary>
	/// <#= Regex.Replace(entity.Summary, "// ", "") #>
	/// </summary>
<#
	}
#>
	public class <#= entity.Name.Trim() #> : BaseEntity
	{
	<#
	foreach (var property in entity.Properties)
	{
		if (property.Nullable)
		{
#>		
		public <#= property.Type #>? <#= property.Name #> { get; set; }
<#
	}
		else
		{
#>		
		public <#= property.Type #> <#= property.Name #> { get; set; }
<#
		}
	}
#>
	}
}
<#
	SaveFile(entitiesFolder, entity.Name, this.GenerationEnvironment.ToString());
	this.GenerationEnvironment.Remove(0, this.GenerationEnvironment.Length);
	}
}
#>

The template above will write a file for each Entity iterated, using the entity.Name property for the filename. Call the SaveFile method at the end of the foreach loop. I added some lines on top that show how I create the path where I want to write to. In my TexTran project, this is located in the TransformSetup.ttinclude file.

Appendix

Just a small note. What would happen if one if your files name changes? It would keep the old file, and write a new one next to it. Add following method to your Class Feature Block, it removes all content from the destination folder:

public void RemoveFilesFromFolder(string path)
{
	Array.ForEach(Directory.GetFiles(path), File.Delete);
}

Simply call this method first in your template, so that the folder is cleared everytime you start re-generating your files.

I hope this tutorial helps! Please leave any feedback or questions in the comments below.

All templates used referenced in this article can be found here.

T4Editor VS2019 Extension: Syntax highlighting for T4 Templates

Several developers complain about the lack of a proper T4 editor experience, as everything is just shown as plain text. See my other T4 tutorials to see the regular T4 text files in the header image.

I created a Visual Studio extension called T4Editor that adds colors to different blocks in you code. You can easily spot the difference between output code, control blocks and class feature blocks. This increases coding productivity and readability of your T4 templates.

In the future, C# code inside these blocks will also be highlighted with the propper C# syntax colors, I’m still working on that.

Update: Now you can set custom colors! Navigate to Extensions – T4Editor – Adjust Colors and pick whatever colors you want.

Update 2: There is now code completion/minor custom intellisense for codeblocks!

Update 4: I have implemented brace matching and improved RegEx. Typing ‘var string = “#>” for example, will not act as a closing tag.

Click here to see the code. Feel free to give feedback or even contribute!

T4Executer VS2019 Extension

Visual Studio 2019 released earlier this week. When I build my experimental TexTran project, I noticed none of the T4 Templates were executed. I used AutoT4 in the past (a VS2012 extension) but this package is not available in the latest 2019 version.

I added an extension to the Visual Studio Marketplace called TT4Executer. When installed, in executes all T4 Templates every time you build or rebuild your solution. This is enabled by default, and an be toggled in the Extensions > T4Executer > Enable/Disable menu.

It’s a pretty simple package, that adds an event to the OnBuildBegin event that fetches all .tt files in all active projects and executes them in before building your projects.

This is my first VSIX project, and it’s open for contributions and improvements, the source code can be found here.

Update: I added an options dialog where you can select which templates to ignore. This is found in the Extensions > T4Executer > Ignore Template menu.

Update 2: I a new option where you can specifiy to execute templates before or after build or ignore them. This can be configured via Extensions > T4Executer > Configure.

T4 Tutorial: Execute templates at build time

This is not so hard to do actually. It ensures your transformations are executed when you build your solution, when in some cases you did not click ‘Build > Transform all T4 Templates’ when changes are made to your .tt files.

If one of your projects uses T4 to generate code, and you want it to execute at build time, consider the next steps:

Unload your project

Righ-click on the project in your solution explorer and click ‘unload’.

Open the .csproj of your project

Right-click again and select ‘edit projectname.csproj’

Edit the .csproj file

Add following PropertyGroup at the beginning of your csproj file (for VS2017):

<PropertyGroup>
    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
    <TransformOnBuild>true</TransformOnBuild>
   <OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles>
    <TransformOutOfDateOnly>false</TransformOutOfDateOnly>
</PropertyGroup>

The TransformOnBuild property set tot true ensures the templates to be transformed when you build your project (false by default).

OverwriteReadOnlyOutputFiles forces overwriting of readonly output files.

Set TransformOutOfDateOnly to false to transform files even if the output is up to date.

At the end of yoru csproj file, add following import:

<Import Project="$(VSToolsPath)\TextTemplating\Microsoft.TextTemplating.targets" />

Now you can reload your project and you are done. All T4 Templates within this project will be executed on each build.