An Atypical ASP.NET Core 6 Design Patterns Guide - Carl-Hugo Marcotte - E-Book

An Atypical ASP.NET Core 6 Design Patterns Guide E-Book

Carl-Hugo Marcotte

0,0
34,79 €

-100%
Sammeln Sie Punkte in unserem Gutscheinprogramm und kaufen Sie E-Books und Hörbücher mit bis zu 100% Rabatt.
Mehr erfahren.
Beschreibung

An Atypical ASP.NET Core 6 Design Patterns Guide, Second Edition approaches programming like playing with LEGO®: snapping small pieces together to create something beautiful. Thoroughly updated for ASP.NET Core 6, with further coverage of microservices patterns, data contracts, and event-driven architecture, this book gives you the tools to build and glue reliable components together to improve your programmatic masterpieces.

The chapters are organized based on scale and topic, allowing you to start small and build on a strong base, the same way that you would develop a program. You will begin by exploring basic design patterns, SOLID architectural principles, dependency injection, and other ASP.NET Core 6 mechanisms. You will explore component-scale patterns, and then move to higher level application-scale patterns and techniques to better structure your applications. Finally, you'll advance to the client side to connect the dots with tools like Blazor and make ASP.NET Core a viable full-stack web development framework.

You will supplement your learning with practical use cases and best practices, exploring a range of significant Gang of Four (GoF) design patterns along the way. By the end of the book, you will be comfortable combining and implementing patterns in different ways, and crafting software solutions of any scale.

Das E-Book können Sie in Legimi-Apps oder einer beliebigen App lesen, die das folgende Format unterstützen:

EPUB
MOBI

Seitenzahl: 914

Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



An Atypical ASP.NET Core 6 Design Patterns Guide

Second Edition

A SOLID adventure into architectural principles and design patterns using .NET 6 and C# 10

Carl-Hugo Marcotte

BIRMINGHAM—MUMBAI

An Atypical ASP.NET Core 6 Design Patterns Guide

Second Edition

Copyright © 2022 Packt Publishing

All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews.

Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the author, nor Packt Publishing or its dealers and distributors, will be held liable for any damages caused or alleged to have been caused directly or indirectly by this book.

Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information.

Senior Publishing Product Manager: Suman Sen

Acquisition Editor – Peer Reviews: Gaurav Gavas

Project Editor: Amisha Vathare

Content Development Editor: Edward Doxey

Copy Editor: Safis Editing

Technical Editor: Tejas Mhasvekar

Proofreader: Safis Editing

Indexer: Hemangini Bari

Presentation Designer: Ganesh Bhadwalkar

First published: December 2020

Second edition: March 2022

Production reference: 2280622

Published by Packt Publishing Ltd.

Livery Place

35 Livery Street

Birmingham

B3 2PB, UK.

ISBN 978-1-80324-984-1

www.packt.com

Foreword

After 10 years of abiding partnership with Carl-Hugo, I still remember the production support project that ushered this partnership in—the application’s accuracy and reliability were the overriding factors, providing the final product’s competitive edge. This project brought to light Carl-Hugo’s talent and his solid grasp of the .NET programming platform, which spurred me on to entrust him with more sensitive and crucial projects, laying the groundwork for our extended partnership.

We started tackling projects that had been on hold for a long time, requiring deep analysis, and more importantly, acute imagination to put them across using .NET programs. Once again, Carl-Hugo’s knowledge and skills stood out, leading to robust and flexible .NET application designs.

Carl-Hugo has consolidated his expertise by spending several years teaching programming. His book An Atypical ASP.NET Core 6 Design Patterns Guide, Second Edition unites that experience with his long-term expertise in the field. I highly recommend reading the book and putting it into practice as I’ve already had the opportunity to attend some of his training sessions and bear witness to this manual’s consistency and practical features.

Abdelhamid Zebdi

IT Director at Nortek Air Solutions (2007-2017), IT OPS Management at House of Commons of Canada (2017-present).

Contributors

About the author

Carl-Hugo Marcotte has been developing, designing, and architecting web applications professionally since 2005, wrote his first line of code at about eight years old, and holds a bachelor’s degree in computer science.

After working at a firm for a few years, he became an independent consultant, and developed projects of different sizes for SMEs and educational institutions. He is now a Senior Solutions Architect at Export Development Canada and is passionate about software architecture, C#, ASP.NET Core, and the Cloud.

He loves to share his knowledge, which led him to teach programming, write a blog, and create, maintain, and contribute to multiple open-source projects.

I want to thank everyone who supported me during my journey into the world of authoring, especially my other half and partner in life, Cathie, who is always there no matter the idea I pursue.

About the reviewer

Damir Arh has many years of experience with software development and maintenance; from complex enterprise software projects to modern consumer-oriented mobile applications. Although he has worked with a wide spectrum of different languages, his favorite language remains C#. In his drive towards better development processes, he is a proponent of test-driven development, continuous integration, and continuous deployment. He shares his knowledge by speaking at local user groups and conferences, blogging, and writing articles. He has received the prestigious Microsoft MVP award for developer technologies 10 times in a row. In his spare time, he’s always on the move: hiking, geocaching, running, and rock climbing.

