RfcClient 1.0.1
See the version list below for details.
dotnet add package RfcClient --version 1.0.1
NuGet\Install-Package RfcClient -Version 1.0.1
<PackageReference Include="RfcClient" Version="1.0.1" />
<PackageVersion Include="RfcClient" Version="1.0.1" />
<PackageReference Include="RfcClient" />
paket add RfcClient --version 1.0.1
#r "nuget: RfcClient, 1.0.1"
#:package RfcClient@1.0.1
#addin nuget:?package=RfcClient&version=1.0.1
#tool nuget:?package=RfcClient&version=1.0.1
RfcClient
RfcClient is a DI-friendly SAP RFC client wrapper for SAP .NET Connector.
It targets .NET 10.0. The library supports multiple SAP RFC connection configs, request-scoped ConfigId switching, typed request/response mapping, and connection/invocation monitoring hooks.
Additional documentation:
Features
- Multiple SAP RFC connection configs by
ConfigId - Default config selection through
IsDefault - ASP.NET Core / generic host dependency injection support
- Request-scoped RFC session switching through
IRfcClient.ConfigId - Typed RFC input/output mapping with
[Table]and[Column] - Connection and invocation monitoring extension points
- SAP NCo runtime files included from project
libs
Installation
Install the NuGet package:
dotnet add package RfcClient --version 1.0.1
The package targets net10.0 and requires Windows x64 because the bundled SAP NCo assemblies are AMD64 binaries. Starting with version 1.0.1, the package automatically changes an unspecified or AnyCPU consumer target to x64; consuming projects do not need to add Platforms or PlatformTarget properties.
Configuration
Add RFC connection configs to appsettings.json:
{
"RfcConnectionConfigs": [
{
"ConfigId": "Sap",
"IsDefault": true,
"ConnectionString": "ApplicationServer=192.168.1.65;SystemNumber=00;SystemId=DEV;Client=800;UserName=DEVUSER;Password=******;Language=ZH;PoolSize=5;MaxPoolSize=10;ConnectionTimeout=30;CommunicationTimeout=60;"
},
{
"ConfigId": "Sap.JSY",
"IsDefault": false,
"ConnectionString": "MessageServerHost=192.168.1.65;MessageServerService=3600;SystemId=DEV;Client=800;UserName=DEVUSER;Password=******;Language=ZH;PoolSize=5;MaxPoolSize=10;"
}
]
}
Supported ConnectionString parameters map to RfcConfigParameter:
ApplicationServer
Server
SystemNumber
SystemId
Client
UserName
User ID
UserId
Password
Language
PoolSize
MaxPoolSize
Max Pool Size
ConnectionTimeout
CommunicationTimeout
MessageServerHost
MessageServerService
MessageServerPort
ApplicationServer / Server is used for direct application server connections. MessageServerHost is used for message server connections.
Register Services
Register the client with IServiceCollection. Pass the configuration explicitly, or let it auto-resolve IConfiguration from the DI container:
using mitzh;
// Option 1: pass configuration explicitly
builder.Services.AddRfcClient(builder.Configuration);
// Option 2: auto-resolve IConfiguration from the DI container
builder.Services.AddRfcClient();
If your SAP RFC settings live under a section:
builder.Services.AddRfcClient(
builder.Configuration.GetSection("Rfc"));
Programmatic registration is also supported:
using mitzh;
builder.Services.AddRfcClient(options =>
{
options.RfcConnectionConfigs.Add(new RfcConnectionConfig
{
ConfigId = "Sap",
IsDefault = true,
ConnectionString = "ApplicationServer=192.168.1.65;SystemNumber=00;Client=800;UserName=DEVUSER;Password=******;"
});
});
The public namespaces are mitzh and mitzh.Abstractions.
Autofac Module registration
RfcClient supports constructor injection and Autofac property injection. When the three dependency properties are not supplied, the built-in implementations are created lazily.
using Autofac;
using mitzh;
using mitzh.Abstractions;
public sealed class RfcModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<RfcConnectionMonitor>()
.As<IRfcConnectionMonitor>()
.SingleInstance();
builder.RegisterType<RfcConfigProvider>()
.As<IRfcConfigProvider>()
.SingleInstance();
builder.RegisterType<RfcDestinationRegistry>()
.As<IRfcDestinationRegistry>()
.SingleInstance();
builder.RegisterType<RfcClient>()
.As<IRfcClient>()
.PropertiesAutowired()
.InstancePerLifetimeScope();
}
}
RfcConfigProvider requires IOptions<RfcOptions>. Register configured options in the Autofac container, or replace ConfigProvider with a custom implementation.
Define RFC Models
Use [Table] on the request type to declare the RFC function name. Use [Column] on properties to map SAP RFC parameter names.
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
[Table("ZFM_MM039")]
public class SupplyDemandRequest
{
[Required]
[Column("IV_MATNR")]
public string MaterialCode { get; set; } = string.Empty;
[Required]
[Column("IV_BUKRS")]
public string CompanyCode { get; set; } = string.Empty;
[Column("IV_WERKS")]
public string PlantCode { get; set; } = string.Empty;
[Required]
[Column("IV_SYSTEM")]
public string Source { get; set; } = string.Empty;
}
using System.ComponentModel.DataAnnotations.Schema;
public class SupplyDemandResponse
{
[Column("EV_STATUS")]
public string Status { get; set; } = string.Empty;
[Column("EV_MESSAGE")]
public string Message { get; set; } = string.Empty;
[Column("ET_DATA")]
public SupplyDemandRow[] Rows { get; set; } = Array.Empty<SupplyDemandRow>();
}
public class SupplyDemandRow
{
[Column("MATNR")]
public string MaterialCode { get; set; } = string.Empty;
[Column("WERKS")]
public string PlantCode { get; set; } = string.Empty;
[Column("BDMNG")]
public decimal Quantity { get; set; }
}
Basic Call
Inject IRfcClient and invoke the RFC with typed request/response models:
using mitzh.Abstractions;
public class SupplyDemandService
{
private readonly IRfcClient _rfcClient;
public SupplyDemandService(IRfcClient rfcClient)
{
_rfcClient = rfcClient;
}
public SupplyDemandResponse Query()
{
var request = new SupplyDemandRequest
{
MaterialCode = "B0505XT-1WR3",
CompanyCode = "1100",
PlantCode = "",
Source = "C"
};
return _rfcClient.Invoke<SupplyDemandResponse>(request);
}
}
IRfcClient exposes a scoped ConfigId property. If ConfigId is empty, the client uses the config marked with IsDefault=true. If no config is marked as default, it uses the first item in RfcConnectionConfigs.
The current invocation API is:
TOut Invoke<TOut>(object input, string functionName = null, bool forceNew = false, string configId = null);
- For a class input, an explicit
functionNametakes precedence over its[Table]attribute. - For a dictionary input,
functionNameis required and dictionary keys are used as RFC parameter names. - Set
forceNewtotrueto bypass the cached destination. - Config selection priority is: method
configId→ instanceConfigId→ default config.
var response = _rfcClient.Invoke<SupplyDemandResponse>(
new Dictionary<string, object>
{
["IV_MATNR"] = "B0505XT-1WR3",
["IV_BUKRS"] = "1100"
},
functionName: "ZFM_MM039");
Switch ConfigId Per Request
Set IRfcClient.ConfigId inside the current request scope. After that, all IRfcClient calls in the same scope use that config automatically.
Example middleware:
using mitzh.Abstractions;
app.Use(async (context, next) =>
{
var rfcClient = context.RequestServices.GetRequiredService<IRfcClient>();
var configId = context.Request.Headers["X-Sap-Rfc-ConfigId"].FirstOrDefault();
if (!string.IsNullOrWhiteSpace(configId))
{
rfcClient.ConfigId = configId;
}
await next();
});
Example controller:
using Microsoft.AspNetCore.Mvc;
using mitzh.Abstractions;
[ApiController]
[Route("api/supply-demand")]
public class SupplyDemandController : ControllerBase
{
private readonly IRfcClient _rfcClient;
public SupplyDemandController(IRfcClient rfcClient)
{
_rfcClient = rfcClient;
}
[HttpPost]
public ActionResult<SupplyDemandResponse> Query(SupplyDemandRequest request)
{
var response = _rfcClient.Invoke<SupplyDemandResponse>(request);
return Ok(response);
}
}
Calling the API with a header switches the SAP RFC config for that request:
X-Sap-Rfc-ConfigId: Sap.JSY
Call With Explicit ConfigId
Pass configId when a config should apply only to the current invocation:
using mitzh.Abstractions;
public class ManualRfcService
{
private readonly IRfcClient _rfcClient;
public ManualRfcService(IRfcClient rfcClient)
{
_rfcClient = rfcClient;
}
public SupplyDemandResponse QueryWithJsy(SupplyDemandRequest request)
{
return _rfcClient.Invoke<SupplyDemandResponse>(request, configId: "Sap.JSY");
}
}
The method-level configId affects only the current invocation and does not change the instance ConfigId property.
Monitor Connections And Calls
Implement IRfcConnectionMonitor to observe resolved destinations and RFC invocations:
using Microsoft.Extensions.Logging;
using mitzh;
using mitzh.Abstractions;
public class LoggingRfcConnectionMonitor : IRfcConnectionMonitor
{
private readonly ILogger<LoggingRfcConnectionMonitor> _logger;
public LoggingRfcConnectionMonitor(ILogger<LoggingRfcConnectionMonitor> logger)
{
_logger = logger;
}
public void DestinationResolved(RfcDestinationResolvedContext context)
{
_logger.LogInformation(
"SAP RFC destination resolved. ConfigId={ConfigId}, ForceNew={ForceNew}, PoolSize={PoolSize}, MaxPoolSize={MaxPoolSize}",
context.ConfigId,
context.ForceNew,
context.ConfigParameter.PoolSize,
context.ConfigParameter.MaxPoolSize);
}
public void InvocationStarted(RfcInvocationContext context)
{
_logger.LogInformation(
"SAP RFC invocation started. ConfigId={ConfigId}, Function={Function}",
context.ConfigId,
context.FunctionName);
}
public void InvocationSucceeded(RfcInvocationContext context)
{
_logger.LogInformation(
"SAP RFC invocation succeeded. ConfigId={ConfigId}, Function={Function}, Elapsed={ElapsedMs}ms",
context.ConfigId,
context.FunctionName,
context.Elapsed.TotalMilliseconds);
}
public void InvocationFailed(RfcInvocationContext context, Exception exception)
{
_logger.LogError(
exception,
"SAP RFC invocation failed. ConfigId={ConfigId}, Function={Function}, Elapsed={ElapsedMs}ms",
context.ConfigId,
context.FunctionName,
context.Elapsed.TotalMilliseconds);
}
}
Register the monitor before or after AddRfcClient:
using mitzh.Abstractions;
builder.Services.AddSingleton<IRfcConnectionMonitor, LoggingRfcConnectionMonitor>();
builder.Services.AddRfcClient(builder.Configuration);
Do not log passwords or full connection strings.
Runtime Files
The project expects SAP NCo runtime files under the libs folder:
libs/cpc4n.dll
libs/ijwhost.dll
libs/sapnco.dll
libs/sapnco_utils.dll
During a source build, these files are copied to the output root directory alongside RfcClient.dll. In the NuGet package, managed AMD64 assemblies are stored under lib/net10.0/, while ijwhost.dll is stored under runtimes/win-x64/native/.
The package also contains:
buildTransitive/RfcClient.props: defaults an unspecified orAnyCPUconsumer tox64.buildTransitive/RfcClient.targets: copiesijwhost.dllto normal build and publish output directories.
Configuration options: the library exposes two timing options in RfcOptions:
CleanupInterval(default00:05:00): how often the client checks for idle destinations to cleanup.DestinationIdleTimeout(default00:10:00): how long a destination can stay unused before being removed from the cache.
You can set these in appsettings.json or via code when registering the services.
XML Documentation
The library generates an XML documentation file (RfcClient.xml) alongside the assembly in the build output. IDEs such as Visual Studio and JetBrains Rider automatically pick it up to provide inline Chinese descriptions for public types and members.
Build And Pack
dotnet build .\RfcClient.sln
dotnet pack .\RfcClient.csproj -c Release
The package is generated under:
bin/Release/RfcClient.1.0.1.nupkg
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.9)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.9)
- Microsoft.Extensions.Options (>= 10.0.9)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.9)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.12 | 87 | 8/5/2026 |
| 1.0.11 | 78 | 7/24/2026 |
| 1.0.10 | 74 | 7/24/2026 |
| 1.0.9 | 77 | 7/22/2026 |
| 1.0.8 | 83 | 7/21/2026 |
| 1.0.7 | 79 | 7/20/2026 |
| 1.0.6 | 75 | 7/15/2026 |
| 1.0.5 | 81 | 7/15/2026 |
| 1.0.4 | 73 | 7/15/2026 |
| 1.0.3 | 81 | 7/14/2026 |
| 1.0.2 | 84 | 7/13/2026 |
| 1.0.1 | 75 | 7/13/2026 |
| 1.0.0 | 88 | 7/11/2026 |
| 0.1.1 | 89 | 7/9/2026 |
| 0.1.0 | 84 | 7/9/2026 |