ASP.NET Core Guide for ASP.NET Framework Developers

If you are a C# developer, chances are you have heard about this new .NET Core thing and the new version of the ASP.NET framework. You can continue to work with ASP.NET Web API or any other framework from the old ASP.NET you’ve known for years. But, ASP.NET Core is here to stay.

In case you missed it, “ASP.NET Core is a cross-platform, high-performance, open-source framework for building modern, cloud-based, Internet-connected applications”. “ASP.NET Core is a redesign of ASP.NET 4.x, with architectural changes that result in a leaner, more modular framework”.

ASP.NET Core has brought a lot of new features. For example, cross-platform development and deployment, built-in dependency injection, middlewares, health checks, out-of-the-box logging providers, hosted services, API versioning and much more.

Don’t worry if you haven’t started to worked with ASP.NET Core yet. This is a new framework with lots of new features, but it has brought many other features from the previous version. So, you will feel like home.

TL;DR

  1. You can create projects from the command line.
  2. NuGet packages are listed on the csproj files.
  3. csproj files don’t list .cs files anymore.
  4. There’s no Web.config, you have a json file instead.
  5. There’s no Global.asax, you have Startup.cs instead.
  6. You have a brand new dependency container.

Every journey begins with the first step

toddler's standing in front of beige concrete stair

If you are adventurous, download and install the ASP.NET Core developer (SDK) and create a new empty web project from Visual Studio. These are the files that you get from it.

|____appsettings.Development.json
|____appsettings.json
|____Program.cs
|____Properties
| |____launchSettings.json
|____<YourProjectName>.csproj
|____Startup.cs

ASP.NET Core has been created with other operating systems and IDEs in mind. Now, you can create a project, compile it, and run the tests from the command line. For example, to create a new empty Web project, you can use $ dotnet new web

Where is the packages.config file?

If you installed a NuGet package into your brand new ASP.NET Core project, one thing you could notice is the missing packages.config file. If you remember, it is an xml file that holds the packages installed. But, where in the world are those packages referenced now? In the csproj file of your project.

Now, a csproj file looks like this:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
  </ItemGroup>

</Project>

NuGet packages are referenced under ItemGroup in a PackageReference node. There you are Newtonsoft.Json! Goodbye, packages.config!

Wait! What happened to the csproj file?

Csproj files have been simplified. Before a csproj file listed every single file in the project. All your files with .cs extension were in it. Now, every .cs file within the folder structure of the project is part of it.

Before, things started to get complicated as time went by and the number of files increased. Sometimes, merge conflicts were a nightmare. There were files under version control not included in the csproj file. Were they meant to be excluded because they didn’t apply anymore? Or somebody tried to solve a merge conflict and forgot to include them? This problem is no more!

Where is the Web.config file?

Another missing file is the Web.config file. Instead you have a Json file: the appsettings.json file. You can use strings, integers and booleans in your config file. There is even support for sections and subsections. Before, if you wanted to achieve that, you had to come up with a naming convention for your keys. For example, prepending the section and subsection name in every key name.

Probably, you have used ConfigurationManager all over the place in your code to read configuration values. Now, you can have a class with properties mapped to a section or subsection of your config file. And you can inject it into your services.

// appsettings.json
{
    "MySettings": {
        "ASetting": "ASP.NET Core rocks",
        "AnotherSetting": true
    }
}
public class MySettings
{
    public string ASetting { get; set; }
    public bool AnotherSetting { get; set; }
}

public class YourService
{
    public YourService(IOptions<MySettings> settings)
    {
        // etc
    }
}

You still need to register that configuration into the dependency container! More on that later.

Additionally, you can override keys per environment. You can use the name of your environment in the file name. For example, appsettings.Development.json or appsettings.QA.json. You can specify the current environment with an environment variable or in the launchSettings.json file.

There’s even support for sensitive settings that you don’t want to version control: secrets.json file. You can manage this file from the command line too.

Where is the Global.asax?

Yet another missing file: Global.asax. You used it to perform actions on application or session events. For example, when application started or ended. It was the place to do one-time setups, register filters or define routes.

But now we use the Startup.cs file. It contains the initialization and all the settings needed to run the application. An Startup.cs file looks like this:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }
        
    public void ConfigureServices(IServiceCollection services)
    {
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
    }
}

It has two methods: ConfigureServices and Configure. The Configure method replaces the Global.asax file. It creates the app’s request processing pipeline. This is the place to register a filter or a default route for your controllers. And the ConfigureServices is to configure the services to be injected into the dependency container…Wait, what?

A brand new dependency container

