Showing posts with label .NET Framework. Show all posts
Showing posts with label .NET Framework. Show all posts

Monday, January 30, 2023

C# 11.0: Newlines in String Interpolation Expressions

In this post let's have a look at another feature that is available with C# 11.0. And that is support for Newlines in String Interpolation Expressions.

Consider the following example code written in C# 10.0.
int age = 60;
string ageCategory = age switch
{
< 1 => "Infant",
< 12 => "Child",
< 17 => "Adolescent",
< 65 => "Adult",
_ => "Older adult"
};
string message = $"Based on the Age of {age}, you are a(n) {ageCategory}.";
Here it would have been nice if I can include the logic to determine the age category within the interpolated string. But Newlines inside a non-verbatim interpolated string are not supported in C# 10.0.

With C# 11.0, we now have the support for Newlines in String Interpolation Expressions. So I can simplify the code as follows.
int age = 60;
string message = $"Based on the Age of {age}, you are a(n) {age switch
{
< 1 => "Infant",
< 12 => "Child",
< 17 => "Adolescent",
< 65 => "Adult",
_ => "Older adult"
}}.";
Isn't it nice? There is no pleasure like having readable simplified code.

Read more of C# features here.
   What's new in C# 11

Happy Coding.

Regards,
Jaliya

Tuesday, January 10, 2023

Azure Durable Functions in Azure Functions .NET Isolated Worker Process

Azure Durable Functions now supports .NET 7.0 running in the Isolated worker process. It's still in it's preview stage, but it's super exciting.

In this post, let's see how we can get ourselves started in Azure Durable Functions in Azure Functions .NET Isolated Worker Process.

First, after creating a Function App that targets .NET 7 Isolated functions worker, we need to install the NuGet package: Microsoft.Azure.Functions.Worker.Extensions.DurableTask. Make sure you have checked Included prerelease checkbox if you are trying to install the package through Visual Studio NuGet Package Manager.

Microsoft.Azure.Functions.Worker.Extensions.DurableTask
After installing the package, I have updated Function1.cs as follows adding a simple Durable Function.
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.DurableTask;
using Microsoft.Extensions.Logging;
 
namespace FunctionApp1;
 
