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
You can use gRPC for hosting web services inside .NET core application.
Introduction
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
Benchmarks
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}!";
}
}
There is a .NET Core port available: https://github.com/dotnet/wcf It's still in preview, but they are actively developing it.
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:
Create service contract
public interface IComputingService
{
float AddFloat(float x, float y);
}
Implement the service
class ComputingService : IComputingService
{
public float AddFloat(float x, float y)
{
return x + y;
}
}
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>();
}
}
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));
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.
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