All About ASP.NET and ASP.NET Core 2 Hosting BLOG

Tutorial and Articles about ASP.NET and the latest ASP.NET Core

ASP.NET Core 3.0 Hosting :: An Early Look at gRPC and ASP.NET Core 3.0

clock March 22, 2019 09:36 by author Jervis

In this post, I want to introduce my very early experience (after a few hours of experimentation) of gRPC and ASP.NET Core 3.0. I’ve conducted some experiments as part of our quarterly Madgex hack day. This will be an introductory post so I don’t expect to show everything in fine detail. This is intended to give an overview of how the main pieces fit together and I’ll hopefully then dive into the details in future posts.

What is gRPC?

gRPC is a schema-first framework initially created by Google. It supports service to service communication over HTTP/2 connections. It uses the Protobuf wire transfer serialisation for lightweight, fast messaging between the services. As more and more of us build interconnected microservices it can sometimes be a pain using REST as our protocol over HTTP for such purposes. REST was designed for human-readable data transfer and often results in a fair amount of boiler-plate code on both the client and server services to implement.

gRPC allows us to define our message schema and “contract” up front using Protocol Buffers. This file is then used to automatically generate much of the client and server code on our behalf. It is an interesting technology and looks well placed to become a popular choice when working with microservices. For my hack day project, I wanted to take it for a quick spin and see if I could get a client and server working over gRPC.

NOTE: The information in this post is therefore based on early code and has the potential to change during the remaining previews and after release. If you’re reading this in the distant future you may want to look for updated blog posts from me!

Getting Started

Microsoft is actively contributing to the “gRPC for .NET” repository to support gRPC in the ASP.NET Core 3.0 timeframe. Currently, this is a work in progress, there is no formal NuGet package available and it depends on some features not yet available in the current ASP.NET Core 3.0 preview 2 release.

To support working with the newest bits, I found that I first needed to download the latest “preview 3 “daily build using the links provided on GitHub.

The sample “domain” for my hack was to develop a basic client data API which would be exposed as a gRPC server.

Creating the Protobuf Service Descriptor

gRPC uses a .proto (Protobuf) file to define the shape of your service contract. This contains the “schema” for the messages that will be sent between the exposed services.

My initial proto file looks like this:

syntax = "proto3";

package ClientPropertyEndpoint;

service ClientProperty {
    rpc GetProperty (PropertyRequest) returns (PropertyReply) {}
}

message PropertyRequest {
    string propertyId = 1;
}

message PropertyReply {
    string clientName = 1;
    string organisationName = 2;
    string productName = 3;
}

For now, I’ve defined a single RPC service interface called GetProperty. This accepts a PropertyRequest and returns a PropertyReply which are defined underneath the service.

Messages are defined using scalar types by providing the type, a name and then a unique number for the field. These uniquely identify the fields as they’ll appear in the binary message format and should not change once defined.

For example, my PropertyRequest message has a single string value called “propertyId” and I’ve assigned it a value of 1.

Creating the Server

The next step is to create an ASP.NET Core 3.0 server which will use code automatically generated from the proto file to reduce the amount of code we need to add. We can stub out the functionality over the generated code, which handles the serialisation and communication.

I created a standard ASP.NET Core 3.0 API project and then updated the csproj file in line with the available example in the grpc-dotnet repo.

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

  <PropertyGroup>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
  </PropertyGroup>

  <ItemGroup>
    <Protobuf Include="..\..\Proto\*.proto" GrpcServices="Server" />
    <Content Include="@(Protobuf)" LinkBase="" />
  </ItemGroup> 

  <ItemGroup>
    <PackageReference Include="Google.Protobuf" Version="3.6.1" />
    <PackageReference Include="Grpc.Tools" Version="1.19.0-pre1" PrivateAssets="All" />   

    <PackageReference Include="MediatR" Version="6.0.0" />
    <PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="6.0.1" />
  </ItemGroup>

</Project>

Without diving too deep this references the .proto file and the Grpc.Tools library in order to support the code generation work.

The next step is create a class derived from the generate code base class which once completed looks like this:

using Grpc.Core;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
using ClientPropertyEndpoint;
using MediatR;

namespace Server
{
    public class PropertyService : ClientProperty.ClientPropertyBase
    {
        private readonly ILogger _logger;
        private readonly IMediator _mediator;

        public PropertyService(ILoggerFactory loggerFactory, IMediator mediator)
        {
            _logger = loggerFactory.CreateLogger<PropertyService>();
            _mediator = mediator;
        }

        public override async Task<PropertyReply> GetProperty(PropertyRequest request, ServerCallContext context)
        {
            _logger.LogInformation($"Handling request for property Id '{request.PropertyId}'");

            var property = await _mediator.Send(new ClientPropertyQuery(request.PropertyId));

            if (property is null)
                context.Status = new Status(StatusCode.NotFound, $"Property Id '{request.PropertyId}' was not found");

            return property ?? new PropertyReply();           
        }
    }
}

You can see the using statement in line 4 which matches the name of the package as defined in the proto file.