public class Function1
{
    [Function(nameof(TriggerHello))]
    public async Task<HttpResponseData> TriggerHello([HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequestData req,
        [DurableClient] DurableClientContext durableContext,
        FunctionContext executionContext)
    {
        ILogger logger = executionContext.GetLogger(nameof(Function1));
 
        string instanceId = await durableContext.Client.ScheduleNewOrchestrationInstanceAsync(nameof(HelloPersonOrchestrator));
        logger.LogInformation("Created new orchestration with instance ID = {instanceId}", instanceId);
 
        return durableContext.CreateCheckStatusResponse(req, instanceId);
    }
 
    [Function(nameof(HelloPersonOrchestrator))]
    public static async Task<stringHelloPersonOrchestrator([OrchestrationTrigger] TaskOrchestrationContext context,
        FunctionContext executionContext)
    {
        ILogger logger = executionContext.GetLogger(nameof(HelloPersonOrchestrator));
 
        string result = await context.CallActivityAsync<string>(nameof(SayHello), "John Doe") + " ";
        result += await context.CallActivityAsync<string>(nameof(SayHello), "Jane Doe") + " ";
        result += await context.CallActivityAsync<string>(nameof(SayHello), "Joe Bloggs") + " ";
        result += await context.CallActivityAsync<string>(nameof(SayHello), "Fred Bloggs");
 
        logger.LogInformation("HelloPersonOrchestrator says: {output}", result);
 
        return result;
    }
 
    [Function(nameof(SayHello))]
    public static string SayHello([ActivityTrigger] string name, FunctionContext executionContext)
    {
        ILogger logger = executionContext.GetLogger(nameof(SayHello));
        logger.LogInformation("Saying hello to {name}", name);
        return $"Hello {name}!";
    }
}
And then we can call the HTTP  trigger to start the orchestration. And it works like a charm.
Output
Can't wait for this to go under GA.

Do try this out and if you see any issues, please do not hesitate to log an issue here: microsoft/durabletask-dotnet

Hope this helps.

Happy Coding.

Regards,
Jaliya

Monday, July 1, 2019

Task.Wait() Vs Task.GetAwaiter().GetResult()

In this post, let's go through one of the best practices when using async/await.

In some cases, we might want to run an async method synchronously and wait for the execution to be completed. Let's consider the below code.
static void Main(string[] args)
{
    // 1
    RunSomeTask().Wait();
 
    // 2
    //RunSomeTask().GetAwaiter().GetResult();
}
 
private static async Task RunSomeTask()
{
    // some long running work
}
 So our options to call RunSomeTask synchronously would be something like below,
  1. Task.Wait() (or Task.Result to get the return value if it returns something)
  2. Task.GetAwaiter().GetResult()
What would you prefer?

So the best practice is, we should be using Task.GetAwaiter().GetResult() instead of Task.Wait()/Task.Result(). Let's see why.

For the purpose of this post, I am modifying the RunSomeTask() method to throw an exception.
private static async Task RunSomeTask()
{
    await Task.Delay(200);
 
    throw new Exception("Failed because of some reason");
}

Now let's have a look at 2 different outputs.

When used Task.Wait():
Task.Wait()
When used Task.GetAwaiter().GetResult():
Task.GetAwaiter().GetResult()
As you can see when used Task.Wait(), if the task threw an exception, it will be wrapped inside an AggregateException. But when we are using Task.GetAwaiter().GetResult(), it will throw the exception directly which will make things like debugging/logging easy.

That's a very simple tip, but can be really useful.

On a final note, we should avoid calling tasks synchronously as much as possible.

Hope this helps.

Happy Coding.

Regards,
Jaliya

Wednesday, May 8, 2019

Introducing .NET 5

At this year's Microsoft Build conference, one of the biggest announcements for developers is what's going to be the future for .NET. In this post, let's see what that is.

Before talking about what's next for .NET, let's have a quick recap of the history of .NET. The first version of .NET is .NET Framework 1.0 which was released back in February 2002. It has come a long way since then, almost for more than 17 years and today, the latest version is .NET Framework 4.8. .NET Framework is only available in Windows machines.

In November 2014, .NET Core 1.0 was released. The main idea of introducing .NET Core is going cross-platform. .NET Core has evolved rapidly since then, as of today the latest version of .NET Core is .NET Core 2.2. .NET Core 3.0 is scheduled to be released on  September 2019, while the preview release, .NET Core 3.0 Preview 5 is already out there. For all this time, Microsoft was porting features of .NET Framework to .NET Core. .NET Core 3.0 embraces the desktop by adding WinForms, WPF and Entity Framework 6 making it possible to port desktop applications to .NET Core.

On the other hand, there is Mono framework, which is an open source implementation of Microsoft’s .NET Framework that originally targetted Linux. It is based on the open standards which has its own C# compiler and a Common Language Runtime. Mono 1.0 was released in June 2004 and as of today, the latest Mono release is 5.20.

So there are basically 3 implementations of .NET, .NET Framework, .NET Core and Mono. All these frameworks has its own Base Class Libraries. So what's next.

6th of May, 2019, Microsoft has announced .NET 5, which is the next release after .NET Core 3.0. Microsoft is skipping .NET Core 4.0 version name because it can cause confusion with .NET Framework 4.x versions which have been there for a long time. And with .NET 5, there will be just one .NET going forward, so there is no need for a special term "Core". But of course, you can use the “.NET Core” name if you like it.

.NET Core 3.0 will be the core for .NET 5 and great features from Mono will be moved in (currently Mono runs on more platforms than .NET Core like Android, iOS, PlayStation etc).

.NET Framework 4.8 will be the last major version of .NET Framework. But that doesn't mean it's completely abandoned, it will get service updates and support for years to come. But after .NET Core 3.0, Microsoft will not be porting any more features from .NET Framework.

So here is what's going to be new with .NET 5.
  • You will have two choices on runtime experiences.
    • CoreCLR or Mono
      • These two runtimes has it's own unique capabilities and you will be able to decide which runtime to use using very simple configuration
  • Java interoperability will be available on all platforms.
  • Objective-C and Swift interoperability will be supported on multiple operating systems.
  • CoreFX will be extended to support static compilation of .NET (AOT), smaller footprints and support for more operating systems.

Release Schedule


.NET 5 is scheduled to be released on November 2020, with the first preview available in the first half of 2020.
.NET Schedule
After November 2020, Microsoft will be shipping a major version of .NET once a year, every November and every even-numbered release will have LTS (Long Term Support).

Developers Take