Prior to ASP.NET Core, if you wanted to apply dependency injection, you had to bring a container and roll the discovery of services for your controllers. For example, you had an xml file to map your interfaces to your classes or did some assembly scanning to do it automatically.

Now, a brand new dependency container is included out-of-the-box. You can inject dependencies into your services, filters, middlewares and controllers. It lacks some of the features from your favorite dependency container, but it is meant to suit “90% of the scenarios”.

If you are familiar with the vocabulary from another containers, AddTransient, AddScoped and AddSingleton ring a bell. These are the lifetimes of the injected services, ranging from the shortest to the largest.

More specifically, a transient service is created every time a new instance is requested. An scoped service is created once per request. Plus, a singleton service is created only once per the application lifetime.

To register your services, you have to do it inside of the ConfigureServices method of the Startup class. Also, you bind your classes to a section or subsection of the config file here.

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IMyService, MyService>();
    
    var section = Configuration.GetSection("MySettings");
    services.Configure<MySettings>(section);
}

Conclusion

You have only scratched the surface of ASP.NET Core. You have learned about some of the changes ASP.NET Core has brought. But, if you haven’t started with ASP.NET Core, go and try it. You may be surprise by how things are done now.

This post was originally published on exceptionnotfound.net as part of Guest Writer Program. I’d like to thank Matthew for the editing of this post.

The Art of Unit Testing: Four Takeaways

This is THE book to learn how to write unit tests. It starts from the definition of an unit test to how to implement them at your organization. Although, it covers extensively the subject, it doesn’t advocate writing unit tests before or after the production code.

The main takeaway from this book is that you should treat your tests as production code. Sometimes, tests aren’t treated as code that needs to be taken care of. You should have test reviews, instead of only code reviews. Your tests are your safety net, so do not let them rot.

Naming convention

Use UnitOfWork_Scenario_ExpectedBehaviour for your test names. You can read it as follow: when calling UnitOfWork with Scenario, then ExpectedBehaviour. A Unit of Work is any logic exposed through public methods that returns value, changes the state of the system or makes an external invocation.

With this naming convention is clear the logic under test, the inputs and the expected result. You will end up with long test names, but it’s OK to have long test names for the sake of readability.

Builders vs SetUp

Use builders instead of SetUp methods. Tests should be isolated from other tests. Sometimes, SetUp methods create shared state among your tests. You will find tests that passes in isolation but don’t pass alongside other tests and tests that need to be run many times to pass.

Often SetUp methods end up with initialization for only some tests. Tests should create their own world. So, initialize what’s need inside every test using builders.

Safe green zone

Keep a set of always passing unit tests. You will need some configurations for your integration tests: a database, environment variables or some files in a folder. Integration tests will fail if those configurations aren’t in place. So, developers could ignore some failing tests, and real issues, because of those configurations.

Therefore, separate your unit tests from your integration tests. You will distinguish between a missing setup and an actual problem with your code. A failing test should mean a real problem, not a false positive.

Organization of your tests

Have a unit test project per project and a test class per class. You should easily find tests for your classes and methods. To achieve this:

  • Create separate projects for your unit and integration tests. Add the suffix UnitTests and IntegrationTests accordingly. For example, for a project Library, name your tests projects Library.UnitTests and Library.IntegrationTests.

  • Create tests inside a file with the same name as the tested code adding the suffix Tests. For MyClass, your tests should be inside MyClassTests. Also, you can group features in separate files adding the name of the feature as suffix. For example, MyClassTests.AnAwesomeFeature.

Conclusion

Unit testing is a broad subject. The Art of Unit Testing cover almost all you need to know about it. The main lesson is your tests should be readable, maintainable and trust-worthy. Remember the next person reading your tests will be you.

Pipeline pattern: Perform tasks with an assembly line of steps

TL;DR Pipeline pattern is like the enrich pattern with factories. Pipeline = Command + Factory + Enricher

Problem

You need to do a complex operation in your system. But, this complex operation consist of smaller tasks or steps. For example, make a reservation, generate an invoice or create an order. If a single task fails, you want to mark the whole operation as failed.

Also, this set of tasks can vary depending of certain conditions. So, your complex operation won’t have the same tasks every time. For example, it will vary per client, type of operation or any other parameter. How would you do it?

Solution

The pipeline pattern to the rescue! A pipeline is like an assembly line in a factory. Each workstation in an assembly adds a part until the product is assembled. For example, in a car assembly line, there are separate stations to put the doors, the engine and the wheels of a car.

You can create a set of reusable steps to perfom each action in your “assembly line”. So you can apply these steps one after the other in a pipeline. For example, to sell an item online, you need to update the stock, charge a credit card, send a delivery order and send an email to the client. To the code!

First, create a command/context class for the inputs of the pipeline