A static class ClientProperty has been generated which includes a sub-class called ClientPropertyBase which is what we derive from.

I can then override the base GetProperty method, which is so named because the proto file defines a service which includes that interface. It accepts a PropertyRequest type and returns a Task<PropertyReply>. Both of these have been code generated for me based on the proto file.

The only code I need to add here is the logic for fulfilling the request. In this sample, I have an in-memory store of some basic test data which I query using MediatR. It’s not too important how those bits work and you can code this however you need.

The program.cs for this API looks like this:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.ConfigureKestrel(options =>
                {
                    options.Limits.MinRequestBodyDataRate = null;
                    options.ListenLocalhost(50051, listenOptions =>
                    {
                        // later we'll use SSL
                        listenOptions.Protocols = HttpProtocols.Http2;
                    });
                })
            .UseStartup<Startup>();
            });
}

This using the new ASP.NET Core 3.0 generic host flow to register a web host which uses Kestrel. Kestrel is set up to listen on HTTP2 on port 50051.

The final bit I’ll show is the Startup class which looks like this:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<ClientPropertyStore>();

        services.AddGrpc();

        services.AddMediatR(typeof(Startup).GetTypeInfo().Assembly);
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseRouting(routes =>
        {
            routes.MapGrpcService<PropertyService>();
        });
    }
}

This calls the AddGrpc extension on the IServiceCollection, available for now due to the Grpc.AspNetCore.Server code I copied in. Later this will be built in a NuGet library.

It then uses the new ASP.NET Core 3.0 routing middleware to map a GrpcService route to the PropertyService. This will allow the server to handle the gRPC requests.

Creating the Client

To send request to the server I created a basic .NET Core console app client. The csproj file for this also had a reference to the proto file so that the client code could be generated.

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

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <LangVersion>latest</LangVersion>
  </PropertyGroup>

  <ItemGroup>
    <Protobuf Include="..\..\Proto\ClientProperty.proto" GrpcServices="Client" />
    <Content Include="@(Protobuf)" LinkBase="" />
  </ItemGroup> 

  <ItemGroup>
    <PackageReference Include="Google.Protobuf" Version="3.6.1" />
    <PackageReference Include="Grpc.Core" Version="1.19.0-pre1" />
    <PackageReference Include="Grpc.Tools" Version="1.19.0-pre1" PrivateAssets="All" />
  </ItemGroup>
</Project>

The only code needed lives in the Main method in this sample:

static async Task Main(string[] args)
{
    Console.WriteLine("Starting GRPC Client...");
    Console.WriteLine();

    var channel = new Channel("localhost:50051", SslCredentials.Insecure);
    var client = new ClientPropertyEndpoint.ClientProperty.ClientPropertyClient(channel);

    try
    {
        var response = await client.GetPropertyAsync(new PropertyRequest { PropertyId = "ac516792-94fc-4e0c-b39c-59b2fcd58a4e" });

        Console.WriteLine("Org Name: " + response.OrganisationName);
        Console.WriteLine("Client Name: " + response.ClientName);
        Console.WriteLine("Product Name: " + response.ProductName);
    }
    catch (RpcException ex)
    {
        if (ex.StatusCode == StatusCode.NotFound)
        {
            Console.WriteLine(ex.Status.Detail);
        }
    }

    Console.WriteLine();
    Console.WriteLine("Shutting down");
    channel.ShutdownAsync().Wait();

    Console.WriteLine("Press any key to exit...");
    Console.ReadKey();
}

First we establish a gRPC channel to the server. For this sample I’m using insecure communication. I’ll show how SSL can be applied in a future post.

I then create a ClientPropertyClient which is a class created by the code gen process based on the proto file.

I can then call methods on the client which map to the methods as defined in the proto file. I simply create a PropertyRequest object (again, the class is from generated code) and send it via the client.

Here I write out the values from the PropertyResponse object to the console. Good enough for now!

I also handle a RpcException which is thrown if the server sends a NotFound status for example. I’ve not explored this too deeply so there may be better approaches to achieve this.

And that’s basically it. I can fire up the server and then fire the client up which will send a request over HTTP2 and gRPC. Awesome!

Summary

This has been a high-level overview of gRPC, after I’ve only spent a few hours learning how it works. I hope it serves to show one of the big benefits of the gRPC proto file which is the fact that we have to write very little boiler-plate code. There are no Controllers to define and even the models can be easily generated for both the client and server. On the client side, I don’t need to construct HTTP requests and fire them. I can simply make a call to a code generated client method, passing a simple request message.



ASP.NET Core 3 Hosting :: ASP.NET Core 3 Preview

clock February 26, 2019 06:50 by author Jervis

ASP.NET Core 3.0 App with .NET Core 3.0 preview 2 release

Before we create the application, first we need to install Visual Studio 2019 and .NET Core 3.0. Let’s first install .NET Core 3.0 SDK.

Installing .NET Core 3.0

