CoreWCF is a port of the functionality of the WCF Server libraries to the .NET [Core] platform. In this article, I'll talk about the objectives of the CoreWCF project and how it can be used to more easily modernize applications to .NET. CoreWCF is an open source project and can be found at https://github.com/CoreWCF/CoreWCF.
If you're going to work with the code examples shown in this article, you'll need the following installed on your system:
- Visual Studio 2022
- .NET 6.0 or greater
Introduction to CoreWCF
Windows Communication Foundation (WCF) is an RPC mechanism that enables rich client/server communication between processes. The advantages WCF has over other RPC mechanisms such as WebAPI, REST, or gRPC are that the contracts and data types are defined using .NET classes and interfaces, it supports a high fidelity serialization of data types, has efficient binary serialization, and includes built-in security and rich communication paradigms such as callbacks and streaming. WCF supports WS-* standards-based communication using SOAP and proprietary bindings for more efficient communication between .NET on the client and server. For these reasons, WCF has been a very commonly used RPC stack in .NET Framework apps.
When the .NET Core project started in 2016, the goal was to take the best of .NET, but also to use it as an opportunity to pare down some of the bloat that had accrued with .NET Framework. The software industry has largely moved on from SOAP to restful Web API and gRPC. WCF has a very large and complex surface area that wasn't seen as fitting well with the “lean and mean” goals of .NET Core.
Microsoft had been recommending WCF as the RPC mechanism to use in .NET for some time, and customers have a large investment in WCF services. We realized that the lack of WCF capability was a major blocker to being able to modernize the apps to .NET Core.
The.NET team didn't initially intend to include WCF in the box, but there were a couple of devoted WCF developers who wanted to update WCF and bring it to modern .NET. So we funded the effort to seed a community project, which became CoreWCF. This gathered the attention of other developers in the community who needed WCF support, including the .NET team at AWS, who contributed toward the support of WS-* security protocols.
CoreWCF isn't a straight port of WCF to .NET - there were a couple of architectural changes that were needed as part of the port:
- Using ASP.NET Core as the service host, push pipeline, and the middleware pattern for extensibility.
- Removing the obsolete Asynchronous Programming Model (APM) programming pattern as it made the codebase incredibly hard to work with, which isn't desirable for a community project wanting to encourage external contributions.
- Removing platform-specific and IO code. Refactoring apps into microservices and Linux-based containers is a common requirement, and so CoreWCF needs to be able to run anywhere that .NET core can be run.
After three years of development, the feature set was deemed functional enough for mainstream usage and so the 1.0 of the project was released in April 2022. CoreWCF, like the .NET platfom it's built on, is cross-platform, so can be used as easily on Linux, in a Kubernetes container, or on MacOS as it can be on a Windows server.
When talking with WCF customers, particularly larger enterprise customers, a large concern with adopting a community project is what level of support would be available. Microsoft came to an internal agreement so that Microsoft Support is available for CoreWCF when used in production. If you've ported to CoreWCF and you find operational bugs (not missing features), Product Support coordinates a fix.
Using CoreWCF in Practice
There are a couple of parts to creating a WCF service:
- Defining the service contracts: C# interfaces with attributes
- Implementing the contracts: C# classes implementing the service interface
- Creating data contracts for data that will be serialized as part of the service contracts: POCO classes with optional attribution
- Exposing the services: Use WCF Bindings
The key advantage of CoreWCF is that in most cases, it doesn't involve any changes to the service definition, implementations, or data contracts. The same code you used for these items in WCF is supported in CoreWCF. The differences come with the ceremony as to how the services are hosted and the bindings that are supported.
What Does a CoreWCF Service Look Like?
The best way to understand CoreWCF is probably to create a new service from scratch. The easiest way to do that is using the new project template (a community contribution), which can be installed using the dotnet tool.
dotnet new --install CoreWCF.Templates
That installs a new template that can be used from the command line or via Visual Studio. Once installed, in the Visual Studio New Project dialog, type “corewcf” in the search box to find the template, as seen in Figure 1
Go through the dialogs to create a project for .NET 6.0 or 7.0. The template has support for the other versions, but with .NET 6+, you get the simplified ASP.NET starter code and top-level statements.
Opening IService.cs
, you can see that the definition of the service is identical to how it's defined with WCF. You have an interface decorated with the ServiceContract
attribute, and each of the methods to be exposed is decorated with OperationContract
. The only difference is that the attributes are now from the CoreWCF namespace rather than System.ServiceModel
namespaces.
using CoreWCF;
using System;
using System.Runtime.Serialization;
namespace CoreWCFService1
{
[ServiceContract]
public interface IService
{
[OperationContract]
string GetData(int value);
[OperationContract]
CompositeType GetObject(CompositeType obj);
}
}
The service implementation is in the form of a class that implements the service definition. There are no changes from how this is defined in WCF.
public class Service : IService
{
public string GetData(int value)
{
return $"You entered: {value}";
}
public CompositeType GetObject(CompositeType obj)
{
if (obj == null)
{
throw new ArgumentNullException("composite");
}
if (obj.BoolValue)
{
obj.StringValue += "Suffix";
}
return obj;
}
}
The last part of the file uses the DataContract
and DataMember
attributes to mark a class for serialization so that it can be used as part of service definition.
[DataContract]
public class CompositeType
{
bool boolValue = true;
string stringValue = "Hello ";
[DataMember]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}
[DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
}
Program.cs
sets up the host and exposes the services via bindings. If you're familiar with ASP.NET Core, this will look somewhat familiar because CoreWCF uses the same host and builder model, but with extra middleware that implements WCF services and provides WSDL generation through metadata.
var builder = WebApplication.CreateBuilder();
builder.Services.AddServiceModelServices();
builder.Services.AddServiceModelMetadata();
builder.Services.AddSingleton<IServiceBehavior,
UseRequestHeadersForMetadataAddressBehavior>();
var app = builder.Build();
app.UseServiceModel(bld =>
{
bld.AddService<Service>();
bld.AddServiceEndpoint<Service, IService>(new BasicHttpBinding(
BasicHttpSecurityMode.Transport), "/Service.svc");
var mb = app.Services.GetRequiredService<ServiceMetadataBehavior>();
mb.HttpsGetEnabled = true;
});
app.Run();
By default, the ASP.NET hosting runs the process using the Kestrel web server, but it can also be hosted in IIS Express, depending on how the process is launched. That can be changed in Visual Studio using the launchSettings.json
file.
Bindings in CoreWCF
Bindings in WCF define how the services will be exposed over the wire. For example, the BasicHttpBinding
used above exposes a service over HTTP using the SOAP protocol. The NetTcp Binding uses a custom binary XML serialization to be more efficient over the wire than plain text XML. WCF supported a large range of bindings and only a subset are currently included in CoreWCF, listed in Table 1.
The WSFederationHttpBinding
and related security functionality have been developed by AWS. The WebHttpBinding
was implemented by a community member who had a large investment in services using the Binding, and they determined that it would be cheaper for them to port the binding than converting all the services to MVC or WebAPI.
The main bindings not yet available that we hear developers asking for are:
- NamedPipeBinding
- MSMQBinding
Both are currently under development. In line with the architectural changes to be cross-platform, the Message Queue binding will be changing to have a provider model for the actual queue implementation so that it isn't tied to the Windows MSMQ Server and can be used with queue implementations for cloud providers such as AWS and Azure.
Configuration in CoreWCF
WCF had strong support for configuration. The bindings that control the way that services are exposed could be specified in code, but more commonly was done through configuration. The idea was that you can create the service, and then Ops/IT can configure the binding and its properties without needing to rebuild the application. This resulted in long and complex configuration files, which proved to be a source of errors and complexity. Later versions of .NET Framework introduced a simplified form of configuration to reduce the verbosity and provide common defaults.
The WCF configuration model was based around the .NET Framework xml/web.config model for configuration that isn't fully supported on Core, and which now uses a JSON-based configuration format. The lack of configuration support was a problem for a couple of users, and their combined work has resulted in the package CoreWCF.Configuration
. Currently, not all options are supported, but this should help when migrating WCF applications.
Here are the basic steps to use the configuration library:
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="netTcpBindingConfig" receiveTimeout="00:10:00" />
</netTcpBinding>
</bindings>
<services>
<service name="Services.ISomeContact">
<endpoint address="net.tcp://localhost:8750/Service"
binding="netTcpBinding"
bindingConfiguration="netTcpBindingConfig"
contract="ISomeContact" />
</service>
</services>
</system.serviceModel>
builder.Services.AddServiceModelServices();
builder.Services.AddServiceModelConfigurationManagerFile("wcf.config");
builder.Services.AddServiceModelMetadata();
builder.Services.AddSingleton<IServiceBehavior,
UseRequestHeadersForMetadataAddressBehavior>();
If NetTcpBinding
is used, the TCP port numbers of endpoints using this binding must be additionally specified when configuring the builder.
That's all there should be to it! If there are any configuration problems in the wcf.config
file, they will be reported when the application starts.
The set of supported configuration elements are:
<bindings>
<basicHttpBinding maxBufferSize="" transferMode="" textEncoding="" />
<netHttpBinding maxBufferSize="" transferMode="" textEncoding=""
messageEncoding="" />
<netTcpBinding maxBufferSize="" maxBufferPoolSize="" maxConnections=""
transferMode="" hostNameComparisonMode="" />
<wSHttpBinding maxBufferPoolSize="" />
</bindings>
All the bindings above also support these properties: name=""
, securityMode=""
, maxReceivedMessageSize=""
, receiveTimeout=""
, closeTimeout=""
, openTimeout=""
, sendTimeout=""
, and xmlReaderQuotas=""
.
<services>
<endpoint name="" address="" binding="" bindingConfiguration="" contract="" />
</services>
The goal is to add more configuration support over time.
Client-Side WCF Support
.NET Core includes client-side support for calling WCF services with the System.ServiceModel.*
NuGet packages. Client wrappers can be generated for services using the ConnectedServices and Service Reference UI in Visual Studio. From the command line, the dotnet-svcutil
tool can be used to generate the same wrapper code.
dotnet tool install --global
dotnet -svcutil
dotnet -svcutil --roll -forward LatestMajor https://localhost:7173/Service.svc?wsdl
Using either of those tools requires that the ServiceModelMetadata
feature is added as part of the app initialization. This is included in the project template and the example code further up. You may need to add the ?wsdl
query string to the URL to get the metadata.
The following console app code uses the service wrapper to call the service above:
using ServiceReference1;
// Instantiate the Service wrapper specifying the binding
// and optionally the Endpoint URL.
var client = new ServiceClient(
ServiceClient.EndpointConfiguration.BasicHttpBinding_IService,
"https://localhost:7173/Service.svc");
var simpleResult = await client.GetDataAsync(10);
Console.WriteLine(simpleResult);
var msg = new CompositeType(){ StringValue = "A ", BoolValue=true};
var msgResult = await client.GetObjectAsync(msg);
Console.WriteLine(msgResult.StringValue);
How to Modernize a WCF Service to .NET Core
Assuming you have an existing WCF application, there are a couple of approaches that can be used to modernize the services using CoreWCF:
- In-place replacement while running on .NET Framework
- Copy individual services over
- In-place upgrade to .NET Core
Read on for some discussion about these options.
In-Place Replacement While Running on .NET Framework
The 1.x releases of CoreWCF have been designed so that it can be used on .NET Framework with ASP.NET Core 2.1. The services can be updated individually to be exposed using CoreWCF before changing the runtime and project file format. This can be an effective way to ensure that CoreWCF will meet your needs before diving into a larger migration.
The code shown in this article for .NET 6 uses top-level statements and the simplified ASP.NET initialization code. Samples for .NET Framework and earlier versions of .NET can be found at https://github.com/corewcf/samples.
Copy Individual Services Over
Some developers prefer to take the approach of creating a new project and then copying code across from their existing projects. You should find that only a few changes are required to the service classes and interfaces to be able to use them with CoreWCF. The CoreWCF project template includes the ceremony for setting up the host. You will need to specify the bindings for each service in the app.UseServiceMode
l block, or use the XML configuration support to set up the bindings.
In-Place Upgrade to .NET Core
If you're doing an in-place upgrade of the project from .NET Framework to Core, there are various steps that need to be performed, regardless of the use of WCF. The Upgrade Assistant tool discussed in other articles in this issue can be used to perform those migration steps, such as updating project file format, changing NuGet references, namespace upgrades, etc.
At the time of writing this article, one of our summer interns has been working on additions to the Upgrade Assistant to perform some of the CoreWCF migration actions for you. By the time you read this, they may have been included in the tool. For more information, see the repo at https://github.com/dotnet/upgrade-assistant.
The manual steps that need to be performed after the .NET Upgrade Assistant has updated a project to .NET 6 are:
- Add a NuGet package reference to
CoreWCF.Primitives
and the packages for the type of binding you will be using. - Replace any
using System.ServiceModel;
import withusing CoreWCF;
as the namespace has been changed. - CoreWCF is built on top of ASP.NET Core, so you need to update the project to start an ASP.NET Core host.
- Update the project's SDK to
Microsoft.NET.Sdk.Web
(because it uses ASP.NET Core). - Make the app's
Main
method async. - Replace
ServiceHost
setup with the code shown above. - The services need to be exposed via bindings.
- That can be done with code such as:
app.UseServiceModel(bld =>
{
bld.AddService<Service>();
bld.AddServiceEndpoint<Service, IService>(new
BasicHttpBinding(BasicHttpSecurityMode.Transport), "/Service.svc");
});
Each service needs to be added and be exposed by at least one binding.
Or it can be done via configuration. If using configuration, be aware that not all of what you could specify in WCF is currently supported in CoreWCF. See the section on configuration above.
For example, the <host>
element isn't supported in the service model configuration. Instead, the port that endpoints listen to is configured in code. So, you need to remove the <host>
element from wcf.config
and add the following line to the app's main
method, before the call to builder.Build
:
builder.WebHost.UseNetTcp(8090);
What's Next for CoreWCF?
The project is being developed in the open at https://github.com/corewcf/corewcf. Its release schedule is independent from that of .NET or Visual Studio: It releases when new functionality is ready to be consumed. We have plans for supporting additional Bindings such as NamedPipe
and MessageQueue
. As a community-driven project, we focus on what's most important to customers and have a pinned issue (https://github.com/CoreWCF/CoreWCF/issues/234) for discussing what features should be added next. If you want a listed feature, add a thumbs up and if it's not on the list, please create a new entry for it.
We welcome contributions from the community, both large and small. If you have a small change or fix, feel free to create a Pull Request (PR) with the change. If you want to contribute something larger, we suggest that you create an issue to discuss the feature and design before submitting a PR - that generally results in a smoother process for all involved.
Conclusion
CoreWCF provides a smoother modernization path for applications that have taken a strong dependency on WCF. Using CoreWCF is often a quicker migration path than re-designing services to use a different RPC mechanism.
Table 1: Bindings supported by CoreWCF
Binding | Description |
---|---|
BasicHttpBinding | Uses SOAP to encode messages over HTTP and is compatible with ASMX-based services and clients using SOAP 1.1 |
NetHttpBinding | Uses a binary format over HTTP for smaller message encoding. If the service contract is duplex, it will use web sockets to create a bi-directional channel |
WSFederationHttpBinding | Supports WS-Federation to enable federated security |
WSHttpBinding | SOAP 1.2-based, only uses open standard WS-* protocols for interoperability with other frameworks |
NetTcpBinding | Uses TCP for delivery with a binary encoding for the message format, with support for Windows Security |
WebHttpBinding | Supports creating Restful web services using WCF contract definitions |