Join our book’s Discord space

Join the book’s Discord workspace for Ask me Anything session with the authors:

https://packt.link/ASPdotNET6DesignPatterns

Contents

Preface

Section 1: Principles and Methodologies

Introduction

What is a design pattern?

Anti-patterns and code smells

Anti-patterns

Anti-pattern – God Class

Code smells

Code smell – Control Freak

Code smell – Long Methods

Understanding the web – request/response

Getting started with .NET

.NET SDK versus runtime

.NET 5+ versus .NET Standard

Visual Studio Code versus Visual Studio versus the command-line interface

An overview of project templates

Running and building your program

Technical requirements

Summary

Questions

Further reading

Automated Testing

An overview of automated testing

Unit testing

Integration testing

End-to-end testing

Other types of tests

Picking the right test style

Testing approaches

Refactoring

Technical debt

Testing .NET applications

Creating an xUnit test project

Getting started with xUnit

Facts

Assertions

Theories

Closing words

Arrange, Act, Assert

Organizing your tests

Unit tests

Integration tests

ASP.NET Core integration testing

Classic web application

Minimal hosting

Important testing principles

Summary

Questions

Further reading

Architectural Principles

The SOLID principles

Single responsibility principle (SRP)

Project – BookStore

What is an interface?

Open/Closed principle (OCP)

Project – IAttacker

Liskov substitution principle (LSP)

Project – HallOfFame

Conclusion

Interface segregation principle (ISP)

Project – Ninja versus Pirate

Project – Bookstore update

Conclusion

Dependency inversion principle (DIP)

Direct dependency

Inverted dependency

Inverting subsystems using DIP

Project – Dependency inversion

Conclusion

What’s next?

Other important principles

Separation of concerns

Don’t repeat yourself (DRY)

Keep it simple, stupid (KISS)

Summary

Questions

Further reading

Section 2: Designing for ASP.NET Core

The MVC Pattern Using Razor

The Model View Controller design pattern

Goal

Design

Anatomy of ASP.NET Core MVC

Directory structure

Controller

Model

View

Default routing

Conclusion

The View Model design pattern

Goal

Design

Project – View models (a list of students)

Conclusion

Summary

Questions

Further reading

The MVC Pattern for Web APIs

An overview of REST

HTTP methods

Status code

HTTP headers

Versioning

Default versioning strategy

Versioning strategy

Wrapping up

The Model View Controller design pattern

Goal

Design

Anatomy of ASP.NET Core web APIs

The entry point

Directory structure

Controller

Returning values

Attribute routing

Conclusion

The Data Transfer Object design pattern

Goal

Design

Project – DTO

Conclusion

API contracts

Summary

Questions

Further reading

Understanding the Strategy, Abstract Factory, and Singleton Design Patterns

The Strategy design pattern

Goal

Design

Project – Strategy

Conclusion

The Abstract Factory design pattern

Goal

Design

Project – AbstractVehicleFactory

Conclusion

The Singleton design pattern

Goal

Design

An alternate (better) way

Code smell – Ambient Context

Conclusion

Summary

Questions

Deep Dive into Dependency Injection

What is dependency injection?

The composition root

Registering your features elegantly

Object lifetime

Code smell – Control Freak

Using external IoC containers

Revisiting the Strategy pattern

Constructor injection

Property injection

Method injection

Project – Strategy

Adding the View Model

Adding a guard clause

Conclusion

Revisiting the Singleton pattern

Project – Application state

Project – Wishlist

Conclusion

Understanding the Service Locator pattern

Project – ServiceLocator

Implementing method injection

Implementing constructor injection

Implementing a minimal API

Conclusion

Revisiting the Factory pattern

Project – Factory

Conclusion

Summary

Questions

Further reading

Options and Logging Patterns

The Options pattern

Getting started

IOptionsMonitor<TOptions>

IOptionsFactory<TOptions>

IOptionsSnapshot<TOptions>

IOptions<TOptions>

Project – CommonScenarios

Named options

Using settings

Project – OptionsConfiguration

Implementing a configurator object

Using multiple configurator objects

Exploring other configuration possibilities

Project – OptionsValidation

Eager validation

Data annotations

Validation types

Project – OptionsValidationFluentValidation

Workaround – Injecting options directly

Conclusion

Becoming familiar with .NET logging abstractions

About logging

Writing logs

Log levels

Logging providers

Configuring logging

Structured logging

Conclusion

Summary

Questions

Further reading

Section 3: Designing at Component Scale

Structural Patterns

Implementing the Decorator design pattern

Goal

Design

Project – Adding behaviors

DecoratorA

DecoratorB

Project – Decorator using Scrutor

Conclusion

Implementing the Composite design pattern

Goal

Design

Project – BookStore

Conclusion

Implementing the Adapter design pattern

Goal

Design

Project – Greeter

Conclusion

Implementing the Façade design pattern

Goal

Design

Project – The façades

Opaque façade

Transparent façade

The program

Flexibility in action

Alternative façade patterns

