What replaces WCF in .Net Core?

C#.NetWcfService.Net Core

C# Problem Overview


I am used to creating a .Net Framework console application and exposing a Add(int x, int y) function via a WCF service from scratch with Class Library (.Net Framework). I then use the console application to proxy call this function within the server.

However if I use Console App (.Net Core) and a Class Library (.Net Core) the System.ServiceModel is not available. I have done some Googling but I haven't figured out what "replaces" WCF in this instance.

How do I expose a Add(int x, int y) function within a class library to a console application all within .Net Core? I see System.ServiceModel.Web, and since this is trying to be cross platform do I have to create a RESTful service?

C# Solutions


Solution 1 - C#

You can use gRPC for hosting web services inside .NET core application.

enter image description here

Introduction

  1. gRPC is a high performance, open source RPC framework initially developed by Google.
  2. The framework is based on a client-server model of remote procedure calls. A client application can directly call methods on a server application as if it was a local object.

Example

Server Code

class Program
{
    static void Main(string[] args)
    {
        RunAsync().Wait();
    }

    private static async Task RunAsync()
    {
        var server = new Grpc.Core.Server
        {
            Ports = { { "127.0.0.1", 5000, ServerCredentials.Insecure } },
            Services =
            {
                ServerServiceDefinition.CreateBuilder()
                    .AddMethod(Descriptors.Method, async (requestStream, responseStream, context) =>
                    {
                        await requestStream.ForEachAsync(async additionRequest =>
                        {
                            Console.WriteLine($"Recieved addition request, number1 = {additionRequest.X} --- number2 = {additionRequest.Y}");
                            await responseStream.WriteAsync(new AdditionResponse {Output = additionRequest.X + additionRequest.Y});
                        });
                    })
                    .Build()
            }
        };

        server.Start();

        Console.WriteLine($"Server started under [127.0.0.1:5000]. Press Enter to stop it...");
        Console.ReadLine();

        await server.ShutdownAsync();
    }
}

Client Code

class Program
{
    static void Main(string[] args)
    {
        RunAsync().Wait();
    }

    private static async Task RunAsync()
    {
        var channel = new Channel("127.0.0.1", 5000, ChannelCredentials.Insecure);
        var invoker = new DefaultCallInvoker(channel);
        using (var call = invoker.AsyncDuplexStreamingCall(Descriptors.Method, null, new CallOptions{}))
        {
            var responseCompleted = call.ResponseStream
                .ForEachAsync(async response => 
                {
                    Console.WriteLine($"Output: {response.Output}");
                });
            
            await call.RequestStream.WriteAsync(new AdditionRequest { X = 1, Y = 2});
            Console.ReadLine();

            await call.RequestStream.CompleteAsync();
            await responseCompleted;
        }

        Console.WriteLine("Press enter to stop...");
        Console.ReadLine();

        await channel.ShutdownAsync();
    }
}

Shared Classes between Client and Server

[Schema]
public class AdditionRequest
{
    [Id(0)]
    public int X { get; set; }
    [Id(1)]
    public int Y { get; set; }
}

[Schema]
public class AdditionResponse
{
    [Id(0)]
    public int Output { get; set; }
}

Service descriptors

using Grpc.Core;
public class Descriptors
{
    public static Method<AdditionRequest, AdditionResponse> Method =
            new Method<AdditionRequest, AdditionResponse>(
                type: MethodType.DuplexStreaming,
                serviceName: "AdditonService",
                name: "AdditionMethod",
                requestMarshaller: Marshallers.Create(
                    serializer: Serializer<AdditionRequest>.ToBytes,
                    deserializer: Serializer<AdditionRequest>.FromBytes),
                responseMarshaller: Marshallers.Create(
                    serializer: Serializer<AdditionResponse>.ToBytes,
                    deserializer: Serializer<AdditionResponse>.FromBytes));
}

Serializer/Deserializer

public static class Serializer<T>
{
    public static byte[] ToBytes(T obj)
    {
        var buffer = new OutputBuffer();
        var writer = new FastBinaryWriter<OutputBuffer>(buffer);
        Serialize.To(writer, obj);
        var output = new byte[buffer.Data.Count];
        Array.Copy(buffer.Data.Array, 0, output, 0, (int)buffer.Position);
        return output;
    }

    public static T FromBytes(byte[] bytes)
    {
        var buffer = new InputBuffer(bytes);
        var data = Deserialize<T>.From(new FastBinaryReader<InputBuffer>(buffer));
        return data;
    }
}

Output

Sample client output

Sample Server output

References

  1. https://blogs.msdn.microsoft.com/dotnet/2018/12/04/announcing-net-core-3-preview-1-and-open-sourcing-windows-desktop-frameworks/
  2. https://grpc.io/docs/
  3. https://grpc.io/docs/quickstart/csharp.html
  4. https://github.com/grpc/grpc/tree/master/src/csharp