public class BuyItemCommand : ICommand
{
    // Item code, quantity, credit card information, etc
}

Then, create one class per each workstation of your assembly line. These are the steps. For example, UpdateStockStep, ChargeCreditCardStep, SendDeliveryOrderStep and NotifyClientStep.

public class UpdateStockStep : IStep<BuyItemCommand>
{
    public Task ExecuteAsync(BuyItemCommand command)
    {
        // Put your own logic here
        return Task.CompletedTask;
    }
}

Next, a builder for a pipeline with all its steps. Since the steps may vary depending on the type of operation, the client or any other condition, you can load your steps from a database or config files. For example, selling an eBook doesn’t need to create a delivery order.

public class BuyItemPipelineBuilder : IPipelineBuilder
{
    private readonly IStep<BuyItemCommand>[] Steps;

    public BuyItemPipelineBuilder(IStep<BuyItemCommand>[] steps)
    {
        Steps = steps;
    }

    public IPipeline CreatePipeline(BuyItemCommand command)
    {
      // Create your pipeline here...
      var updateStockStep = new UpdateStockStep();
      var chargeCreditCardStep = new ChargeCreditCard();
      var steps = new[] { updateStockStep, chargeCreditCardStep };
      return new BuyItemPipeline(command, steps);
    }
}

Now, create the pipeline to run all its steps. It’s a loop through its steps.

public class BuyItemPipeline : IPipeline
{
    private readonly BuyItemCommand Command;
    private readonly IStep<BuyItemCommand>[] Steps;

    public BuyItemPipeline(BuyItemCommand command, IStep<BuyItemCommand>[] steps)
    {
        Command = command;
        Steps = steps;
    }

    public async Task ExecuteAsync()
    {
        foreach (var step in Steps)
        {
            await step.ExecuteAsync(Command);
        }
    }
}

Also, you can use decorators to perform orthogonal actions on the execution of the pipeline or every step. For example, run the pipeline inside a transaction, log every step or measure the execution time of the pipeline.

Now everything is in place, so you can run your pipeline

var command = new BuyItemCommand();
var builder = new BuyItemPipelineBuilder(command);
var pipeline = builder.CreatePipeline();

await pipeline.ExecuteAsync();

But, some steps of the pipeline can be delayed for later processing. The user doesn’t have to wait for these steps to finish his interaction in the system. You can run them in background jobs or schedule its execution for later processing. For example, you can use Hangfire or roll your own queue mechanism (Kiukie…Ahem, ahem)

Conclusion

This is a pattern you may find out there or may need to write. It’s an assembly of steps to perform some actions based on a input object. You could extend this pattern to add custom action on the execution of the pipeline or each step. Also, depending on the expected load of your pipeline, you could use Azure Functions to run your steps.

I have used and implemented this pattern before. I used it in an invoicing platform to generate documents. Each document type had a different pipeline and factories could generate pipelines with different steps depending on the type of client and operation. Also, I have used this pattern in a reservation management system. In this case, I used separate pipelines to create, modify and cancel reservations.

PS: You can take a look at Pipelinie to see more examples. Pipelinie offers abstractions and default implementations to roll your own pipelines and builders. All ideas and contributions are more than welcome!

A Review of Two Clean Code Books

Clean Code

Clean Code will change the way you code. It doesn’t teach how to code in a particular language, but how to produce code easy to read, grasp and maintain. Although code samples are in Java, all concepts can be translated to other languages. This book is based on the premise that code should be optimized to be read.

The book starts defining what it’s Clean Code by collecting quotes from book authors and other well-known people in the field. It covers the subject of Clean Code from variables to architectural design.

Three chapters you may find very instructive are the ones about naming, comments and functions.

Naming. The first concept after the definition of Clean Code is naming thing. This chapter encourage names that reveal intent and are easy to pronounce. For example, int d; // elapsed time in days vs int elapsedTimeInDays, genymdhms vs generationTimestamp, HolyHandGrenade vs DeleteItems.

Comments. Clean Code is better than bad code with comments. Maybe you have heard about commenting your code as the right thing to do. But, this chapter shows what actually need comments. Have you seen this kind of comment before? i++; // Increment i Have you written them? “Don’t use a comment when a function or variable can be used”.

Functions. There is one entire chapter devoted to functions. It recommends short and concise functions. “Functions should do one thing. They should do it well”. For example, this chapter discourages functions with boolean paramaters. They will have to handle the true and false scenarios, then it won’t do only one thing.

Clean Code should be an obligatory reading for every single developer. Teachers should, at least, point students to this book. This book doesn’t deserve to be read, it deserves to be studied. So, grab a copy and study it.

