What replaces WCF in .Net Core?

后端 未结 9 463
逝去的感伤
逝去的感伤 2020-11-30 19:27

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 Fra

相关标签:
9条回答
  • 2020-11-30 19:30

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

    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
    0 讨论(0)
  • 2020-11-30 19:35

    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}!";
        }
    }
    
    0 讨论(0)
  • 2020-11-30 19:36

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

    0 讨论(0)
  • 2020-11-30 19:38

    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));
      
    0 讨论(0)
  • 2020-11-30 19:38

    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.

    0 讨论(0)
  • 2020-11-30 19:44

    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

    0 讨论(0)
提交回复
热议问题