Skip to content

.NET Object-oriented RPC-framework that allows clients to work with hosted objects remotelly (call methods, get or set properties, subscribe to events, etc.).

License

Notifications You must be signed in to change notification settings

ni28/EloquentObjects

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

46 Commits
 
 
 
 
 
 
 
 

Repository files navigation

EloquentObjects

EloquentObjects is a .NET fast and lightweight IPC framework that allows clients to work with hosted objects remotelly (call methods, get or set properties, subscribe to events, etc.). Can be used as an object-oriented replacement to traditional RPC (Remote Procedure Call) mechanisms.

Target Frameworks

  • .NET Framework 4.5+
  • .NET Standard 2.0+

Installation

Packages are available on NuGet: EloquentObjects. You can use the following command in the Package Manager Console: Install-Package EloquentObjects

Concepts

EloquentObjects can host objects that implement attributed interfaces (like in WCF) using TCP (for RPC) or named pipes (for IPC) bindings. Multiple clients can connect to the same hosted object remotelly. Each hosted object has an object ID that is used by clients to distinguish between hosted objects.

Following features are supported:

  • Call methods and get responses.
  • Receive and rethrow exception if it occured in hosted object method
  • Call one-way methods (responses and exceptions are not sent to client for such methods).
  • Subscribe to hosted objects events (EventHandler, EventHandler and Action events are supported).

Note that EloquentObjects behave differently from traditional Remote Procedure Call (RPC) implementations, for example:

  • In RPC, the client makes a request and waits for the response.
  • In RPC, the server doesn't push anything to the client unless it's in response to a request.
  • Often, the design of RPC is such that different clients are served by independent service instances.

Object hosting can be started with just few lines of code:

//Create a server that will run on 127.0.0.1:50000 in RPC mode (i.e. using TCP binding)
using (var remoteObjectServer = new EloquentServer("tcp://127.0.0.1:50000"))
{
	//Start hosting for the given object with given Object ID that will be used by client to access this object.
	remoteObjectServer.Add<IYourContractHere>("<Your Object ID here>", <You object here>);
	
	//Keep the server running
	while(true) { }
}

When object is hosted you can connect to remote object:

//Create a client that will listen receive object events on "tcp://127.0.0.1:50001"
//Client will keep a communication session with the server.
//One client can connect to multiple objects (distinguished by object ID which can be any string)
using (var client = new EloquentClient("tcp://127.0.0.1:50000", "tcp://127.0.0.1:50001"))
{
	//Connect and use the object.
	//The 'yourObject' will have IYourContractHere type below:
	var yourObject = client.Connect<IYourContractHere>("<Your Object ID here>");
}

Quick start example

  1. Create Server, Client and Contract assemblies. Add dependency from Contract assembly both to Server and to Client.
  2. Define an attributed contract in a Contract assembly that is available both for server and client:
public interface IEloquentCalculator
{
	string Name { get; set; }

	int Add(int a, int b);
	
	void Sqrt(int a);

	event EventHandler<OperationResult> ResultReady;
}
    
[DataContract]
public sealed class OperationResult
{
	public OperationResult(double value)
	{
		Value = value;
	}
	
	[DataMember]
	public double Value { get; private set; }
}

Note that complex data DTOs (e.g. OperationResult in example above) can be used as properties, method parameters, method return values and event parameters.

  1. Implement a contract in Server assembly.
internal sealed class EloquentCalculator: IEloquentCalculator
{
	#region Implementation of ICalculatorService
	...
	#endregion
}
  1. Create a server and start an object hosting in a Server assembly:
//Create an object that will be hosted
var calculator = new EloquentCalculator();

//Create a server that will run on 127.0.0.1:50000 using TCP binding
using (var remoteObjectServer = new EloquentServer("tcp://127.0.0.1:50000"))
{
	//Start hosting for the calculator with Object ID = Calculator1
	remoteObjectServer.Add<IEloquentCalculator>("Calculator1", calculator);
	
	//Keep the server running
	Console.ReadLine();
}
  1. Connect to a hosted object from Client assembly:
