Visual ReCode logo Visual ReCode

Your migrated .NET Core gRPC project is based on the default Microsoft grpc project template. For this document we will use the Basic Calculator example which is available from our GitHub Examples repository.

Project Structure

.NET Core gRPC projects are console applications, with a Program.cs file that initializes and runs the service(s), and a Startup.cs where the service is constructed and configured. There is a Protos folder where Protobuf IDL (Interface Definition Language) files are kept, and a Services folder containing the C# implementation code.

The layout of the BasicCalculator project looks like this:

- Properties
 |- launchSettings.json
- Protos
 |- basic_calculator_core.proto
- Services
 |- Calculator.cs
- appsettings.Development.json
- appsettings.json
- BasicCalculatorCore.csproj
- Program.cs
- Startup.cs

The .proto File

The Protos/basic_calculator_core.proto file is the Protobuf IDL definition of your new gRPC service. This is used by the Protobuf compiler to generate the message classes (the equivalent of DataContract classes) and the base class for the service implementation.

The Service

The Services/Calculator.cs contains the implementation of the gRPC service. This is effectively a facade over your service contract implementation from the original WCF application, with logging and error handling added. The implementation is registered with the .NET Core Dependency Injection service in Startup.cs, so it can be retrieved from the constructor.

This class is declared partial so you can add modifications to it without changing the generated file.

public partial class Calculator : global::BasicCalculatorCore.Protos.Calculator.CalculatorBase
{
    private readonly Microsoft.Samples.GettingStarted.ICalculator _serviceContract;
    private readonly ILogger<Calculator> _logger;

    public Calculator(Microsoft.Samples.GettingStarted.ICalculator serviceContract, ILogger<Calculator> logger)
    {
        _serviceContract = serviceContract;
        _logger = logger;
    }

    public override Task<Protos.AddResponse> Add(Protos.AddRequest request, ServerCallContext context)
    {
        try
        {
            var returnValue = _serviceContract.Add(request.Value1, request.Value2);
            var response = new Protos.AddResponse { Value = returnValue };
            return Task.FromResult(response);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error invoking Add");
            throw new RpcException(new Status(StatusCode.Internal, ex.Message));
        }
    }

    // Other methods elided
}

Note that in this example, the original contract methods were not asynchronous so Task.FromResult is used to return the response. The methods in the generated gRPC service base class are all declared with Task<Response> as the return type by default. If your original service methods are working with any kind of IO, such as a database, the file system, or other services, and they are not using async/await and Tasks, consider changing them in the original code before running the migration. WCF works happily with async methods.

Startup.cs

The Startup class is where your .NET Core gRPC application is configured. If you want to add authentication/authorization, specific logging libraries (like Loupe) or any other components or middleware, this is where you do it.

In the generated Startup class, the original service contract implementation is registered with Dependency Injection in the ConfigureServices method, as shown here:

public void ConfigureServices(IServiceCollection services)
{
    services.AddGrpc();
    services.AddScoped<Microsoft.Samples.GettingStarted.ICalculator,
                       Microsoft.Samples.GettingStarted.CalculatorService>();
}

If your original application used a [ServiceContract] interface, then it will be registered using that interface as the dependency. If it was directly on the class, the class will be registered as the dependency. If you need to configure more dependencies to support the creation of the service contract class, you should add them here.

Using the AddScoped method means a new instance of the class will be created for every incoming request.

The service itself is registered with the application in the Configure method, using endpoints.MapGrpcService in the app.UseEndpoints callback, as shown here:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseRouting();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGrpcService<Services.Calculator>();

        endpoints.MapGet("/", async context =>
        {
            await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
        });
    });
}

Again, if you are adding authentication/authorization to your new application, those calls (e.g. app.UseAuthentication and app.UseAuthorization) should go between app.UseRouting and app.UseEndpoints.

See Also