Architektur-Modernisierung: Migration von WCF zu gRPC mit ASP.NET Core – ein pragmatischer Ansatz

Viele Projekte mit verteilten Anwendungen in der .NET-Welt basieren noch auf der Windows Communication Foundation (WCF). Doch wie kommt man weg von der "Altlast" und wie stellt man seinen Code auf sowohl moderne als auch zukunftssichere Beine? Eine mögliche Lösung ist gRPC.

In diesem Artikel:

Versionsinformation:

  • ASP.NET Core 7.0
  • WCF mit .NET 4.8

Den Beispielcode für diesen Artikel finden Sie hier

Einleitung

Der Wechsel von WCF zu gRPC ist ein Schritt, den Projekt-Teams anstreben können, um ihre verteilten Anwendungen zu modernisieren und zu optimieren. WCF-Dienste bieten umfassende Unterstützung für verschiedene Transportprotokolle und Sicherheitsmechanismen. gRPC, ein von Google entwickeltes Open-Source-Framework, verwendet HTTP/2 als Transportprotokoll und verwendet Protocol Buffers (Protobuf) als Standard-Nachrichtenformat, welches höhere Geschwindigkeit und bessere Skalierbarkeit verspricht.

Dieser Artikel stellt einen praktischen und pragmatischen Ansatz zum Migrieren vorhandener WCF-Dienste zu gRPC-Diensten mit ASP.NET Core vor und enthält die notwendigen konkreten Schritte, die zum Planen und Umsetzen der Migration erforderlich sind.

Migration mit gRPC Code-First

Im Standardvorgehen verwendet gRPC einen Contract-First-Ansatz mit Protocol Buffers als Nachrichten- und Contract-Format. Protobuf ist eine schnelle und effiziente Möglichkeit, Daten zwischen verschiedenen Systemen auszutauschen. Zudem erlaubt es die Definition eines Schnittstellen-, Nachrichten- und Datenkontrakts für Kommunikation zwischen verschiedenen Systemen, indem es ermöglicht wird, Service-Methoden und zugehörige Parameter in einer interoperablen Art und Weise zu definieren. Allerdings müsste man dann alle bestehenden Service-Schnittstellen und Modelle in Protobuf beschreiben, was insbesondere bei komplexen Anwendungen einen hohen Aufwand bedeuten kann. Und man hat ja bei existierenden WCF-Lösungen oft .NET sowohl auf der Client- als auch auf der Services-Seite. Um daher den Aufwand bei der Migration zu verringern, gibt es ein Community-Projekt namens protobuf-net.Grpc, das es ermöglicht bestehende WCF-Dienste mittels Code-First-Ansatz in gRPC Services zu refactoren. Dies erlaubt das Schreiben von .NET-Code für die Nutzung von gRPC und die Wiederverwendung existierenden Codes als ersten Schritt zur Modernisierung bestehender Applikationen.

Voraussetzungen für WCF-Dienste

Damit ein bestehender WCF Service als gRPC Code-First Dienst genutzt werden kann, muss der Contract bestimmte Anforderungen erfüllen:

1. Auf dem Contract muss das ServiceContractAttribute gesetzt sein
2. Alle Service-Methoden müssen das OperationContractAttribute haben
3. Alle Service-Methoden dürfen nur ein Argument haben und dieses muss eine Klasse/DTO sein. Gleiches gilt auch für den Rückgabetypen, diese müssen vom Typ void, Task oder eine eigene Klasse/DTO sein
4. Alle DTOs müssen das DataContractAttribute haben
5. Alle Properties auf dem DTO müssen das DataMemberAttribute haben und dies muss eine eindeutige Order haben, damit der richtige Datenfluss garantiert ist.

Im Folgenden sieht man ein Beispiel, wie ein existierender Contract refactored werden muss, damit dieser mit gRPC Code-First nutzbar ist.

				
					[ServiceContract]
public interface IGreeter
{
    [OperationContract]
    public string Greet(string firstName, string lastName);
}
				
			