  • New applications should be built on .NET Core.
  • If you are a Web Forms developer and want to build a new application on .NET Core, Microsoft recommends Blazor
  • If you are remoting or WCF Server developer and want to build a new application on .NET Core, Microsoft recommends using either ASP.NET Core Web APIs, gRPC (provides cross-platform and cross programming language contract based RPCs) or Core WCF.
  • If you are a Windows Workflow developer, Microsoft recommends Core WF

Exciting times ahead!
Happy Coding.

Regards,
Jaliya

Friday, April 27, 2018

Running Service Bus 1.1 and Service Fabric Side by Side?

I had a requirement where I wanted to have both Service Bus 1.1 and Service Fabric Side by Side on my laptop. One of the applications that I am working on required Service Bus 1.1 and another required Service Fabric. Apparently, it turned out only one can be running at a time, not the both.

Service Bus 1.1 was released back in late 2013 and uses Windows Fabric. So if you have Service Bus 1.1 installed before it installs a service named Windows Fabric Host Service.  But if you install Service Fabric Runtime and SDK on top of that, Windows Fabric Host Service which is required for Service Bus 1.1 is no longer there under Services, it’s getting removed by Service Fabric. So currently there is no way to have both the Service Bus 1.1 and Service Fabric running Side by Side in one single machine.

But there are two options. Unfortunately, both the options require moving Service Bus 1.1 away from your computer. If you are a developer, you need to have Service Fabric Runtime and SDK installed on your machine for sure.

1. Have the Service Bus Farm in a VM.

This option is kind of hard. You need to set up the Service Bus Farm on a different machine and configure the certificates required for hosts to connect to the server. Then you need to grab those and install on your local machine. That is a pain.

2. Use Azure Service Bus

This is pretty much straightforward, you just need to create a Service Bus application in Azure. You just need to change the connection string and most of the time it should work without any code changes.

Hope this helps someone who is having the same requirement and save him/her some time.

Happy Coding.

Regards,
Jaliya

Tuesday, January 16, 2018

Passing Nullable Value for a DbCommand Parameter

I had this requirement where I wanted to pass a nullable property for a Parameter in DbCommand.
public void Execute(int? someId)
{
    using (DbCommand dbCommand = _context.Database.GetDbConnection().CreateCommand())
    {
        dbCommand.CommandType = CommandType.StoredProcedure;
        dbCommand.CommandText = "sp_SomeStoredProcedure";
        dbCommand.Parameters.Add(new SqlParameter("ParameterId", someId);

        // some code
    }

    // some code
}
I was expecting when someId is null, ADO.NET will consider passing null for the parameter. But apparently, that doesn't seem to be the case. Got required parameter is not supplied error. I even tried below which I felt would work,
dbCommand.Parameters.Add(new SqlParameter("ParameterId", someId.HasValue ? someId.Value : null));
But kept getting the error. Finally, Null coalescing operator with DBNull was there to my rescue.
dbCommand.Parameters.Add(new SqlParameter("ParameterId", someId ?? (object)DBNull.Value));
Happy Coding.

Regards,
Jaliya

Tuesday, January 9, 2018

C# 7.2 : in Parameters

With C# 7.2, a nice set of syntax improvements that enable working with value types were introduced. My favorite among these is in Parameters.

In this post let’s see what in parameters really is.

As you have already know C# had ref and out for a quite a while now. If we recall what ref and out does (within parameter modifier context), it’s basically as follows.
static void Main(string[] args)
{
    Method1(out int i);
    Console.WriteLine(i); // 10
 
    Method2(ref i);
    Console.WriteLine(i); // 20
}
 
static void Method1(out int i)
{
    // Variable i needs to be assigned a value before leaving the method
    i = 10;
}
 
static void Method2(ref int i)
{
    // Variable i might/might not be assigned a value before leaving the method
    i = 20;
}
Both were used to pass the parameter by reference. The difference is when using out parameter, variable needs to be assigned a value before returning from the method. In ref parameter, there is no such requirement, within the method being called you can or not assign a value to ref parameter. But since there is a possibility of a value not being set there, before passing the ref parameter, it should have a value assigned.

But here from the caller, there is no option to say, within the method being called, the parameter should stay as readonly (if we make the parameter readonly, it’s affecting for outside use of the variable as well).

For this comes the in parameters.
static void Main(string[] args)
{
    Method1(out int i);
    Console.WriteLine(i); // 10
 
    Method2(ref i);
    Console.WriteLine(i); // 20
 
    Method3(i);
    Console.WriteLine(i); // 20
}
 
static void Method1(out int i)
{
    // Variable i needs to be assigned a value before leaving the method
    i = 10;
}
 
static void Method2(ref int i)
{
    // Variable i might/might not be assigned a value before leaving the method
    i = 20;
}
 
static void Method3(in int i)
{
    // Variable i is 20 and cannot assign a value
}
You can see, we have Method3 which accepts int i with in modifier. Unlike out and ref, when we are calling the methods which has in parameters, we don’t have to call like Method(in i). We can omit the in modifier, because the variable is going to be readonly within the method being called. Trying to set a value for in parameters from the method being called is illegal.

Isn’t it nice!

Happy Coding.

Regards,
Jaliya

Friday, January 5, 2018

C# 7 Point Releases

C# 7.0 was publicly released on March, 2017 with the release of Visual Studio 2017. Prior to C# 7, there was less to no discussion about point releases to C# language version (citation needed). But with C# 7, the story is not the same. As of today, we already have C# 7.1 and 7.2.

C# 7.1 was released on August, 2017 with Visual Studio 15.3 while 7.2 was released on December, 2017 with Visual Studio 15.5.

Here is a list of features which got available with point releases.

C# 7.1
  • async Main method
  • default literal expressions
  • Inferred tuple element names
C# 7.2
  • Reference semantics with value types
  • Non-trailing named arguments
  • Leading underscores in numeric literals
  • private protected access modifier
Visual Studio 2017 lets you select the language version for your project. Go to Project Properties -> Build -> Advanced. You can decide whether you are going to live by the edge or not.
image
Project Language Version
Happy Coding.

Regards,
Jaliya

Tuesday, December 5, 2017

AutoMapper : Handling Profile Dependencies using Custom Value Resolvers

If you are using or if you have used (I am sure you have) AutoMapper, Profiles lets you organize your mapping configurations in an easy manner.

In this post, let’s see how we can handle AutoMapper Profile dependencies using Custom Value Resolvers. As it’s always good to go with an example, let’s go with an example ASP.NET Core Web Application.

For an ASP.NET Core Web Application, AutoMapper can be configured with following easy steps.
You can add a class deriving from Profile class, and in the constructor you can setup your mapping configurations.
public class SomeProfile : Profile
{
    public SomeProfile()
    {
        CreateMap<MyClass, MyClassDTO>();
        // likewise
    }
}
Next in the Startup.ConfigureServices method, you just need to add the following line. (note: you will need to installer required AutoMapper nuget package)
public void ConfigureServices(IServiceCollection services)
{
    // some code
    services.AddAutoMapper();
}
Now you can use IMapper in your required classes as follows.
public class MyController : Controller
{
    private IMapper _mapper;
 
    public MyController(IMapper mapper)
    {
        _mapper = mapper;
    }
}
Now consider we have following two classes.
public class MyClass
{
    public int Id { get; set; }
}
 
public class MyClassDTO
{
    public int Id { get; set; }

    public string SomeProperty { get; set; }
}
And here on MyClassDTO, SomeProperty can't be mapped directly, we will need to get the value by calling ISomeService.GetSomeProperty(int id). Imagine ISomeService is registered for dependency injection.
public interface ISomeService
{
    string GetSomeProperty(int id);
}
So what we would expect is we can get the MyProperty value as follows.
public class SomeProfile : Profile
{
    private ISomeService _someService;
 
    public SomeProfile(ISomeService someService)
    {
        _someService = someService;
 
        CreateMap<MyClass, MyClassDTO>()
            .ForMember(obj => obj.SomeProperty,
                exp => exp.MapFrom(prop => _someService.GetSomeProperty(prop.Id)));
    }
}
But unfortunately, if we run this, we will get an error “No parameterless constructor defined for this object” on services.AddAutoMapper().

In these kinds of scenarios, we can use AutoMapper Custom Value Resolvers. Here I can do the DI without any issues.
public class MyPropertyResolver : IValueResolver<MyClass, MyClassDTO, string>
{
    private ISomeService _someService;
 
    public MyPropertyResolver(ISomeService someService)
    {
        _someService = someService;
    }
 
    public string Resolve(MyClass source, MyClassDTO destination, string destMember, ResolutionContext context)
    {
        return _someService.GetSomeProperty(source.Id);
    }
}
And the usage would be as follows.
public class SomeProfile : Profile
{
    public SomeProfile()
    {
        CreateMap<MyClass, MyClassDTO>()
            .ForMember(obj => obj.SomeProperty,
                exp => exp.ResolveUsing<MyPropertyResolver>());
    }
}
Now SomeProperty value should get resolved without any errors.

Hope this helps.

Happy Coding.

Regards,
Jaliya

Wednesday, July 5, 2017

Session : ASP.NET MVC 5 and SignalR 2

Delivered a 2-hour long session today about ASP.NET MVC 5 and SignalR 2 for a Microsoft partnered company in Sri Lanka.

There I went through the following set of topics in the context of ASP.NET MVC 5 and SignalR 2.
  • What is SignalR
  • Transports
  • Supported Platforms
  • Connections and Hubs
  • Hubs API Guide
    • Server (C#)
    • Client (JavaScript)
  • Understanding Lifetime Events
  • Security
  • Performance
  • What’s Next: ASP.NET Core SignalR


And a demo application was created during the session from scratch for a better understanding of the concepts.
Happy Coding.

Regards,
Jaliya

Thursday, April 20, 2017

Proper Use of ArgumentException and ArgumentNullException

I am sure most of you are already aware of this, but thought of clarifying the things, if someone is looking for it.

Basically ArgumentException is deriving from SystemException where as ArgumentNullException is deriving from ArgumentException. So while ArgumentException is more generic, ArgumentNullException is more specific.

Let's consider the following scenario. I have an Employee class and it has three properties.
public class Employee
{
    public string FirstName { get; }
    public string LastName { get; }
    public string JobRole { getset; }
 
    public Employee(string firstName, string lastName)
    {
        FirstName = firstName ?? throw new ArgumentNullException(nameof(firstName));
        LastName = lastName ?? throw new ArgumentNullException(nameof(lastName));
    }
}
Employee should have FirstName, LastName, but JobRole is optional when you are creating an employee.

In here as you can see, in the constructor we must be using ArgumentNullException instead of ArgumentException, because we are checking argument itself is null.

Consider the below method. It’s accepting an employee and let’s say we are going to do something with the employee’s JobRole, and to do that employee's JobRole should have set first.
public void SomeMethod(Employee employee)
{
    if (employee == nullthrow new ArgumentNullException(nameof(employee));
    if (employee.JobRole == nullthrow new ArgumentException($"{nameof(employee)}.{nameof(JobRole)} cannot be null");
    // TODO: Implementation
}
So here, first  we have checked whether the employee is null, if yes, we are throwing an ArgumentNullException. Then we need to check whether the employee’s JobRole is set or not. So if the JobRole of employee is null, we are throwing ArgumentException, but not an ArgumentNullException. The reason is even though the property is null, it has made the employee argument itself to invalid.

Well, hope you understood the difference. Even though these are simple things, these things matters when it comes to writing quality code.

Happy Coding.

Regards,
Jaliya

Friday, December 2, 2016

Deconstructors in C# 7

With the release of Visual Studio 2017 RC, you can now explore the upcoming C# 7 features. In this post let’s see how you can use Deconstructors in C# 7.

Please do not get confused in Deconstructors with Destructors as Destructors has nothing to with Deconstructors.

Basically what a Constructor would do is, it will create a new object of given type with given parameters (here I am opting out the default constructor as it has no parameters). So what the Deconstructor would do is, it will deconstruct the object back into it’s original parts. To be specific, you have the control of specifying how would you like the object to be deconstructed.

Let’s go by an example. Here I have a class named Employee, and there is a Constructor which accepts two parameters, First Name & Last Name, and those are being set to Employee’s properties. (Please note that as of today no errors are thrown by the compiler if you have a return type other than void in Deconstructor methods. In other words current Deconstructors lets you return values even though it doesn't make any sense. Microsoft is aware of this and they will get it fixed in the coming releases.)
public class Employee
{
    public string FirstName { get; }
    public string LastName { get; }
 
    public Employee(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = LastName;
    }
}
Above code is pretty simple. And now let’s see how we can add a Deconstructor to Employee.
public class Employee
{
    public string FirstName { get; }
    public string LastName { get; }
 
    public Employee(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = LastName;
    }
 
    public void Deconstruct(out string firstName, out string lastName)
    {
        firstName = FirstName;
        lastName = LastName;
    }
}
It’s simple, isn’t it. The only thing we need to have is, we should have a public void method named Deconstruct and one or more properties that I want the object to be deconstructed into, should be specified as out parameters.

Now let’s see how we can call the Deconstructor.
Employee employee = new Employee("Jaliya", "Udagedara");
var (firstName, lastName) = employee;
 
Console.WriteLine(firstName);
Console.WriteLine(lastName);
Here something to note is Deconstructor is being invoked by C# 7 Tuple syntax. If you are not aware about the Tuples in C# 7, please read this previous post of mine.

And the following will be the output.
image
Output
And the nice thing is, you can have multiple Deconstructors with different parameters (that’s basically method overloading).
public void Deconstruct(out string firstName, out string lastName)
{
    firstName = FirstName;
    lastName = LastName;
}
 
public void Deconstruct(out string firstName)
{
    firstName = FirstName;
}
The respective Deconstructor invocations are as follows.
// first deconstructor invocation
var (firstName, lastName) = employee;
 
// second deconstructor invocation
var (firstName1) = employee; 
If you still couldn’t download Visual Studio 2017 RC, download and try out these new C# 7 features.

Happy Coding.

Regards,
Jaliya

Thursday, December 1, 2016

Response Compression Middleware in ASP.NET Core

Response Compression is if client browser supports response compression, the server sends the content compressed, so the response size is  reduced. There are couple of compression schemes, but almost all the browsers supports gzip and deflate. In this post let’s see how you can use Response Compression Middleware in an ASP.NET Core application to serve the content compressed using gzip.

I am going to use Visual Studio 2017 RC and let’s create an ASP.NET Core Web Application targeting .NET Core and I am selecting the template as Web API as I want to simulate large content being sent to the client.

Once the project is created and all the dependencies are restored, let’s update all our dependencies to ASP.NET Core 1.1. Please note that Visual Studio 2017 RC targets ASP.NET Core 1.0.1 for it’s default ASP.NET Core templates as it’s the LTS (Long Term Support) version as of now.
image
Update Nuget Packages
Once update is completed, let’s modify Get() action in default ValuesController to return some large set of data.
[HttpGet]
public IEnumerable<string> Get()
{
   List<string> someStrings = new List<string>();
   for (int i = 0; i < 100000; i++)
   {
       someStrings.Add($"Value{i}");
   }
 
   return someStrings;
}
Now let’s just run the application, trigget Get() action in ValuesController and explore the request and response information.

First if we examine the request headers, it looks likes follows.
image
Request and Reponse Headers
I am using Chrome and it seems that my current version of Chrome supports set of compression types. But in the response there was no indication the content which got received is compressed.
image
Response Size
Size of the content received is 1.2 MB.

Now let’s add some code to send the content compressed using gzip. Let’s add a new nuget package to the project and that is Microsoft.AspNetCore.ResponseCompression.
image
Install Microsoft.AspNetCore.ResponseCompression
Now let’s modify the ConfigureServices method and Configure method in startup.cs to add and use Response Compression Middleware.
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services.AddResponseCompression(options =>
       {
           options.Providers.Add<GzipCompressionProvider>();
       });
    services.AddMvc();
  
}
 
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();
 
    app.UseResponseCompression();
    app.UseMvc();
}
You will need to add the following using.
using Microsoft.AspNetCore.ResponseCompression;
Now let’s run the application again, trigget Get() action in ValuesController and examine the request and response.
image
Request and Reponse Headers
Request is obviously the same and now you can see that response is being compressed using gzip.
image
Response Size
And this time only 238 KB was transferred. 1.2 MB has been reduced to 238 KB.

Happy Coding.

Regards,
Jaliya