To download .NET Core 3.0 preview 2, visit this link. Based on your platform, download the appropriate installer. Once the download is complete, run the installer to install .NET Core 3.0 on your system. The .NET Core 3.0 preview installation will not impact your existing .NET Core version installation.

Installing Visual Studio 2019 Preview

To install Visual Studio 2019 preview, download the installer from this location. Don’t worry. Visual Studio and Visual Studio “Preview” can be installed side-by-side on the same device. It will have no impact on your current stable VS installation.

Visual Studio 2019 offers a completely new project creation experience. Once the installation is complete, let’s open the Visual Studio 2019 preview and create the ASP.NET Core 3.0 app. Select the ASP.NET Core Web Application template.

When you click Ok, you will get the following prompt. Select ASP.NET Core 3.0 and choose the MVC template.

The Visual Studio will create an ASP.NET Core 3.0 based MVC project. The solution structure looks similar to the previous version of ASP.NET Core. However, there is one change with respect to dependencies reference, which is the Microsoft.AspNetCore.Mvc.NewtonsoftJson nuget package.

 

ASP.NET Core shared framework (Microsoft.AspNetCore.App) will only contain first-party assemblies that are fully developed, supported, and serviceable by Microsoft. As part of this change, the following sub-components will be removed from shared framework.

  • Json.NET (Newtonsoft.Json)
  • Entity Framework Core (Microsoft.EntityFrameworkCore.*)
  • Microsoft.CodeAnalysis (Roslyn)

The project file is now targeting to .NET Core 3.0 and also has a reference of Microsoft.AspNetCore.Mvc.NewtonsoftJson package.

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

  <PropertyGroup>
    <TargetFramework>netcoreapp3.0</TargetFramework>
  </PropertyGroup> 

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.0.0-preview-19075-0444" />
  </ItemGroup> 

</Project>

Let’s take a look at the code level changes.

1. Open the Program.cs and you will see the following code. The ASP.NET Core 3.0 templates use Generic Host. Previous versions used Web Host.

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    } 

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

The goal of the Generic Host is to decouple the HTTP pipeline from the Web Host API to enable a wider array of host scenarios. Messaging, background tasks, and other non-HTTP workloads based on the Generic Host benefit from cross-cutting capabilities, such as configuration, dependency injection (DI), and logging.

The above code uses webBuilder which is a type of IWebHostBuilder interface used with WebHostBuilder. But it will be deprecated and eventually its functionality will be replaced by HostBuilder, though the interface will remain.

The biggest difference between WebHostBuilder and HostBuilder is that you can no longer inject arbitrary services into your Startup.cs. Instead, you will be limited to the IHostingEnvironment and IConfiguration interfaces. This removes a behavior quirk related to injecting services into Startup.cs before the ConfigureServices method is called.

2. As mentioned earlier, Json.NET is removed from the shared framework and now needs to be added as a package. Open Startup.cs and take a look at ConfigureServices method

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        // This lambda determines whether user consent for non-essential cookies is needed for a given request.
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    }); 

    services.AddMvc()
        .AddNewtonsoftJson();
}

3. There are some updates to EndPoint routing introduced with ASP.NET Core 2.2. Endpoint routing allows frameworks like MVC as well as other routable things to mix with middleware in a way that hasn’t been possible before. With this, routing decisions can occur earlier into the pipeline so that incoming requests can be mapped to their eventual endpoint before MVC is even invoked. This is now present in the project templates in 3.0.0-preview-2 (Startup.cs -> Configure()).

app.UseRouting(routes =>
{
     routes.MapApplication();
     routes.MapControllerRoute(
         name: "default",
         template: "{controller=Home}/{action=Index}/{id?}");
});

Here, the app.UseRouting() call adds a new Endpoint Routing middleware. The UseRouting replaces many of the features that were implemented inside UseMvc() in the past. The MapApplication() brings in MVC controllers and pages for routing and MapControllerRoutedefines the default route.

That’s it for now

Summary

ASP.NET Core 3.0 will bring some code breaking changes and some of them are available with this preview 2 release. The change regarding ASP.NET Core shared framework to include only those libraries which are developed, supported, and serviceable by Microsoft will definitely reduce the application size by a few bytes. It is the right time to play around ASP.NET Core 3.0 and expect some more changes when the final version comes out.



Cheap ASP.NET 4.5 Hosting

We’re a company that works differently to most. Value is what we output and help our customers achieve, not how much money we put in the bank. It’s not because we are altruistic. It’s based on an even simpler principle. "Do good things, and good things will come to you".

Success for us is something that is continually experienced, not something that is reached. For us it is all about the experience – more than the journey. Life is a continual experience. We see the Internet as being an incredible amplifier to the experience of life for all of us. It can help humanity come together to explode in knowledge exploration and discussion. It is continual enlightenment of new ideas, experiences, and passions


Author Link

 photo ahp banner aspnet-01_zps87l92lcl.png

 

Corporate Address (Location)

ASPHostPortal
170 W 56th Street, Suite 121
New York, NY 10019
United States

Tag cloud

Sign in