Dieser Service-Contract erfüllt nicht alle Anforderungen, um als gRPC Code-First Dienst verwendet zu werden, da er mehrere Parameter entgegennimmt. Die Methode kann jedoch leicht angepasst werden, indem zwei DTO-Klassen erstellt werden, welche zum einen die Parameter und zum anderen die Rückgabe enthalten. Zum Beispiel:

				
					[DataContract]
public class GreetRequest
{
    [DataMember(Order = 1)]
    public string FirstName { get; set; }

    [DataMember(Order = 2)]
    public string LastName { get; set; }
}

[DataContract]
public class GreeterResponse
{
    [DataMember(Order = 1)]   
    public string Response { get; set; }
}

[ServiceContract]
public interface IGreeter
{
    [OperationContract]
    public GreeterResponse Greet(GreetRequest request);
}
				
			

gRPC-Dienst in ASP.NET Core hosten

Wenn der Contract alle Anforderungen erfüllt, kann der Dienst mittels des Pakets protobuf-net.Grpc.AspNetCore in ASP.NET Core gehostet werden. Folgende Schritte sind notwendig:

1. Das Nuget-Paket hinzufügen. Dafür kann die Command Line oder der Nuget-Paket-Manager der IDE genutzt werden bzw. die csproj-Datei angepasst werden:

				
					<ItemGroup>
    <PackageReference Include="protobuf-net.Grpc.AspNetCore" Version="1.1.1" />
</ItemGroup>
				
			

2. Die gRPC Code-First Dienste müssen in der Dependency Injection (DI) registriert werden. Hierfür muss die Methode AddCodeFirstGrpc aufgerufen werden.

				
					builder.Services.AddCodeFirstGrpc();
				
			

3. Die Service-Implementierung (GreeterService) muss in die ASP.NET Core Pipeline hinzugefügt werden, um ihn erreichbar zu machen.

				
					app.MapGrpcService<GreeterService>();
				
			

Das vollständige Beispiel sieht dann so aus:

				
					using ProtoBuf.Grpc.Server;
using Service;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCodeFirstGrpc();

var app = builder.Build();
app.MapGrpcService<GreeterService>();
app.Run();
				
			

Damit ist der Dienst in der ASP.NET-Core-Anwendung registriert und erreichbar.

gRPC Code-First Clients

Um einen Client für einen gRPC Code-First Dienst in C# zu erstellen, müssen zuerst die benötigten NuGet-Pakete installiert werden. Die Pakete Grpc.Net.Client und protobuf-net.Grpc enthalten die erforderlichen Klassen, um einen gRPC-Client zu erstellen.

				
					<ItemGroup>
    <PackageReference Include="Grpc.Net.Client" Version="2.51.0" />
    <PackageReference Include="protobuf-net.Grpc" Version="1.1.1" />
</ItemGroup>
				
			

Anschließend muss eine Verbindung zum Dienst hergestellt und eine Client-Instanz erstellt werden. Dazu kann die GrpcChannel-Klasse und die Methode CreateGrpcService mit dem Service Contract verwendet werden. Dies ist die Power des Code-First-Ansatzes für gRPC. WCF-Entwickler werden hier sicherlich die ChannelFactory aus den alten Zeiten wiedererkennen.

Sobald die Client-Instanz erstellt ist, kann die gRPC-Methode über die entsprechende Client-Methode aufgerufen werden.

Hier ist ein Beispiel für die Erstellung eines Clients für den GreeterService:

				
					using Grpc.Net.Client;
using ProtoBuf.Grpc.Client;
using Service;

using var channel = GrpcChannel.ForAddress("https://localhost:7199");
var client = channel.CreateGrpcService<IGreeter>();
var reply = client.Greet(new GreetRequest() { FirstName = "John", LastName = "Doe"});
Console.WriteLine($"Greeting: {reply.Response}");
				
			

WCF Behaviors in gRPC nachstellen