The Art of Readable Code

The Art of Readable Code, the perfect companion for the Clean Code. It isn’t as dogmatic as Clean Code. It contains simple and practical tips to improve your code at the function level. Tips aren’t as strict as the Clean Code ones. But, it still deserves to be read. Code samples are in Javascript, Python, C and Java.

This book presents the concept that code should be written to minimize the time for someone else to understand the code. Here, understand means to solve errors, to spot bugs, to make changes. Among other tips in this book, you can find:

  • Attach extra information to your names. For example, encode units. delay vs delay_secs, size vs size_mb
  • Avoid misunderstading from your names. For example: results = Database.all_objects.filter("year <= 2011") Does filter pick out elements? Use select. Or does it get rid of? Use exclude.
  • Use min and max in your names for inclusive limits. For example, MAX_ITEMS_IN_CART
  • Use first and last for inclusive limits. For example, print integer_range(start=2, stop=4) What is the result? [2,3] or [2,3,4]. Instead, print integer_range(first=2, last=4)
  • Use begin and end for inclusive/exclusive ranges. For example: To find all events on a date PrintEventsInRange("OCT 16 12:00am", "OCT 17 12:00am") instead of PrintEventsInRange("OCT 16 12:00am", "OCT 16 11:59.999am")
  • Use named parameters or try to mimic them. For example: connect(/*timeout_ms=*/10, /*use_ssl=*/false)
  • Separate business logic from problem specific code. This encourages to keep code at a single level of abstraction and to write reusable methods.
  • The most readable code is not code at all. This book recommend reading documentation and method signatures from your libraries and tools. So you don’t roll out your own code.

The Art of Readable Code is a good starting point to introduce the concept of readability and clean code to your team. These tips and tricks are a good reference for code standards and reviews.

Tips and Tricks for Better Code Reviews

Code reviews are a great tool to indentify bugs before the code head to the QA team or the clients. Sometimes you need another pair of eyes to spot unnoticed things. Also, they are great to keep the code clean as the project moves forward, spread knowledge inside a team and mentor newcomers or juniors. But, it’s true that code reviews can be terse and frustating for the reviewer and the reviewee. No worries! Here, you have a collected list of tips and tricks for better code reviews.

TL;DR For the reviewer: be nice and remember you are reviewing the code, not the writer. For the reviewee: don’t take it personal, every code review is an opportunity to learn. For all the dev team: reviews take time too, add them to the estimates.

What to look for in a code review?

What should I look at? You’re new to code reviews and you don’t know what it’s going to be review in the code you wrote. Or you have been asked to review somebody else’s code, and you don’t know what to look for. You can take a look at this:

Does the code…

  • Compile in somebody else’s machine? If you have a Continuous Integration tool, you can spot if the code is compiling and all tests are passing.
  • Include unit or integration tests?
  • Introduce new bugs?
  • Follow current standards?
  • Reimplement things? Some logic is already implemented in the standard library or in a extension method?
  • Build things the hard way?
  • Kill performance?
  • Have duplication? Has code been copied and pasted?

For the reviewer

Before you start any review, make sure to understand the requirement and look at the tests, they should be like documentation. It’s a good idea to look at the diff twice: one for the general picture and another for the details.

  • Be humble: We all have something to learn
  • Take out the person when giving feedback. Remember you are reviewing the code, not the author.
  • Be clear: You may be reviewing code from juniors, mid-level or seniors. Even from non native speakers of your language. Everybody doesn’t have your same amount of experience. Obvious things for you aren’t obvious for somebody else.
  • Always give at least one positive remark. For example: It looks good to me (LGTM)
  • Use questions instead of commands or orders. For example, Could this be changed? vs Change it
  • Use “we” instead of “you”
  • Instead of showing an ugly code, teach. Link to resource to explain even more. For example: blog posts and StackOverflow questions.
  • Review only the code that has changed
  • Find bugs while reading the code, instead of style issues

For the reviewee

  • Don’t take it personal. It’s the code under review, not you
  • Find in every review an opportunity to learn
  • Make sure the reviewer have enough context to review. For example: write a description of your PR, what it does, what decision you made
  • Keep all the discussion online. If you contacted the reviewer by chat or email, bring some relevant comments online

For team management

  • Code reviews should be the highest priority
  • Code reviews are as important as writing code. They take time too. Make sure to add code review to your estimates
  • Make someone familiar with the code base review it
  • Have at least two reviewers. For example, pick one reviewer, then he will pick another one until the two of them agree

You may feel frustrated with code reviews, either reviewer or reviewee. Reviews could end up being a discussion about styling issues. But, be humble and nice. Every code review is a chance to learn something new.

Sources: