WCF services on .NET 8 or 10: CoreWCF, gRPC or Web API

If your .NET Framework application hosts WCF services, the port to .NET 8 or 10 has one problem the rest of the code does not: Microsoft ships the WCF client for .NET and no server. The server side comes from CoreWCF, a community project, or from a rewrite to gRPC or a Web API. This guide says what each option covers, using Microsoft’s and CoreWCF’s own pages.

What Microsoft ships for .NET

The dotnet/wcf repository holds, in its own words, “the .NET Core version of the Windows Communication Foundation client libraries”, and describes them as a subset of the .NET Framework version. The packages are System.ServiceModel.Primitives, System.ServiceModel.Http (BasicHttpBinding), System.ServiceModel.NetTcp, System.ServiceModel.NetNamedPipe, System.ServiceModel.UnixDomainSocket and System.ServiceModel.Federation (WS-Federation). For hosting a service, the same readme sends you to CoreWCF.

Microsoft’s list of .NET Framework technologies that are not in .NET says the same in a tip: a WCF server can be used in .NET 6 and later through the CoreWCF NuGet packages. The page also says that Windows Workflow Foundation is not supported in .NET, which takes Workflow Services with it.

Microsoft’s gRPC documentation names two recommended paths for a WCF application. gRPC is the first. The second is CoreWCF, described as “a community effort to bring support for hosting WCF services to .NET”, with the caveat that it “only supports a subset of WCF’s features, and .NET Framework apps that migrate to use it will need code changes and testing to be successful”. The page calls CoreWCF “a good choice if an app has to maintain compatibility with existing clients that call WCF services”. If you cannot change the clients, the service keeps speaking SOAP, and CoreWCF is the only way to host it on .NET.

CoreWCF

What it is

CoreWCF is “a port of the service side of Windows Communication Foundation (WCF) to .NET Core”, published under the MIT license and supported by the .NET Foundation. The service host runs inside ASP.NET Core. When 1.0 came out, Microsoft wrote on the .NET blog that support for 1.x follows the support of the .NET platform it runs on, and the project’s support document says Microsoft Product Support is available for CoreWCF customers. The current 1.9 releases list .NET Framework 4.6.2 and above, .NET 8, .NET 9 and .NET 10 as supported targets.

The 1.0 announcement describes the scope as “a subset of the functionality from WCF, but represents what we believe are the most used features”. It recommends considering alternatives to SOAP such as gRPC, and says that “The sweet spot for CoreWCF is to make it easier to modernize server and clients”: an existing service with existing clients.

What it has

From the 1.0 announcement, the package list and the release notes:

  • Bindings: BasicHttpBinding, NetHttpBinding, WSHttpBinding, WS2007HttpBinding, NetTcpBinding and WebHttpBinding (WCF REST).
  • Transports beyond HTTP and TCP, each in its own package: named pipes (CoreWCF.NetNamedPipe), Unix domain sockets (CoreWCF.UnixDomainSocket, on Linux and Windows, with a matching client in dotnet/wcf), MSMQ (CoreWCF.MSMQ), RabbitMQ and Kafka.
  • Security: transport security and transport with message credential; user name, certificate and Windows authentication; WS-Federation. The 1.0 announcement lists “Message security beyond Transport and Transport with Message Credentials” as not implemented, and we found no later release note that adds it.
  • WSDL generation. AddServiceModelMetadata() registers the behavior, ServiceMetadataBehavior.HttpGetEnabled = true serves the WSDL over HTTP, and UseRequestHeadersForMetadataAddressBehavior writes the host name the client used into it instead of localhost.
  • Extensibility: IServiceBehavior, IEndpointBehavior and, per the road map, message inspectors and similar extensions with a namespace change.
  • Configuration from a file: the CoreWCF.ConfigurationManager package adds AddServiceModelConfigurationManagerFile(path), which reads services, endpoints and bindings from a system.serviceModel section.

What it leaves out

The 1.0 announcement lists distributed transactions and message queueing as not implemented, and says that NetTcpBinding and WSHttpBinding have “some WS-* features not supported”. An MSMQ package has appeared since. Transactions have not: the release notes from 1.6 to 1.9.1 do not mention them. The project’s feature request thread lists WS-AtomicTransaction, WS-Discovery and the Routing Service without a status, and WS-ReliableMessaging as in progress. We found no CoreWCF package for routing. Tracing was missing in 1.0; basic OpenTelemetry support arrived in 1.8.

How a service moves