WCF Behaviors ermöglichen zusätzliche Verarbeitungsfunktionen für eingehende und ausgehende Nachrichten in einem WCF-Dienst. Diese Funktionalität kann in gRPC mit Hilfe von ASP.NET Core Middlewares  und gRPC Interceptors  nachgebildet werden. Eine Middleware kann zum Beispiel genutzt werden, um Authentifizierung und Autorisierung zu implementieren, während Interceptors dazu dienen können, eingehende und ausgehende Nachrichten zu überwachen, zu ändern oder zu blockieren.

Zur Registrierung von ASP.NET Core Middlewares kann die Use-Methode der IApplicationBuilder-Schnittstelle aufgerufen werden. So kann mit folgendem Beispiel Authentifizierung für einen gRPC-Dienst hinzugefügt werden.

				
					..
builder.Services
    .AddAuthentication()
    .AddJwtBearer(options =>
    {
        // TODO: JWT Bearer Authentifizierung konfigurieren
    });
....
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapGrpcService<GreeterService>();
....
				
			

Mit dem AddInterceptor-Methodenaufruf auf den GrpcServiceOptions können gRPC-Interceptors konfiguriert werden, um benutzerdefinierten Code für die Verarbeitung von eingehenden und ausgehenden Nachrichten zu registrieren. Interceptors können global für alle Dienste oder aber pro Dienst festgelegt werden. Ein Beispiel für einen globalen Interceptor, der eingehende Nachrichten logged, kann so aussehen:

				
					public class LoggingInterceptor : Interceptor
{
    private readonly ILogger<LoggingInterceptor> _logger;

    public LoggingInterceptor(ILogger<LoggingInterceptor> logger)
    {
        _logger = logger;
    }
    
    public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(TRequest request, ServerCallContext context,
        UnaryServerMethod<TRequest, TResponse> continuation)
    {
        _logger.LogDebug("Aufruf der gRPC-Methode {method} mit Argument {argument}", context.Method, request);
        var result = await base.UnaryServerHandler(request, context, continuation);
        _logger.LogDebug("gRPC-Methode {method} mit Ergebnis {result} wurde aufgerufen", context.Method, result);
        return result;
    }
}
				
			

Um den Interceptor zu registrieren, muss die entsprechende AddInterceptor-Method aufgerufen werden.

				
					// global
builder.Services
    .AddCodeFirstGrpc(options => options.Interceptors.Add<LoggingInterceptor>())

// per Service
builder.Services
    .AddCodeFirstGrpc(_ => { })
    .AddServiceOptions<GreeterService>(options => options.Interceptors.Add<LoggingInterceptor>());
				
			

Durch die Verwendung von ASP.NET Core Middlewares und gRPC Interceptors kann also die Funktionalität von WCF Behaviors in einem gRPC Service nachgebildet werden.

Fazit

Insgesamt bietet der Wechsel von WCF zu gRPC mit ASP.NET Core ein leistungsfähigeres und skalierbareres Framework für die Entwicklung verteilter Anwendungen. Das Code-First-Design mit dem Community-Projekt protobuf-net.Grpc und ASP.NET Core Middlewares sowie die Verwendung von gRPC Interceptors sorgen für einen einfacheren Übergang und verringern den Aufwand für die Portierung vorhandener WCF-Dienste. Die Migration von WCF zu gRPC kann ein komplexes Unterfangen sein, aber sie hat viele Vorteile, einschließlich verbesserter Performance, Skalierbarkeit, Interoperabilität und Zukunftssicherheit.

Wesentlich umfassendere Informationen zu dem Thema gRPC für WCF Entwickler gibt es direkt von Microsoft als eBook.

Mehr Artikel zu ASP.NET Core, gRPC
Kostenloser
Newsletter

Aktuelle Artikel, Screencasts, Webinare und Interviews unserer Experten für Sie

Verpassen Sie keine Inhalte zu Angular, .NET Core, Blazor, Azure und Kubernetes und melden Sie sich zu unserem kostenlosen monatlichen Dev-Newsletter an.