Conclusion

Summary

Questions

Further reading

Behavioral Patterns

Implementing the Template Method pattern

Goal

Design

Project – Building a search machine

Conclusion

Implementing the Chain of Responsibility pattern

Goal

Design

Project – Message interpreter

Project – Improved message interpreter

Project – A final, finer-grained design

Conclusion

Summary

Questions

Understanding the Operation Result Design Pattern

The Operation Result pattern

Goal

Design

Project – Implementing different Operation Result patterns

The consumer

The simplest form of the Operation Result pattern

A single error message

Adding a return value

Multiple error messages

Adding message severity

Sub-classes and factories

Advantages and disadvantages

Advantages

Disadvantages

Summary

Questions

Further reading

Section 4: Designing at Application Scale

Understanding Layering

Introducing layering

Classic layering model

Splitting the layers

Layers versus tiers versus assemblies

What is an assembly?

Responsibilities of the common layers

Presentation

Domain

Rich domain model

Anemic domain model

Service layer

Data

Repository pattern

Unit of Work pattern

Abstract layers

Sharing the model

Clean Architecture

Implementing layering in real life

To be or not to be a purist?

Building a façade over a database

Summary

Questions

Further reading

Getting Started with Object Mappers

Object mapper

Goal

Design

Project – Mapper

Code smell – Too many dependencies

Pattern – Aggregate Services

Pattern – Mapping Façade

Project – Mapping service

Project – AutoMapper

Conclusion

Summary

Questions

Further reading

Mediator and CQRS Design Patterns

A high-level overview of Vertical Slice Architecture

Implementing the Mediator pattern

Goal

Design

Project – Mediator (IMediator)

Project – Mediator (IChatRoom)

Conclusion

Implementing the CQRS pattern

Goal

Design

Project – CQRS

Code smell – Marker Interfaces

Metadata

Dependency identifier

Conclusion

Using MediatR as a mediator

Project – Clean Architecture with MediatR

Conclusion

Summary

Questions

Further reading

Getting Started with Vertical Slice Architecture

Vertical Slice Architecture

What are the advantages and disadvantages?

Anti-pattern – Big Ball of Mud

Project – Vertical Slice Architecture

Project organization

Exploring a feature

Request validation

Testing

Continuing your journey

Conclusion

Summary

Questions

Further reading

Introduction to Microservices Architecture

What are microservices?

Cohesive unit of business

Ownership of data

Microservice independence

An introduction to event-driven architecture

Domain events

Integration events

Application events

Enterprise events

Conclusion

Getting started with message queues

Conclusion

Implementing the Publish-Subscribe pattern

Message brokers

The event sourcing pattern

Example

Conclusion

Introducing Gateway patterns

Gateway Routing pattern

Gateway Aggregation pattern

Backends for Frontends pattern

Mixing and matching gateways

Conclusion

Revisiting the CQRS pattern

Conclusion

Exploring the Microservice Adapter pattern

Adapting an existing system to another

Decommissioning a legacy application

Adapting an event broker to another

Conclusion

Summary

Questions

Further reading

Section 5: Designing the Client Side

ASP.NET Core User Interfaces

Getting familiar with Razor Pages

Design

Routing

Conclusion

Organizing the user interface

Partial views

Project – Shared form

Conclusion

Tag Helpers

Built-in Tag Helpers

Creating a custom Tag Helper

Creating an RSS feed TagHelperComponent

Conclusion

View components

Project – Reusable employee count

Conclusion

Display and editor templates

Display templates

Editor templates

Project – Composite BookStore revisited

Conclusion

Summary

Questions

Further reading

A Brief Look into Blazor

Overview of Blazor Server

Overview of Blazor WebAssembly

Getting familiar with Razor components

Creating Razor components

C#-only components

Razor-only components

Razor and C# hybrid components

CSS isolation

Component life cycle

Event handling

The Model-View-Update pattern

Goal

Design

Project – Counter

Conclusion

A medley of Blazor features

Summary

Questions

Further reading

An end is simply a new beginning

Appendices

Appendix A

Older C# features

