140

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?

ekad
  • 14,436
  • 26
  • 44
  • 46
Sigex
  • 2,834
  • 2
  • 24
  • 25
  • `do I have to create a RESTful service?` - AFAIK yes (or use some 3rd-Party solution I would not know any for .NET Core) – Christoph Fink Jan 30 '18 at 13:43
  • 4
    WCF won't likely be ported to .NET Core, because most of the code-base depends on Windows internal libraries. Can you use ASP.NET Core? There you'll have a HTTP server that's readily cross-platform – Camilo Terevinto Jan 30 '18 at 13:45
  • 2
    WCF client-side is already supported (I don't know how much), server-side is a hotly debated and votred feature request. – H H Mar 31 '18 at 21:48
  • It appears Visual Studio 2017 15.5 and later support [generating .NET Core client proxy classes](https://learn.microsoft.com/en-us/dotnet/core/additional-tools/wcf-web-service-reference-guide) . There is also a list of [supported features](https://github.com/dotnet/wcf/blob/master/release-notes/SupportedFeatures-v2.0.0.md). – jamiebarrow May 02 '18 at 09:21
  • There is a port for .NET Core: https://github.com/dotnet/wcf – Flupp Sep 07 '18 at 06:59
  • 7
    In short : [CoreWCF](https://github.com/CoreWCF/CoreWCF) – Ognyan Dimitrov Nov 22 '19 at 13:36
  • https://www.mobilize.net/blog/can-i-use-wcf-in-.net-core-3.1-or-.net5 – orellabac Jan 15 '21 at 18:23

14 Answers14

86

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
Gopi
  • 1,311
  • 1
  • 10
  • 8
  • 8
    As of March 2019, this answer is more relevant. See https://github.com/grpc/grpc-dotnet (and [ASP.NET Core updates in .NET Core 3.0](https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-core-3-0-preview-3/)). – resnyanskiy Mar 14 '19 at 09:20
  • 2
    I think this is the closest answer but still, sadly , it doesn't provide any behavior or throttling support. – joe Mar 26 '19 at 06:33
  • 4
    Be also aware that as of now `gRPC` does not compile against the .net native tool chain in VS 2019 (16.0.2) and therefore won't work with UWP. – Samuel May 15 '19 at 13:50
  • 2
    If you're looking for named pipe support, I wrote a gRPC transport: https://github.com/cyanfish/grpc-dotnet-namedpipes – Cyanfish Jan 25 '20 at 01:05
  • 1
    Note that (as of 2020-04-06) grpc-dotnet doesn't have packages for ARM. – GafferMan2112 Apr 06 '20 at 20:59
  • Also note, that it makes use of the HTTP/2 protocol. https://blog.netsil.com/http-2-and-grpc-the-next-generation-of-microservices-interactions-aff4ffa6faed – Latency Jul 19 '20 at 16:23
40

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));
    
KyleMit
  • 30,350
  • 66
  • 462
  • 664
Jacques Kang
  • 728
  • 7
  • 3
  • 3
    Nice! Might be worth updating to take advantage of the .Net core system.io.pipelines https://blogs.msdn.microsoft.com/dotnet/2018/07/09/system-io-pipelines-high-performance-io-in-net/ – Sigex Jul 16 '18 at 15:51
  • Sorry am i missing something important here? Isn't pipes supposed to be only for same host communications? – user1034912 Nov 01 '18 at 00:20
  • 3
    Yes, what you're missing is that this briefly demonstrates that IpcServiceFramework, like WCF, allows you to switch kind-of-seamlessly between different messaging technologies. – Chris F Carroll Apr 02 '19 at 13:13
  • 4
    WCF might be considered windows specific in some of the protocols that it abstracts, but SOAP services are not. How would one create a SOAP web service in .net core? – Jeremy Jul 26 '19 at 23:19
  • 2
    WCF is not a "windows specific technology" but rather, a .NET way of implementing the interoperable SOAP/WSDL stack. A web framework that doesn't support it has no easy way to connect to thousand of already implemented services. Definitely something that has to be addressed in future .NET core. – Wiktor Zychla Oct 08 '19 at 16:10
  • 3
    Note: the author of this project wrote the following comment: "Guys, for personal reason I do have no time to maintain this project since a few months. Meantime .NET Core 3.0 is released with gRPC feature." (https://github.com/jacqueskang/IpcServiceFramework/issues/84#issuecomment-556247986). See the second answer for gRPC. – gerard Jan 08 '20 at 11:19
  • 1
    "WCF is not supported in .NET Core since it's a Windows specific technology while .NET Core is supposed to be cross-platform." And then, there's WPF. :( – Bruno Brant May 27 '20 at 15:50
  • Why did you decide to use a Lambda for invoke rather than DispatchProxy or even IL Emit? You could construct a transparent Proxy object with either of these options. Since you are going over network the reflection perf hits would be swamped by the latency anyway. – Aron Jun 12 '20 at 10:27
  • The client part if supported up to certain extend: https://www.mobilize.net/blog/can-i-use-wcf-in-.net-core-3.1-or-.net5 – orellabac Jan 15 '21 at 18:23
36

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.

KyleMit
  • 30,350
  • 66
  • 462
  • 664
Rafał Straszewski
  • 960
  • 11
  • 10
  • This is a misleading answer. Microsoft has only ported the wcf client. Wcf host or servicehost is not available and they have no intention to do so. I learned this the hard way. gRPC is the way to go – user1034912 Oct 30 '20 at 20:31
  • 1
    @user1034912 you are not correct. CoreWCF is lightweight WCF server which is ported to .NET core. It has limitations, but for some cases it is a good choice. – Access Denied Nov 05 '20 at 01:54
  • Yes, only if you are a Consuming Client, there is no servicehost implementation – user1034912 Nov 05 '20 at 06:48
  • @user1034912 No, server side is available. https://github.com/CoreWCF/CoreWCF/blob/master/src/Samples/NetCoreServer/Startup.cs – Access Denied Nov 05 '20 at 08:13
  • https://github.com/CoreWCF/CoreWCF/blob/master/src/Samples/DesktopServer/Program.cs and https://github.com/CoreWCF/CoreWCF/blob/master/src/Samples/NetCoreServer/Program.cs – Access Denied Nov 05 '20 at 08:22
  • Wow this is great, didn't knew about this. Thanks, It however doesn't support WebHttpBinding – user1034912 Nov 05 '20 at 08:38
  • to add to this, at BingAds, we leveraged CoreWCF library to serve billions of requests. So, pretty stable now for port. – AAATechGuy Jul 24 '22 at 04:57
16

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.

Sly Gryphon
  • 3,751
  • 1
  • 25
  • 17
7

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.

orellabac
  • 2,077
  • 2
  • 26
  • 34
5

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.

yunus
  • 83
  • 2
  • 9
4

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

Sigex
  • 2,834
  • 2
  • 24
  • 25
2

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://learn.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://learn.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}!";
    }
}
DoronG
  • 2,576
  • 16
  • 22