The walkthrough puts it in one sentence: “The definition and implementation of data and service contracts is the same as WCF. The major difference is in the definition of the host which is now based on ASP.NET Core, and the ceremony of how a service is exposed.” In practice:

  • Contracts and the service class stay. The attributes come from the CoreWCF namespace; if the project also references System.ServiceModel.Primitives, the road map says the contract attributes can stay in the System.ServiceModel namespace.
  • ServiceHost and the .svc file go. An ASP.NET Core project (dotnet new web) with CoreWCF.Primitives and CoreWCF.Http becomes the host, or dotnet new corewcf from the CoreWCF.Templates package creates one with the pieces below in place.
  • Bindings and endpoints are declared in Program.cs. web.config can be kept through CoreWCF.ConfigurationManager, or its contents moved into code.
  • Authentication is configured in ASP.NET Core, not on the binding. The 1.0 release post says so directly. WSHttpBinding defaults to Windows credentials, which the walkthrough turns off for its example because of that.

The host from the walkthrough, with a BasicHttp endpoint, a WSHttp endpoint over TLS and WSDL enabled:

using CoreWCF;
using CoreWCF.Configuration;
using CoreWCF.Description;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddServiceModelServices().AddServiceModelMetadata();
builder.Services.AddSingleton<IServiceBehavior, UseRequestHeadersForMetadataAddressBehavior>();

var app = builder.Build();

var wsHttp = new WSHttpBinding(SecurityMode.Transport);
wsHttp.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;

app.UseServiceModel(serviceBuilder =>
{
    serviceBuilder.AddService<EchoService>(options => { })
        .AddServiceEndpoint<EchoService, IEchoService>(new BasicHttpBinding(), "/EchoService/basichttp")
        .AddServiceEndpoint<EchoService, IEchoService>(wsHttp, "/EchoService/WSHttps");
});

var metadata = app.Services.GetRequiredService<ServiceMetadataBehavior>();
metadata.HttpGetEnabled = true;

app.Run();

Authorization moved to ASP.NET Core as well. An operation can carry the ASP.NET Core [Authorize] attribute with a policy, and the service calls HttpContext.AuthenticateAsync() with the default scheme when the binding’s client credential type is InheritedFromHost. In that mode a ServiceAuthorizationManager no longer runs, Roles and AuthenticationSchemes on the attribute are not supported, and [AllowAnonymous] has no effect; the build warns about each.

Because CoreWCF runs on .NET Framework 4.6.2 and above, the host can change first and the runtime later. The road map describes this as the intended path: move the service to CoreWCF while it still runs on .NET Framework, then port the rest.

gRPC

Microsoft’s e-book for WCF developers walks through the conversion. A [ServiceContract] becomes a service in a .proto file and a [DataContract] a message. The properties of OperationContract mostly fall away: Name and ReplyAction are SOAP concepts with no meaning in gRPC, ProtectionLevel has no counterpart because there is no message encryption, and IsInitiating and IsTerminating give way to streams that one method opens and closes. A callback contract becomes a server stream and a full-duplex service a bidirectional stream. The book compares the bindings one by one: NetTCP gives persistent connections and small messages between .NET endpoints, gRPC does the same over HTTP/2 for any language; BasicHttpBinding carries SOAP text, gRPC carries Protobuf; an MSMQ binding has no gRPC counterpart, and the book points to a message broker instead; a WebHttpBinding service is better moved to an ASP.NET Core Web API than to gRPC.

The book also lists what gRPC does not have: WS-Security, WS-Federation and XML Encryption; WS-ReliableMessaging, where retries move into code; and distributed transactions, which it covers in an appendix on why two-phase commit does not fit services on mixed platforms. Security is TLS on the connection, with X.509 client certificates for mutual authentication. WSDL is replaced by the shared .proto file; a server reflection extension exists for dynamic discovery.

The ASP.NET Core documentation adds that a browser cannot call a gRPC service directly; gRPC-Web or JSON transcoding fills that gap. For processes on one machine, gRPC on ASP.NET Core works over Unix domain sockets and named pipes, which covers what NetNamedPipeBinding did.

Every gRPC client is generated from the .proto file. A SOAP client that you do not control cannot call the new service, so this path assumes you can change, or replace, every client.

Web API

An ASP.NET Core Web API answers JSON over HTTP, and that is what Microsoft recommends for a WebHttpBinding service. For a service that is called from browsers or from code in other languages it is the plainest target. It is also the one with the least in common with a SOAP contract: the operations, the faults and the client proxies are all redesigned, and an existing SOAP client cannot use it.

Leave the service where it is