Benchmarks

  1. http://csharptest.net/787/benchmarking-wcf-compared-to-rpclibrary/index.html

Solution 2 - C#

WCF is not supported in .NET Core since it's a Windows specific technology and .NET Core is supposed to be cross-platform.

If you are implementing inter-process communication consider trying the IpcServiceFramework project.

It allows creating services in WCF style like this:

  1. Create service contract

    public interface IComputingService
    {
        float AddFloat(float x, float y);
    }
    
  2. Implement the service

    class ComputingService : IComputingService
    {
        public float AddFloat(float x, float y)
        {
            return x + y;
        }
    }
    
  3. Host the service in Console application

    class Program
    {
        static void Main(string[] args)
        {
            // configure DI
            IServiceCollection services = ConfigureServices(new ServiceCollection());
    
            // build and run service host
            new IpcServiceHostBuilder(services.BuildServiceProvider())
                .AddNamedPipeEndpoint<IComputingService>(name: "endpoint1", pipeName: "pipeName")
                .AddTcpEndpoint<IComputingService>(name: "endpoint2", ipEndpoint: IPAddress.Loopback, port: 45684)
                .Build()
                .Run();
        }
    
        private static IServiceCollection ConfigureServices(IServiceCollection services)
        {
            return services
                .AddIpc()
                .AddNamedPipe(options =>
                {
                    options.ThreadCount = 2;
                })
                .AddService<IComputingService, ComputingService>();
        }
    }
    
  4. Invoke the service from client process

    IpcServiceClient<IComputingService> client = new IpcServiceClientBuilder<IComputingService>()
        .UseNamedPipe("pipeName") // or .UseTcp(IPAddress.Loopback, 45684) to invoke using TCP
        .Build();
    
    float result = await client.InvokeAsync(x => x.AddFloat(1.23f, 4.56f));
    

Solution 3 - C#

It seems, that there will be a CoreWCF project maintained by .NET Foundation with Microsoft support.

More details at Welcoming Core WCF to the .NET Foundation

Initially only netTcp and http transport will be implemented.

Solution 4 - C#

WCF does many things; it is an easy way to remote procedure calls between two applications (processes) on one machine, using named pipes; it can be a high volume internal client-server communication channel between .NET components, using binary serialization over TCPIP; or it can provide a standardised cross-technology API, e.g. via SOAP. It even has support for things like asynchronous messaging, via MSMQ.

For .NET Core, there are different replacements based on the purpose.

For cross-platform API, you would replace this with a REST service using ASP.NET.

For inter-process connections, or client-server connection, gRPC would be good, with an excellent answer given by @Gopi.

So the answer to "What replaces WCF" depends on what you are using it for.

Solution 5 - C#

There is a community repo https://github.com/CoreWCF/CoreWCF that implements some parts of WCF. You can use it to support some simple WCF services. However not all features are supported.

Solution 6 - C#

> What's new in .NET 5 / Windows Communication Foundation

The original implementation of Windows Communication Foundation (WCF) was only supported on Windows. However, there is a client port available from the .NET Foundation. It is entirely open source, cross platform, and supported by Microsoft.

The community maintains the server components that complement the aforementioned client libraries. The GitHub repository can be found at CoreWCF. The server components are not officially supported by Microsoft. For an alternative to WCF, consider gRPC.

Solution 7 - C#

So from my research the best solution does not have the auto-generated proxy classes. This best solution is to create a RESTful service and to serialise the response body into model objects. Where the models are the usual model objects found in the MVC design pattern.

Thank you for your responses

Solution 8 - C#

You can also self-host ASP.NET Core Web API.

<!-- SelfHosted.csproj -->
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <!-- see: https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.1&tabs=visual-studio#framework-reference -->
    <FrameworkReference Include="Microsoft.AspNetCore.App" />
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.0" />
  </ItemGroup>

</Project>
// Program.cs
using System.IO;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;