1

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

Flupp
  • 856
  • 10
  • 24
  • 14
    I believe this port is for communication from Core to WCF but not for writing WCF in Core. – hal9000 Sep 12 '18 at 17:11
  • 7
    The linked github repository clearly says: " This repo contains the client-oriented WCF libraries that enable applications built on .NET Core to communicate with WCF services. " – Bahaa Dec 04 '18 at 14:30
0

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.

AbbathCL
  • 79
  • 6
0
// 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
Astro
  • 9
  • 1
  • 4
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jan 24 '22 at 04:36
0

https://devblogs.microsoft.com/dotnet/corewcf-v1-released/

April 2022, WCF is available on the following .NET core versions .NET Core 3.1 .NET 5 & 6

Kapoor
  • 1,388
  • 11
  • 21
0

I have found the ServiceWire package to be an excellent replacement for NamedPipes and WCF, especially if you do not need to queue requests.

James Westgate
  • 11,306
  • 8
  • 61
  • 68
0

As already mentioned by others, gRPC is a good alternative and is Microsoft's current recommendation. There is a considerable amount of documentation available directly from Microsoft.

However, if all endpoints are .NET applications, gRPC may be unnecessary heavy (not at runtime, but implementation wise). I was missing a really simple solution and ended up creating something myself, called NetMessage. Bear in mind that I'm the author of that library (so my recommendation may be biased).

It supports custom C# classes as communication objects, ensures type safety and enforces request/response contracts. This should give an idea how it works (for more details, see the Readme on GitHub):

// request packet
public class WeatherRequest : IRequest<WeatherResponse>
{
  public string City { get; set; }
  public DateTime Date { get; set; }
}

// corresponding response packet
public class WeatherResponse
{
  public string Forecast { get; set; }
}

// and this is how to use it (compiler enforces correct types):
var response = await client.SendRequestAsync(new WeatherRequest { City = "Bonn" });
Console.WriteLine(response.Forecast);
Till F.
  • 189
  • 1
  • 9