The fourth option is to port the rest of the application and leave the service on .NET Framework for now. The WCF client packages exist for this case: the readme names “mid-tier servers that talk to existing WCF services” as a target. Microsoft’s feature list for the client marks BasicHttpBinding, NetHttpBinding, NetTcpBinding and CustomBinding as working on Windows, Linux and macOS, with request/reply, duplex, buffered and streamed transfers, and transport security. It marks message security and WebHttpBinding as not supported, and NTLM as Windows only. A client on .NET 8 or 10 reaches a basicHttp or netTcp endpoint on .NET Framework without the service changing. This works as a first step when the service is the only thing holding back the port, and settles nothing when the service itself is the reason for the port.

Clients

dotnet-svcutil is the .NET version of svcutil. It is installed with dotnet tool install --global dotnet-svcutil, reads the metadata from a running service or a WSDL file, writes ServiceReference/Reference.cs with a class derived from ClientBase, and adds the WCF packages the proxy needs to the project. dotnet-svcutil -u regenerates the proxy after the service changes. Visual Studio offers the same through “Add Service Reference” with the “WCF Web Service” type.

There is no app.config for the client on .NET. The 8.0 release of the client packages lists the removal of the configuration API, and the generated proxy takes the endpoint in code: the walkthrough creates it with new EchoServiceClient(EchoServiceClient.EndpointConfiguration.WSHttpBinding_IEchoService, "https://..."). Bindings and credentials that lived in system.serviceModel are set on the binding and the client object.

What usually hurts on the server side

  • web.config with a long system.serviceModel section. Either keep the file and load it through CoreWCF.ConfigurationManager, or translate it into binding objects in Program.cs. Custom behaviors declared there need the code path.
  • IIS hosting through a .svc file. The service becomes an ASP.NET Core application, and the deployment changes with it.
  • Windows authentication and other credentials. They move to ASP.NET Core authentication; the binding’s credential type becomes InheritedFromHost and policies replace ServiceAuthorizationManager.
  • Message security beyond transport with message credential. Not in CoreWCF and not in gRPC. A client that signs or encrypts SOAP at message level needs the service to stay on .NET Framework until that client changes.
  • OperationContext, behaviors and message inspectors. The road map says most of them need only a namespace change; the ones that reach into the HTTP layer are the ones to test first.
  • Transactions flowing over WS-AtomicTransaction. Not in CoreWCF, and the gRPC e-book explains why nothing replaces them.
  • MSMQ. CoreWCF has a package for it; gRPC has no queue at all.
  • Workflow Services and the Routing Service. Workflow Foundation is not in .NET; for routing we found no CoreWCF implementation.

How to assess your own services

Make a list before you pick a target. For each service: its bindings and security mode as configured in web.config, who calls it (your own .NET code, other teams, Java or other SOAP clients, browsers), and whether those callers can be changed on your schedule. Then check every row against the table: a service whose callers you cannot touch has to keep its contract, which means CoreWCF, or staying on .NET Framework for now. A service whose only callers are your own code can take any of the three. If you would rather have us go through the list with you, the migration service starts with a free check.

Not sure which option fits your services?

Send us the list of projects, or a description of the application, and we answer by email with what blocks the port and what we would propose for each service. The check is free and does not commit you to anything.

If you then want us to do the work, we agree a fixed price for an agreed scope, by email.

  • What does the application do and who uses it?
  • Which .NET Framework version and project types (ASP.NET MVC, Web Forms, WCF, Windows service, WinForms/WPF)?
  • Which third-party packages or components does it depend on?
  • What is the goal: .NET 8/10 on Windows, Linux, containers, cloud?
In WCFCoreWCFgRPC or Web API
BasicHttpBindingYes (CoreWCF.Http)gRPC over HTTP/2, or a Web API; clients are regenerated
WSHttpBinding, WS2007HttpBindingYes; transport security and transport with message credentialNo WS-* protocols; TLS on the connection
NetTcpBindingYes (CoreWCF.NetTcp)gRPC over HTTP/2
NetNamedPipeBindingYes (CoreWCF.NetNamedPipe)gRPC over named pipes or Unix domain sockets
WebHttpBinding (WCF REST)Yes (CoreWCF.WebHttp)ASP.NET Core Web API
NetMsmqBindingCoreWCF.MSMQ packageNone; a message broker instead
Message security (WS-Security)Not implemented beyond transport with message credentialNot in gRPC
Windows authenticationThrough ASP.NET Core authenticationThrough ASP.NET Core authentication
WSDL and metadataYes, HttpGetEnabledShared .proto file; server reflection
Callback contracts, duplexListed as done in the feature threadServer and bidirectional streaming
WS-AtomicTransactionNot implementedNone
WS-ReliableMessagingIn progress in the feature threadNone; retries in code
Workflow ServicesNo; Workflow Foundation is not in .NETNo
Routing ServiceNone foundNone

Sources

More guides