//Create a client that will listen to object events on "tcp://127.0.0.1:50001"
using (var client = new EloquentClient("tcp://127.0.0.1:50000", "tcp://127.0.0.1:50001"))
{
	//Use the same Object ID - Calculator1
	var calculator = client.Connect<IEloquentCalculator>("Calculator1");
		
	//Work with calculator remotelly
	var res1 = calculator.Add(1, 1);
	calculator.ResultReady += (s, r) => {...}
}

Bindings

EloquentObjects support twos communication mechanisms:

  • TCP binding (RPC)
  • Named pipes binding (IPC)

Binding is selected by URI scheme in address as shown in examples below:

//Start server with TCP binding:
using (var tcpServer = new EloquentServer("tcp://127.0.0.1:50000")) 
{
	...
}

//Start server with Named pipes binding:
using (var pipesServer = new EloquentServer("pipe://127.0.0.1:50000")) 
{
	...
}

//Create client with TCP binding:
using (var client = new EloquentClient("tcp://127.0.0.1:50000", "tcp://127.0.0.1:50001"))
{
	...
}    

//Create client with Named pipes binding:
using (var client = new EloquentClient("pipe://127.0.0.1:50000", "pipe://127.0.0.1:50001"))
{
	...
}    

Patterns

[TBD] Use following patterns with EloquentObject to get best results:

  1. Events handling
  2. Accessing child objects
  3. Hosting model layer
  4. Interface inheritance

Events handling

Eloquent object can have events of following types:

  • EventHandler
  • EventHandler
  • Action (with any number of arguments)
public interface IContract
{
	event EventHandler RegularEvent;

	event EventHandler<CustomEventArgs> RegularEventWithArgs;

	event Action NoParameterEvent;

	event Action<int> EventWithIntParameter;
}

When event handler is called for EventHandler and EventHandler event types on client side then the sender parameter will contain a proxy object. So Event Handling pattern can be used to operate with the sender.

//The following method will handle following subscribtions:
//remoteObject1.RegularEvent += OnRegularEvent;
//remoteObject2.RegularEvent += OnRegularEvent;

void OnRegularEvent(object sender, EventArgs args)
{
    //Here object will be remoteObject1 when event occured in remoteObject1 and will be remoteObject2 when event occured in remoteObject2.
    var object = (IContract)sender;
}

Modification history

[!] - Breaking API change
[+] - New feature
[B] - Bug fix

2.0.0

[!] Removed attributes from contracts. Standard C# interfaces can be used now.
[!] Changed client API. No need to create a disposable connection anymore.
[+] Implemented ability to transfer objects by references (DTO objects are still supported).

1.0.4

[+] Added test for default parameters in interface
[+] Cleaned the Calculator example to demonstrate the HostingModel layer.

1.0.3

[+] Added robustness integration tests
[B] Fixed exception on a client when server is lost
[B] Fixed exception on a client when server stopped hosting object
[B] Fixed exception on a client when server does not host any objects for requested ID

1.0.2

[!] Renamed EloquentInterfaceAttribute to EloquentContractAttribute
[+] Added named pipes transport protocol support
[+] Added integration tests
[+] Added support for EventHandler and EventHandler event types

1.0.1

[!] Channged scheme in URI address to contain "xxx://" prefix (tcp is used for TCP transport protocol, pipe is used for named pipes)

1.0.0

[+] Initial release.

TODO

Features TODO

  1. Security
  2. Polling mode
  3. Client event that connection is lost. Restore connection.

Improvements TODO

  1. Timeouts?
  2. Benchmark: gRPC
  3. Benchmark: .NET Remoting
  4. Named pipes between different PCs
  5. Support out parameters

About

.NET Object-oriented RPC-framework that allows clients to work with hosted objects remotelly (call methods, get or set properties, subscribe to events, etc.).

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages