This guide provides comprehensive instructions and patterns for migrating .NET Framework 4.8 Remoting applications to CoreRemoting, enabling cross-platform compatibility and modern .NET support.
- Overview
- Migration Approaches
- Step-by-Step Migration Guide
- Configuration Migration
- Code Migration Patterns
- Advanced Migration Scenarios
- Best Practices
- Troubleshooting
- Cross-Platform Support: Run on Windows, Linux, and macOS
- Modern .NET Support: Compatible with .NET Core 3.1+, .NET 5/6/7/8, and .NET Standard 2.0
- Enhanced Security: Built-in encryption and modern authentication providers
- Multiple Transport Options: TCP, WebSocket, NamedPipe, and QUIC channels
- Flexible Serialization: JSON-based (BSON), binary, and custom serializers
- Dependency Injection Integration: Native support for DI containers
CoreRemoting provides a ClassicRemotingApi compatibility layer that allows drop-in replacement for most .NET Remoting scenarios with minimal code changes.
The ClassicRemotingApi provides a drop-in replacement with minimal code changes:
// Before (.NET Remoting)
using System.Runtime.Remoting;
RemotingConfiguration.Configure(configFilePath, ensureSecurity: true);
// After (CoreRemoting)
using CoreRemoting.ClassicRemotingApi;
RemotingConfiguration.Configure(configFilePath);For new implementations or comprehensive migrations:
// Server-side
using var server = new RemotingServer(new ServerConfig()
{
HostName = "localhost",
NetworkPort = 9090,
RegisterServicesAction = container =>
{
container.RegisterService<IService, ServiceImplementation>(ServiceLifetime.Singleton);
}
});
server.Start();
// Client-side
using var client = new RemotingClient(new ClientConfig()
{
ServerHostName = "localhost",
ServerPort = 9090
});
client.Connect();
var proxy = client.CreateProxy<IService>();This guide walks through migrating a complete .NET Remoting application using the provided example. You can find the original .NET Remoting example and the migrated example here: Migration Example Code
TaskDemoAppNetRemoting/
├── TaskDemoAppNetRemoting.Server/
├── TaskDemoAppNetRemoting.Client/
└── TaskDemoAppNetRemoting.Shared/
- Create new solution with modern project structure
- Target frameworks: Use .NET Standard 2.0 for shared libraries, .NET 8.0 for applications
- Add CoreRemoting NuGet packages to server and client projects
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CoreRemoting" Version="*" />
<PackageReference Include="CoreRemoting.ClassicRemotingApi" Version="*" />
</ItemGroup>
</Project>Original Server Config (App.config):
<configuration>
<system.runtime.remoting>
<application name="TaskDemoAppNetRemoting">
<service>
<wellknown mode="Singleton"
type="TaskDemoAppNetRemoting.Server.TodoService, TaskDemoAppNetRemoting.Server"
objectUri="TodoService"/>
</service>
<channels>
<channel ref="tcp" name="TaskDemoAppNetRemoting.Server" port="9090" secure="true"/>
</channels>
</application>
</system.runtime.remoting>
</configuration>Migrated Server Config (App.config):
<configuration>
<configSections>
<section name="coreRemoting"
type="CoreRemoting.ClassicRemotingApi.ConfigSection.CoreRemotingConfigSection, CoreRemoting"/>
</configSections>
<coreRemoting>
<serverInstances>
<add uniqueInstanceName="TaskServer" networkPort="8080"
serializer="binary" channel="ws"/>
</serverInstances>
<services>
<add serviceName="TodoService"
interfaceAssemblyName="MigratedTaskDemoAppNetRemoting.Shared"
interfaceTypeName="MigratedTaskDemoAppNetRemoting.Shared.ITodoService"
implementationAssemblyName="MigratedTaskDemoAppNetRemoting.Server"
implementationTypeName="MigratedTaskDemoAppNetRemoting.Server.TodoService"
lifetime="Singleton"
uniqueServerInstanceName="TaskServer"/>
</services>
</coreRemoting>
</configuration>Original Client Config (App.config):
<configuration>
<appSettings>
<add key="serverUrl" value="tcp://localhost:9090" />
</appSettings>
<system.runtime.remoting>
<application name="TaskDemoAppNetRemoting.Client">
<channels>
<channel ref="tcp" secure="true" />
</channels>
</application>
</system.runtime.remoting>
</configuration>Migrated Client Config (App.config):
<configuration>
<configSections>
<section name="coreRemoting"
type="CoreRemoting.ClassicRemotingApi.ConfigSection.CoreRemotingConfigSection, CoreRemoting"/>
</configSections>
<coreRemoting>
<clientInstances>
<add uniqueInstanceName="DefaultClient"
serverHostName="localhost" serverPort="8080"
serializer="binary" isDefault="true" channel="ws"/>
</clientInstances>
</coreRemoting>
</configuration>Original Server Program:
using System;
using System.Configuration;
using System.Runtime.Remoting;
namespace TaskDemoAppNetRemoting.Server
{
internal class Program
{
public static void Main(string[] args)
{
var configFilePath = ConfigurationManager
.OpenExeConfiguration(ConfigurationUserLevel.None).FilePath;
RemotingConfiguration.Configure(configFilePath, ensureSecurity: true);
Console.WriteLine("Server running (Press [Enter] to quit)");
Console.ReadLine();
}
}
}Migrated Server Program:
using System;
using System.Configuration;
using CoreRemoting.ClassicRemotingApi; // Changed namespace
namespace MigratedTaskDemoAppNetRemoting.Server
{
internal class Program
{
public static void Main(string[] args)
{
var configFilePath = ConfigurationManager
.OpenExeConfiguration(ConfigurationUserLevel.None).FilePath;
RemotingConfiguration.Configure(configFilePath); // Removed ensureSecurity parameter
Console.WriteLine("Server running (Press [Enter] to quit)");
Console.ReadLine();
}
}
}Original Client ServiceProxyHelper:
using System;
using System.Configuration;
using TaskDemoAppNetRemoting.Shared;
namespace TaskDemoAppNetRemoting.Client
{
public static class ServiceProxyHelper
{
private static string _serverUrl;
public static string ServerUrl
{
get
{
if (string.IsNullOrWhiteSpace(_serverUrl))
_serverUrl = ConfigurationManager.AppSettings.Get("serverUrl");
return _serverUrl;
}
}
public static ITodoService GetTaskServiceProxy()
{
return (ITodoService)Activator.GetObject(typeof(ITodoService),
ServerUrl + "/TodoService");
}
}
}Migrated Client ServiceProxyHelper:
using MigratedTaskDemoAppNetRemoting.Shared;
using CoreRemoting.ClassicRemotingApi; // Added namespace
namespace MigratedTaskDemoAppNetRemoting.Client
{
public static class ServiceProxyHelper
{
public static ITodoService GetTaskServiceProxy()
{
// Create proxy using CoreRemoting.ClassicRemotingApi
return (ITodoService)RemotingServices.Connect(
interfaceType: typeof(ITodoService),
serviceName: "TodoService");
// Original code:
// return (ITodoService)Activator.GetObject(typeof(ITodoService),
// ServerUrl + "/TodoService");
}
}
}The service interfaces typically require minimal changes:
using System;
using System.Collections.Generic;
namespace MigratedTaskDemoAppNetRemoting.Shared
{
public interface ITodoService
{
List<Todo> GetTodoList();
Todo SaveTodo(Todo item);
void DeleteTodo(Guid id);
}
}Note: The Todo data class and service implementation usually require no changes.
| .NET Remoting Setting | CoreRemoting Equivalent |
|---|---|
<system.runtime.remoting> |
<coreRemoting> |
<wellknown mode="Singleton"> |
lifetime="Singleton" |
objectUri="TodoService" |
serviceName="TodoService" |
<channel ref="tcp" port="9090"> |
networkPort="9090" channel="tcp" |
| .NET Remoting Setting | CoreRemoting Equivalent |
|---|---|
<appSettings> server URL |
<clientInstances> server configuration |
Activator.GetObject() |
RemotingServices.Connect() |
| Custom channel setup | Built-in channel selection |
- Default: BSON (JSON-based, widely compatible)
- Binary: Set
serializer="binary"for binary serialization (Depreated! Needs additional CoreRemoting.Serialization.Binary nuget package) - NeoBinary: Use
serializer="neobinary"for modern binary format
| Channel | Config Value | Use Case |
|---|---|---|
| TCP | channel="tcp" |
Default, reliable TCP connection |
| WebSocket | channel="ws" |
Modern standard |
| NamedPipe | channel="namedpipe" |
Inter-process communication |
| QUIC | channel="quic" |
Modern protocol (.NET 9.0 only; Windows only! Needs additional CoreRemoting.Channels.Quic assembly) |
For minimal changes, use the ClassicRemotingApi:
// Replace
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
// With
using CoreRemoting.ClassicRemotingApi;Before (App.config):
<service>
<wellknown mode="Singleton"
type="MyNamespace.MyService, MyAssembly"
objectUri="MyService"/>
</service>After (App.config):
<services>
<add serviceName="MyService"
interfaceAssemblyName="MySharedAssembly"
interfaceTypeName="MyNamespace.IMyService"
implementationAssemblyName="MyServerAssembly"
implementationTypeName="MyNamespace.MyService"
lifetime="Singleton"/>
</services>Before:
var proxy = (IMyService)Activator.GetObject(
typeof(IMyService),
"tcp://localhost:9090/MyService");After:
var proxy = (IMyService)RemotingServices.Connect(
typeof(IMyService),
"MyService");For full modernization, replace configuration with code:
Server:
using var server = new RemotingServer(new ServerConfig()
{
HostName = "localhost",
NetworkPort = 9090,
RegisterServicesAction = container =>
{
container.RegisterService<IMyService, MyService>(ServiceLifetime.Singleton);
}
});
server.Start();Client:
using var client = new RemotingClient(new ClientConfig()
{
ServerHostName = "localhost",
ServerPort = 9090
});
client.Connect();
var proxy = client.CreateProxy<IMyService>();CoreRemoting supports events across the remoting boundary:
// Service interface
public interface INotificationService
{
event Action<string> NotificationReceived;
void Subscribe();
void Unsubscribe();
}
// Client usage
proxy.NotificationReceived += message =>
Console.WriteLine($"Received: {message}");CoreRemoting provides enhanced authentication:
<coreRemoting>
<serverInstances>
<add uniqueInstanceName="SecureServer"
networkPort="9090"
authenticationRequired="true"
authenticationProvider="windows"/>
</serverInstances>
</coreRemoting>Enable message encryption:
<add uniqueInstanceName="SecureServer"
networkPort="9090"
messageEncryption="true"/>Modernize with DI containers:
// Microsoft.Extensions.DependencyInjection
services.AddCoreRemotingServer(new ServerConfig
{
RegisterServicesAction = container =>
{
container.RegisterService<IMyService, MyService>(ServiceLifetime.Singleton);
}
});- Phase 1: Add CoreRemoting compatibility layer
- Phase 2: Update configuration files
- Phase 3: Test basic functionality
- Phase 4: Modernize API usage (optional)
- Unit Tests: Test service implementations unchanged
- Integration Tests: Verify client-server communication
- Performance Tests: Compare with original .NET Remoting
- Cross-Platform Tests: Test on multiple operating systems
- Environment-Specific Configs: Use separate configs for development/staging/production
- Security: Enable authentication and encryption in production
- Monitoring: Add logging for troubleshooting
try
{
var proxy = RemotingServices.Connect<IMyService>("MyService");
var result = proxy.GetData();
}
catch (CoreRemotingException ex)
{
// Handle CoreRemoting-specific errors
}
catch (Exception ex)
{
// Handle general exceptions
}// Use using statements for proper disposal
using var client = new RemotingClient(clientConfig);
client.Connect();
// Or manually dispose when needed
IServiceProxy proxy = null;
try
{
proxy = client.CreateProxy<IMyService>();
// Use proxy
}
finally
{
proxy?.Dispose();
}Problem: Client cannot connect to server Solution:
- Verify firewall settings
- Check port availability
- Ensure server is running before client
Problem: Type not found during deserialization Solution:
- Ensure shared assemblies are compatible
- Check serialization format consistency
- Verify type names are identical
Problem: Authentication provider errors Solution:
- Verify authentication provider configuration
- Check credentials format
- Ensure provider is available on target platform
Problem: Channel not supported on platform Solution:
- Use TCP for cross-platform compatibility
- QUIC requires .NET 9.0
- Enable Logging:
<coreRemoting>
<logging enabled="true" level="Debug"/>
</coreRemoting>- Verify Configuration:
// Validate configuration before starting
var config = new ServerConfig();
// ... configure
var server = new RemotingServer(config);- Test Connectivity:
// Simple connectivity test
try
{
client.Connect();
Console.WriteLine("Connection successful");
}
catch (Exception ex)
{
Console.WriteLine($"Connection failed: {ex.Message}");
}Migrating from .NET Remoting to CoreRemoting provides:
- Cross-platform compatibility
- Enhanced security features
- Modern .NET support
- Flexible configuration options
- Multiple transport channels
The ClassicRemotingApi compatibility layer enables incremental migration with minimal code changes, while the modern API provides full access to CoreRemoting's advanced features for comprehensive modernization.
For additional examples and patterns, refer to the provided migration examples in the Examples/MigrateNetRemoting/ directory.