The null-coalescing operator (C# 2.0)

Expression-bodied member (C# 6-7)

Throw expressions (C# 7.0)

Tuples (C# 7.0+)

Default literal expressions (C# 7.1)

Switch expressions (C# 8)

Discards (C# 7)

Async main (C# 7.1)

User-defined conversion operators (C# 1)

Local functions (C# 7) and a static local function (C# 8)

What’s new in .NET 5 and C# 9?

Top-level statements

Target-typed new expressions

Init-only properties

Record classes

Simplifying the record creation

The with keyword

Deconstruction

Equality comparison

Conclusion

What’s new in .NET 6 and C# 10?

File-scoped namespaces

Global using directives

Implicit using directives

Constant interpolated strings

Record struct

Minimal hosting model

Minimal APIs

Nullable reference types

Appendix B

An overview of containers

Docker

Docker Compose

Orchestration

Project Tye

Kubernetes

Scaling

Summary

Further reading

Assessment Answers

Chapter 1

Chapter 2

Chapter 3

Chapter 4

Chapter 5

Chapter 6

Chapter 7

Chapter 8

Chapter 9

Chapter 10

Chapter 11

Chapter 12

Chapter 13

Chapter 14

Chapter 15

Chapter 16

Chapter 17

Chapter 18

Acronyms Lexicon

Other Books You May Enjoy

Index

Landmarks

Cover

Index

Share your thoughts

Once you’ve read An Atypical ASP.NET Core 6 Design Patterns Guide, Second Edition, we’d love to hear your thoughts! Please click here to go straight to the Amazon review page for this book and share your feedback.

Your review is important to us and the tech community and will help us make sure we’re delivering excellent quality content.

Section 1: Principles and Methodologies

This section focuses on architectural principles and development methodologies that we use throughout the book. These introductory chapters are essential for progressing toward making great architectural decisions.

We first look at how to approach the book itself, explore prerequisites, and see a few helpful topics. Then, we cover automated testing and xUnit, to finally jump into the architectural principles, where we begin our study of the fundamentals of modern software engineering.

This section comprises the following chapters:

Chapter 1, IntroductionChapter 2, Automated TestingChapter 3, Architectural Principles

1

Introduction

The goal of this book is not to create yet another design pattern book; instead, the chapters are organized according to scale and topic, allowing you to start small with a strong foundation and build slowly upon it, in just the same way that you would build a program.

Instead of this being a guide that covers a few ways of applying a design pattern, we will explore the thought processes behind the systems we are designing from a software engineer’s point of view.

This is not a magic recipe book, and from experience, there is no magic recipe when designing software; there is only your logic, knowledge, experience, and analytical skills. Let’s define “experience” as your past successes and failures. And don’t worry, you will fail during your career, but don’t get discouraged by it. The faster you fail, the faster you can recover and learn, leading to successful products. Many techniques covered in this book should help you achieve that goal. Everyone has failed and made mistakes; you aren’t the first, and you certainly won’t be the last. To paraphrase a well-known saying of Roosevelt’s, the people that never fail are the ones who never do anything.

The high-level plan looks like this:

We will explore basic patterns, unit testing, architectural principles, and some crucial ASP.NET Core mechanisms.Then, we will move up to the component scale, exploring patterns oriented toward small chunks of software and individual units.After that, we will move to application-scale patterns and techniques, where we explore higher-level patterns and how to structure an application as a whole.Afterward, we will tackle the client side to connect the dots and make ASP.NET a viable full stack alternative.

Many subjects covered throughout the book could have a book of their own. Once you are done with this book, you should have plenty of ideas about where to continue your journey into software architecture.

Here are a few pointers that I believe are worth stating:

The chapters are organized to start with small-scale patterns and then progress to higher-level ones, making the learning curve easier.Instead of giving you a recipe, the book focuses on the thinking behind things and shows the evolution of some techniques to help you understand why the evolution happened.Many use cases combine more than one design pattern to illustrate alternate usage, so that you can understand the patterns and use them efficiently. This also shows that design patterns are not beasts to tame but tools to use, manipulate, and bend to your will.As in real life, no textbook solution can solve all our problems, and real problems are always more complicated than what’s explained in textbooks. In this book, my goal is to show you how to mix and match patterns to think “architecture,” instead of giving you step-by-step instructions to reproduce.

This chapter introduces the different concepts that we will be exploring throughout the book, including refreshers on a few notions. We will also cover .NET and its tooling, as well as the technical requirements, such as where the source code is located.

The following topics will be covered in this chapter:

What is a design pattern?Anti-patterns and code smellsUnderstanding the web – request/responseGetting started with .NET

What is a design pattern?

Since you just purchased a book about design patterns, I guess that you have some idea of what design patterns are, but let’s just make sure that we are on the same page.

Abstract definition: A design pattern is a proven technique that can be used to solve a specific problem.

In this book, we will apply different patterns to solve different problems and leverage some open source tools to go further, faster! Abstract definitions make people sound intelligent and all, but there is no better way to learn than by experimenting with something, and design patterns are no different.

If that definition does not make sense to you yet, don’t worry. You should have enough information by the end of the book to correlate the multiple practical examples and explanations with that definition, making it clear enough.

I like to compare programming to playing with LEGO® because what you have to do is generally the same: put small pieces together to create something bigger. It could be a castle, a spaceship, or something else that you want to build. With that analogy in mind, a design pattern is a plan to assemble a solution that fits one or more scenarios; a tower or a reactor, for example. Therefore, if you lack imagination or skills in the case of LEGO®, possibly because you are too young, your castle might not look as good as someone else’s who has more experience. Design patterns give you the tools you need, helping you put beautiful and reliable pieces together to improve your masterpiece.

However, instead of snapping LEGO® blocks together, you nest code blocks and interweave objects in a virtual environment!

Before going into more detail, well-thought-out applications of design patterns should improve your application designs. That is true whether you are designing a small component or a whole system. However, be careful: throwing patterns into the mix just to use them can lead to the opposite result. Aim to write readable code that solves the issue at hand, not at over-engineering systems with as many patterns as you can.

As we have briefly mentioned, there are design patterns that are applicable to multiple software engineering levels, and in this book, we will start small and grow to cloud scale! We will follow a smooth learning curve, starting with simpler patterns and code samples that bend good practices to focus on the patterns, and finally ending with more advanced full stack topics, integrating multiple patterns and good practices.

Of course, some subjects are overviews more than deep dives, like automated testing, because no one can fit it all in a single book. Nonetheless, I’ve done my best to give you as much information about as many architecture-related subjects as I can, and I hope you’ll find this book a helpful and enjoyable read.

Let’s start with the opposite of design patterns because it is essential to identify wrong ways of doing things if you want to avoid making those mistakes or correct them when you see them. Knowing the right way to overcome specific problems using design patterns is crucial.

Anti-patterns and code smells

Anti-patterns and code smells are architectural bad practices or tips about possible bad design. Learning about best practices is as important as learning about bad ones, which is where we start. There are multiple anti-patterns and code smells throughout the book to help you get started.

Anti-patterns

An anti-pattern is the opposite of a design pattern: it is a proven flawed technique that will most likely cause you some trouble and cost you time and money (and probably give you a headache or two along the way).

An anti-pattern is a pattern that seems to be a good idea and that seems to be the solution you were looking for, but that in the end will most likely cause more harm than good. Some anti-patterns started as legitimate design patterns and were labeled anti-patterns later. Sometimes, it is a matter of opinion, and sometimes the classification can be influenced by the programming language.

Let’s look at an example next. We will explore some other anti-patterns throughout the book.

Anti-pattern – God Class

A God class is a class that handles way too many things. It is usually a central class that many other classes inherit from or use; it is the class that knows and manages everything in the system; it isthe class. On the other hand, it is also the class that nobody wants to update, and the class that breaks the application every time somebody touches it: it is an evil class!

The best way to fix this is to separate responsibilities and distribute them to multiple classes instead of only one. We will see how to split responsibilities throughout the book, which helps create more robust software.

If you have a personal project with a God class at its core, start by reading the book, and then try to apply the principles and patterns that you learn to divide that class into multiple smaller classes that interact together. Try to organize those new classes into cohesive units, modules, or assemblies.

We will be getting into architectural principles very soon, which will open the way to concepts such as responsibility segregation.

Code smells

A code smell is an indicator of a possible problem. It points to areas of your design that could benefit from a redesign. By “code smell,” we mean “code that stinks” or “code that does not smell right.”

It is important to note that a code smell only indicates the possibility of a problem; it does not mean that there is a problem. They usually are good indicators though, so it is worth taking the time to analyze a “smelly” part of your software.

An excellent example of this is when many comments are being used to explain the logic of a method. That often means that the code could be split into smaller methods with proper names, leading to more readable code and allowing you to get rid of those pesky comments.

Another note about comments is that they don’t evolve, so what often happens is that the code described by the comments changes but the comment remains the same. That leaves a false or obsolete description of a block of code that can lead a developer astray.

The same is also true with method names. Sometimes, the method’s name and its body tell a different story, leading to the same issues. Nevertheless, it is rare that this will happen since programmers tend to read and write code better than spoken language comments. Nonetheless, keep both in mind when reading, writing, or reviewing code.

Code smell – Control Freak

An excellent example of a code smell is when you use the new keyword. This is an indication of a hardcoded dependency where the creator controls the new object and its lifetime. This is also known as the Control Freak anti-pattern, but I prefer to box it as a code smell instead of an anti-pattern since the new keyword is not intrinsically wrong.

At this point, you may be wondering how it is possible not to use the new keyword in object-oriented programming, but rest assured, we will cover that and expand on the control freak code smell in Chapter 7, Deep Dive into Dependency Injection.

Code smell – Long Methods

The long methods code smell is when a method starts to extend to more than 10 to 15 lines of code. That is a good indicator that you should think about that method differently. Having comments that separate multiple code blocks is a good indicator of a method that may be too long.

Here are a few examples of what might be the case:

The method contains complex logic intertwined in multiple conditional statements.The method contains a big switch block.The method does too many things.The method contains duplications of code.

To fix this, you could do the following:

Extract one or more private methods.Extract some code to new classes.Reuse the code from external classes.If you have a lot of conditional statements or a huge switch block, you could leverage a design pattern such as the Chain of Responsibility, or CQRS, which you will learn about in Chapter 10, Behavioral Patterns, and Chapter 14, Mediator and CQRS Design Patterns.

Usually, each problem has one or more solutions; you need to spot the problem and then find, choose, and implement one of the solutions. Let’s be clear here: a method containing 16 lines does not necessarily need refactoring; it could be OK. Remember that a code smell indicates that there might be a problem, not that there necessarily is one—apply common sense.

Understanding the web – request/response

Before going any further, it is imperative to understand the basic concept of the web. The idea behind HTTP 1.X is that a client sends an HTTP request to a server, and then the server responds to that client. That can sound trivial if you have web development experience. However, it is one of the most important web programming concepts, irrespective of whether you are building web APIs, websites, or complex cloud applications.

Let’s reduce an HTTP request lifetime to the following:

The communication starts.The client sends a request to the server.The server receives the request.The server most likely does something (executes some code/logic).The server responds to the client.The communication ends.

After that cycle, the server is no longer aware of the client. Moreover, if the client sends another request, the server is unaware that it responded to a request earlier for that same client because HTTP is stateless.

There are mechanisms for creating a sense of persistence between requests for the server to be “aware” of its clients. The most well known of these is probably cookies.

If we dig a little deeper, an HTTP request is composed of a header and an optional body. The most commonly used HTTP methods are GET and POST. Made popular by web APIs, we could also add PUT, DELETE, and PATCH to that list.

Although not every HTTP method accepts a body, can respond with a body, or should be idempotent, here is a quick reference table:

Method

Request has body

Response has body

Idempotent

GET

No*

Yes

Yes

POST

Yes

Yes

No

PUT

Yes

No

Yes

PATCH

Yes

Yes

No

DELETE

May

May

Yes

* Sending a body with a GET request is not forbidden by the HTTP specifications, but the semantics of such a request is not defined either. It is best to avoid sending GET requests with a body.

An idempotent request is a request that always yields the same result, whether it is sent once or multiple times. For example, sending the same POST request multiple times should create multiple similar entities, while sending the same DELETE request multiple times should delete a single entity. The status code of an idempotent request may vary, but the server state should remain the same. We will explore some of those concepts in more depth in Chapter 5,The MVC Pattern for Web APIs.

Here is an example of a GET request (without a body since that’s not allowed for GET requests):

GET http: //www.forevolve.com/ HTTP/1.1 Host: www.forevolve.com Connection: keep-alive Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate Accept-Language: en-US,en;q=0.9,fr-CA;q=0.8,fr;q=0.7 Cookie: ...

The HTTP header is composed of a list of key/value pairs representing metadata that a client wants to send to the server. In this case, I queried my blog using the GET method and Google Chrome attached some additional information to the request. I replaced the Cookie header’s value with ... because cookies can be quite large and are irrelevant to this sample. Nonetheless, cookies are passed back and forth like any other HTTP header.

Important note about cookies

The client sends cookies, and the server returns them for every request-response cycle. This could kill your bandwidth or slow down your application if you pass too much information back and forth (cookies or otherwise). One good example would be a serialized identity cookie that is very large.

Another example, one that is unrelated to cookies but that created such a back and forth, was the good old Web Forms ViewState. This was a hidden field sent with every request. That field could become very large when left unchecked.

Nowadays, with high-speed internet, it is easy to forget about those issues, but they can significantly impact the user experience of a client using a slow network.

When the server decides to respond to the request, it returns a header and an optional body, following the same principles as the request. The first line indicates the status of the request: whether it was successful. In our case, the status code was 200, which indicates success. Each server can add more or less information to their response, as can you in your code.

Here is the response to the previous request:

HTTP/1.1 200 OK Server: GitHub.com Content-Type: text/html; charset=utf-8 Last-Modified: Wed, 03 Oct 2018 21:35:40 GMT ETag: W/"5bb5362c-f677" Access-Control-Allow-Origin: * Expires: Fri, 07 Dec 2018 02:11:07 GMT Cache-Control: max-age=600 Content-Encoding: gzip X-GitHub-Request-Id: 32CE:1953:F1022C:1350142:5C09D460 Content-Length: 10055 Accept-Ranges: bytes Date: Fri, 07 Dec 2018 02:42:05 GMT Via: 1.1 varnish Age: 35 Connection: keep-alive X-Served-By: cache-ord1737-ORD X-Cache: HIT X-Cache-Hits: 2 X-Timer: S1544150525.288285,VS0,VE0 Vary: Accept-Encoding X-Fastly-Request-ID: 98a36fb1b5642c8041b88ceace73f25caaf07746 <Response body truncated for brevity>

Now that the browser has received the server’s response, in the case of HTML pages, it starts rendering it. Then, for each resource, it sends another HTTP call to its URI and loads it. A resource is an external asset, such as an image, a JavaScript file, a CSS file, or a font.

After the response, the server is no longer aware of the client; the communication has ended. It is essential to understand that to create a pseudo-state between each request, we need to use an external mechanism. That mechanism could be the session-state leveraging cookies, simply using cookies, or some other ASP.NET Core mechanisms; or we could create a stateless application. I recommend going stateless whenever you can.

Note

If you are interested in learning more about session and state management, I left a link in the Further reading section at the end of the chapter.

As you can imagine, the backbone of the internet is its networking stack. The Hypertext Transfer Protocol (HTTP) is the highest layer of that stack (layer 7). HTTP is an application layer built on the Transmission Control Protocol (TCP). TCP (layer 4) is the transport layer, which defines how data is moved over the network (for instance, the transmission of data, the amount of transmitted data, and error checking). TCP uses the Internet Protocol (IP) layer to reach the computer it tries to talk to. IP (layer 3) represents the network layer, which handles packet IP addressing.

A packet is a chunk of data that is transmitted over the wire. We could send a large file directly from a source to a destination machine, but that is not practical, so the network stack breaks down large items into smaller packets. For example, the source machine breaks a file into multiple packets, sends them to the target machine, and then the target reassembles them back into the source file. This process allows numerous senders to use the same wire instead of waiting for the first transmission to be done. If a packet gets lost in transit, the source machine can also send only that packet back to the target machine.

Rest assured, you don’t need to understand every detail behind networking to program web applications, but it is always good to know that HTTP uses TCP/IP and chunks big payloads into smaller packets. Moreover, HTTP/1 allows a limited number of parallel requests that a browser can open. This knowledge can help you optimize your apps. For example, a high number of assets to load, their size, and the order in which they are sent to the browser can increase the page load time, the perceived page load time, or paint time.

To conclude this subject and not dig too deep into networking, HTTP/1 is older but foundational. HTTP/2 is more efficient and supports streaming multiple assets using the same TCP connection. It also allows the server to send assets to the client before it requests the resources, called a server push.

If you find HTTP interesting, HTTP/2 is an excellent place to start digging deeper, as well as the newest experimental HTTP/3 specifications.

Getting started with .NET

A bit of history: .NET Framework 1.0 was first released in 2002. .NET is a managed framework that compiles your code into an Intermediate Language (IL) named Microsoft Intermediate Language (MSIL). That IL code is then compiled into native code and executed by the Common Language Runtime (CLR). The CLR is now known simply as the .NET runtime. After releasing several versions of .NET Framework, Microsoft never delivered on the promise of an interoperable stack. Moreover, many flaws were built into the core of .NET Framework, tying it to Windows.

Mono, an open source project, was developed by the community to enable .NET code to run on non-Windows OSes. Mono was used and supported by Xamarin, acquired by Microsoft in 2016. Mono enabled .NET code to run on other OSes like Android and iOS. Later, Microsoft started to develop an official cross-platform .NET SDK and runtime they named .NET Core.

The .NET team did a magnificent job building ASP.NET Core from the ground up, cutting out compatibility with the older .NET Framework versions. That brought its share of problems at first, but .NET Standard alleviated the interoperability issues between the old .NET and the new .NET.

After years of improvements and two major versions in parallel (Core and Framework), Microsoft reunified most .NET technologies into .NET 5 (now .NET 6) and the promise of a shared Base Class Library (BCL). With .NET 5, .NET Core simply became .NET while ASP.NET Core remained ASP.NET Core. There is no .NET Core 4, to avoid any potential confusion with .NET Framework 4.X.

New major versions of .NET should release every year now. Even-number releases are Long-Term Support (LTS) releases with free support for 3 years, and odd-number releases (Current) have free support for only 18 months.

The good thing behind this book is that the architectural principles and design patterns covered should remain relevant in the future and are not tightly coupled with the versions of .NET you are using. Minor changes to the code samples should be enough to migrate your knowledge and code to new versions.

Now, let’s cover some key information about the .NET ecosystem.

.NET SDK versus runtime

You can install different binaries grouped under SDKs and runtimes. The SDK allows you to build and run .NET programs, while the runtime only allows you to run .NET programs.

As a developer, you want to install the SDK on your deployment environment. On the server, you want to install the runtime. The runtime is lighter, while the SDK contains more tools, including the runtime.

.NET 5+ versus .NET Standard

When building .NET projects, there are multiple types of projects, but basically, we can separate them into two categories:

ApplicationsLibraries

Applications target a version of .NET, such as net5.0 and net6.0. Examples of that would be an ASP.NET application or a console application.

Libraries are bundles of code compiled together, often distributed as a NuGet package. .NET Standard class library projects allow code to be shared between .NET Core, .NET 5+, and .NET Framework projects. .NET Standard came into play to bridge the compatibility gap between .NET Core and .NET Framework, which eased the transition. Things were not easy when .NET Core 1.0 first came out.

With .NET 5 unifying all the platforms and becoming the future of the unified .NET ecosystem, .NET Standard is no longer needed. Moreover, app and library authors should target the base Target Framework Moniker (TFM), as in net5.0 and net6.0. You should target netstandard2.0 or netstandard2.1 only when needed, for example, to share code with .NET Framework, and avoid targeting .NET Standard 1.X. Microsoft also introduced OS-specific TFMs with .NET 5 and 6 that allow code to use OS-specific APIs like net6.0-android and net6.0-tvos. You can also target multiple TFMs when needed.

Note

I’m sure that we are going to see .NET Standard libraries stick around for a while. All projects are not just going to migrate from .NET Framework to .NET 5 magically, and people are likely to want to continue sharing code between the two.

The next versions of .NET should be built over .NET (Core) 5, while .NET Framework 4.X is going to stay where it is today, receiving only security patches and minor updates. For example, .NET 6 is built over .NET 5.

Visual Studio Code versus Visual Studio versus the command-line interface

How can one of these projects be created? .NET Core comes with the dotnetcommand-line interface (CLI), which exposes multiple commands, including new. Running the dotnet new command in a terminal generates a new project.

To create an empty class library, we can run the following commands:

md MyProject cd MyProject dotnet new classlib

That would generate an empty class library in the newly created MyProject directory. The -h option can come in handy when discovering available commands and their options. You can use dotnet -h to find the available SDK commands, or dotnet new -h to find out about options and available templates.

The CLI enables us to automate our workflows in continuous integration (CI) pipelines, while developing locally, or through any other process.

The CLI also makes it easier to write documentation that anyone can follow; it is way easier and faster to write a few commands in a terminal than install programs like Visual Studio and emulators.

Visual Studio Code is my favorite text editor. I don’t use it much for .NET coding, but I still do to reorganize projects, when it’s CLI time, or for any other task that is easier to complete using a text editor, such as writing documentation using Markdown, writing JavaScript or TypeScript, or managing JSON, YAML, or XML files. To create a project or solution, or to add a NuGet package using Visual Studio Code, open a terminal and use the CLI.

As for Visual Studio, my favorite IDE, it uses the CLI under the hood to create the same projects, making it consistent between tools. Visual Studio adds a user interface over the CLI, which is a good example of leveraging it.

You can also create and install additional dotnetnew project templates in the CLI or even create global tools. Those topics are beyond the scope of this book.

An overview of project templates

Here is an example of the templates that are installed (dotnetnew --list):

Figure 1.1: Project templates

A study of all the templates is beyond the scope of this book, but I’d like to visit the few that are worth mentioning, some of which we will use later:

dotnet new console creates a console applicationdotnet new classlib creates a class librarydotnet new xunit creates an xUnit test projectdotnet new web creates an empty web projectdotnet new mvc scaffolds an MVC applicationdotnet new webapi scaffolds a web API application

Running and building your program

If you are using Visual Studio, you can always hit the play button, or F5, and run your app. If you are using the CLI, you can use one of the following commands (and more). Each of them also offers different options to control their behavior. Add the -h flag with any command to get help on that command, such as dotnet build -h:

Command

Description

dotnet restore

Restore the dependencies (a.k.a. NuGet packages) based on the .csproj or .sln file present in the current dictionary.

dotnet build

Build the application based on the .csproj or .sln file present in the current dictionary. It implicitly runs the restore command first.

dotnet run

Run the current application based on the .csproj file present in the current dictionary. It implicitly runs the build and restore commands first.

dotnet watch run

Watch for file changes. When a file has changed, the CLI updates the code from that file using the hot-reload feature. When that is impossible, it rebuilds the application and then reruns it (equivalent to executing the run command again). If it is a web application, the page should refresh automatically.

dotnet test

Run the tests based on the .csproj or .sln file present in the current directory. It implicitly runs the build and restore commands first. We cover testing in the next chapter.

dotnet watch test

Watch for file changes. When a file has changed, the CLI reruns the tests (equivalent to executing the test command again).

dotnet publish

Publish the current application, based on the .csproj or .sln file present in the current directory, to a directory or remote location, such as a hosting provider. It implicitly runs the build and restore commands first.

dotnet pack

Create a NuGet package based on the .csproj or .sln file present in the current directory. It implicitly runs the build and restore commands first. You don’t need a .nuspec file.

dotnet clean

Clean the build(s) output of a project or solution based on the .csproj or .sln file present in the current directory.

Technical requirements

Throughout the book, we will explore and write code. I recommend that you install Visual Studio, Visual Studio Code, or both to help with that. Other alternatives are Visual Studio for Mac, Riders, or any other text editor of your choice. I use Visual Studio and Visual Studio Code.

Unless you install Visual Studio, which comes with the .NET SDK, you may need to install it. The SDK comes with the CLI that we explored earlier, as well as the build tools for running and testing your programs. Have a look at the README.md file in the GitHub repository for more information and links to those resources.

The source code of all chapters is available for download on GitHub at the following address: https://adpg.link/net6.

Summary

In this chapter, we took a look at design patterns, anti-patterns, and code smells. We also explored a few of them. We then moved on to a recap of the request/response cycle of a typical web application.

We continued by exploring .NET essentials, such as SDK versus runtime and app targets versus .NET Standard. This has set us up to explore the different possibilities we have when building our .NET applications. We then dug a little more into the .NET CLI, where I laid down a list of essential commands, including dotnet build and dotnet watch run. We also covered how to create new projects.

In the next two chapters, we explore automated testing and architectural principles. These are foundational chapters for anyone wishing to build robust, flexible, and maintainable applications.

Questions

Let’s take a look at a few practice questions:

Can we add a body to a GET request?Why are long methods a code smell?Is it true that .NET Standard should be your default target when creating libraries?What is a code smell?

Further reading

Here are some links to consolidate what has been learned in the chapter:

Overview of how .NET is versioned: https://adpg.link/n52L.NET CLI overview: https://adpg.link/Lzx3Custom templates for dotnetnew: https://adpg.link/74i2Session and state management in ASP.NET Core: https://adpg.link/Xzgf

Join our book’s Discord space

Join the book’s Discord workspace for Ask me Anything session with the authors:

https://packt.link/ASPdotNET6DesignPatterns