4 minute read

When Microsoft started porting .NET Framework to .NET Core (now simply .NET), some features were to remain on .NET Framework. One such feature was the server-side codebase of WCF preventing WCF from being hosted in .NET Core apps.

Although Microsoft has deprecated WCF, I have found many legacy applications rely on WCF for integration. In an ideal world, the integration would be migrated to more modern REST or GraphQL endpoints and the consuming application updated. However, this is often not possible. Fortunately, the .NET community have revived WCF server-side through CoreWCF.

What is CoreWCF?

CoreWCF “is a port of the service side of Windows Communication Foundation (WCF) to .NET Core”. This adds the capability of hosting a WCF endpoint in an ASPNET Core application along with its many benefits.

Creating the WCF Endpoint

The CoreWCF project provides a comprehensive walkthrough of creating a new WCF service and endpoint from scratch. There are also posts on migrating C# WCF definitions to CoreWCF such as this post.

However, some integration solutions don’t have C# contract definitions that can be migrated, such as BizTalk WCF receive locations. This post will focus on the scenario of using an existing WSDL to generate an equivalent WCF endpoint in CoreWCF.

Generating the Service Contract from a WSDL

To generate WCF proxy clients, Microsoft provide dotnet-svcutil which can generate the relevant C# objects from a WSDL file or endpoint. To install this tool, run the command below:

dotnet tool install dotnet-svcutil --global --version "2.2.0-preview1.23462.5"

Notice that a preview version of the tool has been specified. At the time of writing, the current stable version does not include an argument we need which is --serviceContract. This argument skips the generation of the client as it’s not needed for us to host the WCF service. The next command will then generate the ServiceContract:

dotnet svcutil .\path\to\wsdl.wsdl --namespace "*,Namespace.To.Use" --outputDir .\path\to\output\cs\ --serviceContract --noLogo

The command will then output a Reference.cs in the path specified similar to below:

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace Namespace.To.Use
{
    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.2.0-preview1.23462.5")]
    [System.ServiceModel.ServiceContractAttribute(Namespace="http://sample.xmlns.com", ConfigurationName="WcfService")]
    public interface WcfService
    {
        
        [System.ServiceModel.OperationContractAttribute(Action="MyOperation", ReplyAction="*")]
        [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)]
        System.Threading.Tasks.Task<myOperationResponse> myOperationAsync(myOperationRequest request);

        ...
    }

    ...
}

Cleaning up the code

As mentioned, dotnet-svcutil is primarily used to create WCF proxy clients in .NET. However, with a few changes, the generated Reference.cs can be used to define the CoreWCF service.

Renaming the interface

Depending on what the WSDL contains, the generated interface may not follow the typical convention of IServiceName. Therefore I typically rename the interface to follow this convention.

Fix namespaces

As the WCF client-side code is still in .NET, the System.ServiceModel namespace needs to be replaced with the namespace CoreWCF. Typically, the generated code uses fully qualified class names and attributes, therefore, CoreWCF can be added as a using statement and System.ServiceModel removed using find and replace.

using CoreWCF; 

namespace Namespace.To.Use
{
    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.2.0-preview1.23462.5")]
    [ServiceContractAttribute(Namespace="http://sample.xmlns.com", ConfigurationName="WcfService")]
    public interface WcfService
    {
        ...
    }

    ...
}

Fix attribute convention

Another tidy-up step is to remove the Attribute suffix to follow C# convention. Once again, this can be done using find and replace e.g. ServiceContractAttribute becomes ServiceContract.

Remove OperationContract ReplyAction

The generated operations may include the attribute property ReplyAction.

[OperationContract(Action="MyOperation", ReplyAction="*")]
[XmlSerializerFormat(SupportFaults=true)]
System.Threading.Tasks.Task<myOperationResponse> myOperationAsync(myOperationRequest request);

This can cause issues when consuming the WSDL of the CoreWCF service. After some research, a StackOverflow post suggested removing these properties which has so far resolved the issue.

image1

Implementing the service

With the WCF contract generated and tidied up, the service can now be implemented in a service class.

public class WcfService : IWcfService
  {
      private readonly ILogger _logger;

      public WcfService(ILogger<WcfService> logger)
      {
          _logger = logger;
      }

      public string GetData(int value)
      {
          throw new NotImplementedException();
      }

      public CompositeType GetDataUsingDataContract(CompositeType composite)
      {
          throw new NotImplementedException();
      }
  }

To register the generated ServiceContract and implemented service, the simplest way is to follow the walkthrough provided by CoreWCF and replace the sample ServiceContract and implementation.

image2

One thing to note, in the above C# snippet, ILogger is injected as is typical of an ASPNET Core app. The sample project in the walkthrough does demonstrate dependency injection. To allow services to be injected, the implemented WCF service needs to be added to the ServiceCollection

var builder = WebApplication.CreateBuilder();

builder.Services.AddServiceModelServices();
builder.Services.AddServiceModelMetadata();
builder.Services.AddSingleton<IServiceBehavior, UseRequestHeadersForMetadataAddressBehavior>();

builder.Services.AddTransient<WcfService>();

var app = builder.Build();

app.UseServiceModel(serviceBuilder =>
{
    serviceBuilder.AddService<WcfService>();
    serviceBuilder.AddServiceEndpoint<WcfService, IWcfService>(new BasicHttpBinding(BasicHttpSecurityMode.Transport), "/Service.svc");
    var serviceMetadataBehavior = app.Services.GetRequiredService<ServiceMetadataBehavior>();
    serviceMetadataBehavior.HttpsGetEnabled = true;
});

app.Run();

In the above code, the WcfService implementation is added as a transient service using builder.Services.AddTransient<WcfService>(); so that its dependencies are injected when the service is constructed.

Sum up

In this post, we’ve looked at how to create an equivalent WCF service hosted in ASPNET Core using CoreWCF by generating the contract from a WSDL. By maintaining the same WCF contract, the integration can be swapped out with minimal changes to the consuming application.

Comments