Diese Artikel könnten Sie interessieren
ASP.NET Core
gRPC Code-First mit ASP.NET Core 7 und Blazor WebAssembly

gRPC Code-First mit ASP.NET Core 7 und Blazor WebAssembly

Wie in allen anderen browserbasierten Single-Page-Application (SPA) Frameworks, ist Blazor WebAssembly JSON-over-HTTP (über Web- oder REST-APIs) die bei weitem häufigste Methode, um Daten auszutauschen und serverseitige Vorgänge auszulösen. Der Client sendet eine HTTP-Anfrage mit JSON-Daten an eine URL, mitunter über unterschiedliche HTTP-Verben. Anschließend führt der Server eine Operation aus und antwortet mit einem HTTP-Statuscode und den resultierenden JSON-Daten. Warum sollte man das ändern? Nun, es gibt Gründe - vor allem wenn man in einem geschlossenen System ist und .NET sowohl im Frontend als auch im Backend einsetzt.
30.03.2023
ASP.NET Core
Blazor WebAssembly in .NET 7: UI-Performance-Optimierung auf Komponentenebene

Blazor WebAssembly in .NET 7: UI-Performance-Optimierung auf Komponentenebene

Stockende UI, keine Reaktion nach dem Klick auf einen Button oder einer Eingabe in einem Feld - dies sind nur wenige Beispiele alltäglicher Probleme, die der Nutzung von Client-Anwendungen im Allgemeinen, und bei Webanwendungen im Speziellen, immer wieder auftreten können. In diesem Artikel schauen wir uns an, wie wir komponentenbasierte UIs in Blazor WebAssembly optimieren können, um dadurch eine für die Benutzer zufriedenstellende Geschwindigkeit und ein flüssiges UI zu bekommen.
29.03.2023
.NET
Understanding and Controlling the Blazor WebAssembly Startup Process

Understanding and Controlling the Blazor WebAssembly Startup Process

There are a lot of things going on in the background, when a Blazor WebAssembly application is being started. In some cases you might want to take a bit more control over that process. One example might be the wish to display a loading screen for applications that take some time for initial preparation, or when users are on a slow internet connection. However, in order to control something, we need to understand what is happening first. This article takes you down the rabbit hole of how a Blazor WASM application starts up.
07.03.2023
.NET
Adding Superpowers to your Blazor WebAssembly App with Project Fugu APIs

Adding Superpowers to your Blazor WebAssembly App with Project Fugu APIs

Blazor WebAssembly is a powerful framework for building web applications that run on the client-side. With Project Fugu APIs, you can extend the capabilities of these apps to access new device features and provide an enhanced user experience. In this article, learn about the benefits of using Project Fugu APIs, the wrapper packages that are available for Blazor WebAssembly, and how to use them in your application.

Whether you're a seasoned Blazor developer or just getting started, this article will help you add superpowers to your Blazor WebAssembly app.
28.02.2023
.NET
Blazor WebAssembly in Practice: Maturity, Success Factors, Showstoppers

Blazor WebAssembly in Practice: Maturity, Success Factors, Showstoppers

ASP.NET Core Blazor is Microsoft's framework for implementing web-based applications, aimed at developers with knowledge of .NET and C#. It exists alongside other frameworks such as ASP.NET Core MVC. About two and a half years after the release of Blazor WebAssembly and based on our experiences from many customer projects at Thinktecture, we want to have a close look at the following questions: What is the current state of the framework? How can you successfully use Blazor? And where does it have limitations?
24.11.2022
.NET
Blazor WebAssembly: Debugging gRPC-Web with Custom Chrome Developer Tools

Blazor WebAssembly: Debugging gRPC-Web with Custom Chrome Developer Tools

If you are working with Blazor, gRPC is a big issue for transferring data from APIs to clients. One issue of developing with gRPC-Web is debugging the transmitted data because the data is in an efficient binary message format. In this article, I will show you how to solve this problem with the help of my NuGet.
17.11.2022