[c#] .Net Core에서 WCF를 대체하는 것은 무엇입니까?
.Net Framework 콘솔 응용 프로그램을 만들고 Add(int x, int y)
클래스 라이브러리 (.Net Framework)를 사용하여 처음부터 WCF 서비스를 통해 함수를 노출하는 데 익숙합니다 . 그런 다음 콘솔 응용 프로그램을 사용하여 서버 내에서이 함수를 프록시 호출합니다.
그러나 콘솔 앱 (.Net Core) 및 클래스 라이브러리 (.Net Core)를 사용하는 경우 System.ServiceModel을 사용할 수 없습니다. 인터넷 검색을 수행했지만이 인스턴스에서 WCF를 “대체”하는 것이 무엇인지 파악하지 못했습니다.
Add(int x, int y)
.Net Core 내의 콘솔 애플리케이션에 클래스 라이브러리 내의 함수를 어떻게 노출 합니까? System.ServiceModel.Web이 표시되고 이것이 교차 플랫폼이 되려고하므로 RESTful 서비스를 만들어야합니까?
답변
WCF는 Windows 특정 기술이고 .NET Core는 크로스 플랫폼이어야하므로 .NET Core에서 지원되지 않습니다.
프로세스 간 통신을 구현하는 경우 IpcServiceFramework 프로젝트를 시도해보십시오 .
다음과 같이 WCF 스타일로 서비스를 만들 수 있습니다.
-
서비스 계약 생성
public interface IComputingService { float AddFloat(float x, float y); }
-
서비스 구현
class ComputingService : IComputingService { public float AddFloat(float x, float y) { return x + y; } }
-
콘솔 애플리케이션에서 서비스 호스팅
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>(); } }
-
클라이언트 프로세스에서 서비스 호출
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));
답변
.NET 핵심 애플리케이션 내에서 웹 서비스를 호스팅하는 데 gRPC를 사용할 수 있습니다.
소개
- gRPC는 Google에서 처음 개발 한 고성능 오픈 소스 RPC 프레임 워크입니다.
- 프레임 워크는 원격 프로 시저 호출의 클라이언트-서버 모델을 기반으로합니다. 클라이언트 응용 프로그램은 마치 로컬 개체 인 것처럼 서버 응용 프로그램의 메서드를 직접 호출 할 수 있습니다.
예
서버 코드
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();
}
}
클라이언트 코드
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();
}
}
클라이언트와 서버 간의 공유 클래스
[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; }
}
서비스 설명자
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));
}
시리얼 라이저 / 디시리얼라이저
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;
}
}
산출
참고 문헌
- https://blogs.msdn.microsoft.com/dotnet/2018/12/04/announcing-net-core-3-preview-1-and-open-sourcing-windows-desktop-frameworks/
- https://grpc.io/docs/
- https://grpc.io/docs/quickstart/csharp.html
- https://github.com/grpc/grpc/tree/master/src/csharp
벤치 마크
답변
Microsoft 지원을 통해 .NET Foundation에서 유지 관리 하는 CoreWCF 프로젝트 가있을 것 같습니다 .
.NET Foundation에 Core WCF 환영 에서 자세한 내용
처음에는 netTcp 및 http 전송 만 구현됩니다.
답변
WCF는 많은 일을합니다. 명명 된 파이프를 사용하여 한 시스템에서 두 응용 프로그램 (프로세스) 간의 원격 프로 시저 호출을 쉽게 수행 할 수 있습니다. TCPIP를 통한 이진 직렬화를 사용하는 .NET 구성 요소 간의 대용량 내부 클라이언트-서버 통신 채널이 될 수 있습니다. 또는 SOAP를 통해 표준화 된 교차 기술 API를 제공 할 수 있습니다. MSMQ를 통한 비동기 메시징과 같은 기능도 지원합니다.
.NET Core의 경우 목적에 따라 다른 대체 항목이 있습니다.
크로스 플랫폼 API의 경우이를 ASP.NET을 사용하는 REST 서비스로 대체합니다.
프로세스 간 연결 또는 클라이언트-서버 연결의 경우 gRPC가 좋으며 @Gopi가 제공하는 훌륭한 대답이 있습니다.
따라서 “WCF를 대체하는 항목”에 대한 대답은 사용 용도에 따라 다릅니다.
답변
WCF의 일부를 구현 하는 커뮤니티 저장소 https://github.com/CoreWCF/CoreWCF 가 있습니다. 몇 가지 간단한 WCF 서비스를 지원하는 데 사용할 수 있습니다. 그러나 모든 기능이 지원되는 것은 아닙니다.
답변
따라서 내 연구에서 최고의 솔루션에는 자동 생성 프록시 클래스가 없습니다. 이 최상의 솔루션은 RESTful 서비스를 만들고 응답 본문을 모델 개체로 직렬화하는 것입니다. 모델은 MVC 디자인 패턴에서 발견되는 일반적인 모델 개체입니다.
귀하의 답변에 감사드립니다
답변
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}!";
}
}