namespace SelfHosted
{
    class Program
    {
        static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args)
        {
            // see: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-3.1
            return Host.CreateDefaultBuilder(args)
                .ConfigureHostConfiguration(configHost =>
                {
                    configHost.SetBasePath(Directory.GetCurrentDirectory());
                    configHost.AddJsonFile("appsettings.json", optional: true);
                    configHost.AddEnvironmentVariables(prefix: "SelfHosted_");
                    configHost.AddCommandLine(args);
                })
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.CaptureStartupErrors(true);
                    webBuilder.UseStartup<Startup>();
                });
        }
    }
}
// Startup.cs
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace SelfHosted
{
    public class Startup
    {
        public Startup(IConfiguration configuration, IWebHostEnvironment env)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            // see: https://github.com/aspnet/AspNetCore.Docs/tree/master/aspnetcore/web-api/index/samples/3.x
            services.AddControllers();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}
// Controllers\TestController.cs
using System.Net.Mime;
using Microsoft.AspNetCore.Mvc;

namespace SelfHosted.Controllers
{
    [ApiController]
    [Produces(MediaTypeNames.Application.Json)]
    [Route("[controller]")]
    public class HelloController : SelfHostedControllerBase
    {
        [HttpGet]
        public ActionResult<string> HelloWorld() => "Hello World!";

        [HttpGet("{name}")]
        public ActionResult<string> HelloName(string name) => $"Hello {name}!";
    }
}

Solution 9 - C#

There is a .NET Core port available: https://github.com/dotnet/wcf It's still in preview, but they are actively developing it.

Solution 10 - C#

As today all the WCFCore selfhost Available are not that easy to install and use.
The best for HostedService it will be the alternatives as gRPC showed in the previous answer and notice that in 1 year can change many things sure WCF is supported in Core only as a client that works fine.

Solution 11 - C#

// I found a way to implement WCF client proxy in .Net 6.0 (Core):


//--------------------------------------WCF duplex fix------------------------------
// I found a way to fix my problem.. it took me a week of research
// So here it is:
// How to generate WCF Service (.Net Framework 4.8) proxy in client (.Net 6.0):

// If using a callback I need duplex communication

    [ServiceContract(CallbackContract = typeof(IEventsService))]

// Just do as explain here but dont expect it to work for Client .Net 6.0 it will
// only work for client .net Framework 4.8 as Wcf service is .Net Framework 4.8

https://www.codeproject.com/articles/663333/understanding-events-in-wcf#:~:text=Background%20While%20events%20in%20WCF%20are%20nothing%20more,typical%20relationship%20between%20a%20client%20and%20a%20service.

// so here is what I need to do to make it work in .Net 6.0 client:
// Use netHttpBinding for duplex
// Put this on web.config of your Wcf service

    <service name="HomeManagerWcfService.HomeManagerService" behaviorConfiguration="HomeManagerServiceBehavior">
        <host>
            <baseAddresses>
            <add baseAddress="http://localhost:53318/HomeManagerService"/>
            </baseAddresses>
        </host>
    
    <endpoint address="" binding="netHttpBinding" contract="HomeManagerWcfService.IHomeManagerService"/>
    <!--<endpoint address="" binding="wsDualHttpBinding" contract="HomeManagerWcfService.IHomeManagerService"/>-->
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
    </service>
    </services>
    
    <behaviors>
        <serviceBehaviors>
        <!-- HomeManagerService Behavior -->
        <behavior name="HomeManagerServiceBehavior">
            <serviceMetadata httpGetEnabled="true"/>
            <serviceDebug includeExceptionDetailInFaults="true "/>
        </behavior>
        </serviceBehaviors>
    </behaviors>

// Generate files for your client proxy on VisualStudio.Tools.Command line.Developer command prompt
// The WCF service must be running

    svcutil http://localhost:53318/HomeManagerService.svc

//copy both files generated in your client project.
// if using the VS UI generator (Managed connected service) it won't work, there is a bug in it I guess.

// I also need System.ServiceModel.Http
// & I need System.ServiceModel.Duplex
// in the client
// use NetHttpBinding for duplex communication
// Use IHomeManagerServiceCallback to implement the callback function 
// where you want it to run the callback

    InstanceContext iCntxt = new InstanceContext(this);// "this" is where i implement the callback for my case
    var endpointAddress = new EndpointAddress("http://localhost:53318/HomeManagerService.svc");
    var binding = new NetHttpBinding();
    var factory = new DuplexChannelFactory<IHomeManagerService>(iCntxt, binding, endpointAddress);
    var clientWCF = factory.CreateChannel();
    
    EmailMessage emailMessage = new EmailMessage();
    emailMessage.Name = "ww23";
    emailMessage.Body = "SSWDDW223";
    emailMessage.EmailAddress = "EMAIL AD dsf2223";
    
    clientWCF.SubscribeCalculatedEvent(); // where we register to the event on the server
    clientWCF.AddEmail(emailMessage);   // the callback event call is in this function on the server

//----------------------------------------------------------------------------------

// for IIS 
// In order to make sure this mapping appears you need to go to control panel
    -> turn windows features on or off
    -> .NET Framework 4.8 Advanced Services
    -> WCF Services -> HTTP Activation

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSigexView Question on Stackoverflow
Solution 1 - C#GopiView Answer on Stackoverflow
Solution 2 - C#Jacques KangView Answer on Stackoverflow
Solution 3 - C#Rafał StraszewskiView Answer on Stackoverflow
Solution 4 - C#Sly GryphonView Answer on Stackoverflow
Solution 5 - C#orellabacView Answer on Stackoverflow
Solution 6 - C#yunusView Answer on Stackoverflow
Solution 7 - C#SigexView Answer on Stackoverflow
Solution 8 - C#DoronGView Answer on Stackoverflow
Solution 9 - C#FluppView Answer on Stackoverflow
Solution 10 - C#AbbathCLView Answer on Stackoverflow
Solution 11 - C#AstroView Answer on Stackoverflow