Skip to content

C# Server Events Client

Demis Bellot edited this page Oct 25, 2016 · 7 revisions

This page has moved to docs.servicestack.net


Like ServiceStack's other C# Service Clients, the new ServerEventsClient is a portable library contained in the ServiceStack.Client NuGet package:

PM> Install-Package ServiceStack.Client

And like the Service Clients it requires the BaseUri of your ServiceStack instance as well as an optional channel for the client to subscribe to:

var client = new ServerEventsClient(
    "http://chat.servicestack.net", channels:"home");

Managed Connection

The C# ServerEvent Client is a managed .NET client with feature parity with the ServiceStack's JavaScript client that auto-reconnects when a connection is lost, sends periodic heartbeats to maintain an active subscription as well as auto-unregistering once the client stops listening for messages, or gets disposed.

Handling Server Events

Unlike other C# clients, the ServerEvents Client is mainly reactive in that it's primarily waiting for Server Events to be initiated from a remote server instead of the typical scenario in which requests are initiated by clients. To maximize utility, there are a number of different API's to receive and process messages:

Assigning Callback Handlers

One way to receive messages (useful in long-running clients) is to assign handlers for each of the different events that are fired. This example shows how to capture all the different events a Client can receive:

ServerEventConnect connectMsg = null;
var msgs = new List<ServerEventMessage>();
var commands = new List<ServerEventMessage>();
var errors = new List<Exception>();

var client = new ServerEventsClient(baseUri) {
    OnConnect = e => connectMsg = e,
    OnCommand = commands.Add,
    OnMessage = msgs.Add,
    OnException = errors.Add,
}.Start();

Once the Client is configured, calling Start() will start listening for messages and calling Stop() or Dispose() will cancel the background HTTP connection and stop it listening for server events.

Customizing Metadata sent to clients

As ServerEvents have deep integration with the rest of ServiceStack we're able to offer Typed Messages containing the users UserAuthId, DisplayName and ProfileUrl of the users avatar when it's available. The typed messages also offer an extensible Dictionary<string,string> Meta collection for maintaining custom metadata that can be sent to clients by appending to them in the ServerEventsFeature hooks, which can be defined when registering ServerEventsFeature:

Plugins.Add(new ServerEventsFeature { 
    // private Connect args
    OnConnect = (subscription,httpReq) => AppendTo(subscription.Meta),

    // public Join/Leave args
    OnCreated = (subscription,httpReq) => AppendTo(subscription.Meta), 
})

Using C# Async/Await friendly API's

Depending on your use-case, if you only want to use the ServerEvent Client for a short-time to listen for predictable responses (i.e. waiting for a Server callback on a pending request) you can alternatively use the Task-based API's letting you to participate in C# async/await workflows:

var client = new ServerEventsClient(baseUri, channel="Home");

// Wait to receive onConnect event
ServerEventConnect connectMsg = await client.Connect();

// Wait to receive onJoin command event
ServerEventCommand joinMsg = await client.WaitForNextCommand();

// Hold a future task to get notified once a msg has been received
Task<ServerEventMessage> msgTask = client1.WaitForNextMessage();

// Send a Web Service Request using the built-in JsonServiceClient
client.ServiceClient.Post(new PostChatToChannel {
    Channel = client.Channel,     // The channel we're listening on
    From = client.SubscriptionId, // Populated after Connect() 
    Message = "Hello, World!",
});

// Wait till we receive the chat Msg event we sent earlier
ServerEventMessage msg = await msgTask;

The above example showcases the 3 Task-based API's available:

  1. Connect() wait till receiving confirmation of a successful event subscription
  2. WaitForNextCommand() wait for the next onJoin or onLeave subscription events
  3. WaitForNextMessage() wait for the next message published to the channel

The ServiceClient property lets you access a JsonServiceClient that's pre-configured with the clients BaseUri so that is primed for Sending Web Service Requests with.

After the ServerEvent Client has connected, the ConnectionInfo property is populated with the typed ServerEventConnect response.

Message Event Handlers

The above examples show generic API's for receiving any type of message, but just like in the JavaScript client, more fine-grained API's are available for handling specific message types.

The Handlers dictionary is akin to the JavaScript Client's Global Event Handlers which specify lambda's to be executed when messages are sent with the cmd.* selector:

client.Handlers["chat"] = (client, msg) => {
    //Deserialize JSON string to typed DTO
    var chatMsg = msg.Json.FromJson<ChatMessage>(); 
    "Received '{0}' from '{1}'".Print(chatMsg.Message, chatMsg.FromName);
};

Roughly translates to the equivalent JavaScript below:

$(source).handleServerEvents({
  handlers: {
    chat: function (msg, event) {
        console.log("Received " + msg.message + " from " + msg.fromName);
    }
  }
});

Where both methods handle the ChatMessage sent with the cmd.chat selector.

Named Receivers

Whilst handlers provide a light way to handle loose-typed messages, there's a more structured and typed option that works similar to ServiceStack's IService classes but are used to instead handle typed Server Event Messages.

To be able to handle messages with your own classes, get them to implement the IReceiver empty marker interface:

public interface IReceiver
{
    void NoSuchMethod(string selector, object message);
}

Whilst primarily a marker interface, IReceiver does include a NoSuchMethod API to be able to handle messages sent with a unknown selector target that doesn't match any defined method or property.

Named Receivers are equivalent to Receivers in the JavaScript client which can be assigned to handle all messages sent to a receiver with the selector format:

{receiver}.{target}

A Named Receiver can be registered with the API below:

client.RegisterNamedReceiver<TestNamedReceiver>("test");

Which will forward all messages with a test.* selector to an instance of the TestNamedReceiver Type

public class TestNamedReceiver : ServerEventReceiver
{
    public void FooMethod(CustomType request) {} // void return type

    public CustomType BarMethod(CustomType request)
    {        
        return request; // works with any return type, which are ignored
    }

    public CustomType BazSetter { get; set; } // Auto populate properties

    public override void NoSuchMethod(string selector, object message)
    {
        var msg = (ServerEventMessage)message;
        var nonExistentMethodType = msg.Json.FromJson<CustomType>();
    }
}

This is roughly equivalent to the following JavaScript code:

$(source).handleServerEvents({
    receivers: {
        test: {
            FooMethod: function (msg, event) { ... },
            BarMethod: function (msg, event) { ... },
            BazSetter: null,            
        }
    }
});

The ServerEventReceiver is a convenient base class that in addition to implementing IReceiver interface, gets injected with the Client as well as additional context about the raw message available in base.Request.

Unknown Message Handling

One difference in the JavaScript client is that messages with unknown targets are assigned as properties on the test receiver, e.g test.QuxTarget = {..}.

Sending messages to Named Receivers

Once registered, an instance of TestNamedReceiver will process messages sent with a test.* selector. The example below shows how to send a DTO to each of TestNamedReceiver defined methods and properties:

public class MyEventServices : Service
{
    public IServerEvents ServerEvents { get; set; }

    public void Any(CustomType request)
    {
        ServerEvents.NotifyChannel("home", "test.FooMethod", request);
        ServerEvents.NotifyChannel("home", "test.BarMethod", request);
        ServerEvents.NotifyChannel("home", "test.BazSetter", request);

        ServerEvents.NotifyChannel("home", "test.QuxTarget", request);
    }
}

Life-cycle of Receivers

Similar to Services in ServiceStack, each message is processed with an instance of the Receiver that's resolved from ServerEventsClient.Resolver which by default uses the NewInstanceResolver to execute messages using a new instance of the Receiver Type:

public class NewInstanceResolver : IResolver
{
    public T TryResolve<T>()
    {
        return typeof(T).CreateInstance<T>();
    }
}

This can be changed to re-use the same instance by assigning a SingletonInstanceResolver instead:

public class SingletonInstanceResolver : IResolver
{
    ConcurrentDictionary<Type, object> Cache = 
        new ConcurrentDictionary<Type, object>();

    public T TryResolve<T>()
    {
        return (T)Cache.GetOrAdd(typeof(T), 
            type => type.CreateInstance<T>());
    }
}

client.Resolver = new SingletonInstanceResolver();

We can also have it resolve instances from your preferred IOC. Here's an example showing how to register all Receiver Types, auto-wire them with any custom dependencies, and instruct the client to resolve instances from our IOC:

// Register all Receivers:
client.RegisterNamedReceiver<TestNamedReceiver>("test");
...

// Register all dependencies used in a new Funq.Container:
var container = new Container();
container.RegisterAs<Dependency, IDependency>();

// Go through an auto-wire all Registered Receiver Types with Funq:
container.RegisterAutoWiredTypes(client.ReceiverTypes);

// Change the client to resolve receivers from the new Funq Container:
client.Resolver = container;

We can assign Funq.Container directly as it already implements the IResolver interface, whilst you can re-use the existing IOC Container Adapters to enable support for other IOCs.

The Global Receiver

Whilst Named Receivers are used to handle messages sent to a specific namespaced selector, the client also supports registering a Global Receiver for handling messages sent with the special cmd.* selector.

Handling Messages with the Default Selector

All IServerEvents Notify API's inlcudes overloads for sending messages without a selector that by convention will take the format cmd.{TypeName}.

These events can be handled with a Global Receiver based on Message type, e.g:

public class GlobalReceiver : ServerEventReceiver
{
    public SetterType AnyNamedProperty { get; set; }

    public void AnyNamedMethod(CustomType request)
    {
        ...
    }
}

client.RegisterReceiver<GlobalReceiver>();

Which will be called when messages are sent without a selector, e.g:

public class MyServices : Service
{
    public IServerEvents ServerEvents { get; set; }

    public void Any(Request request)
    {
        ServerEvents.NotifyChannel("home", new CustomType { ... });
        ServerEvents.NotifyChannel("home", new SetterType { ... });
    }
}

As Global Receivers handle other messages sent with the cmd.* selector and can be re-used as a named receiver, we can define a single class to handle all the different custom messages sent in chat.servicestack.net App, E.g:

cmd.chat Hi
cmd.announce This is your captain speaking...
cmd.toggle#channels
css.background-image url(https://servicestack.net/img/bg.jpg)
...

The above messages can all be handled with the Receiver below:

public class JavaScriptReceiver : ServerEventReceiver
{
    public void Chat(ChatMessage message) { ... }
    public void Announce(string message) { ... }
    public void Toggle(string message) { ... }
    public void BackgroundImage(string cssRule) { ... }
}

client.RegisterReceiver<JavaScriptReceiver>();
client.RegisterNamedReceiver<JavaScriptReceiver>("css");

As seen above the target names are case-insensitive and - are collapsed to cater for JavaScript/CSS naming conventions.

Add Authentication support to .NET ServerEvents Client

The explicit Authenticate and AuthenticateAsync API's can be used to authenticate the ServerEvents ServiceClient which shares cookies with the WebRequest that connects to the /event-stream so authenticating with the Server Events ServiceClient will also authenticate the /event-stream HTTP Connection:

client.Authenticate(new Authenticate {
    provider = CredentialsAuthProvider.Name,
    UserName = "user",
    Password = "pass",
    RememberMe = true,
});

client.Start();

This is equivalent to:

client.ServiceClient.Post(new Authenticate {
    provider = CredentialsAuthProvider.Name,
    UserName = "user",
    Password = "pass",
    RememberMe = true,
});

.NET UpdateSubscriber APIs

The sync/async APIs below built into the C# ServerEventsClient will let you modify an active Server Events subscription to join new or leave existing channels:

client.UpdateSubscriber(new UpdateEventSubscriber { 
    SubscribeChannels = new[]{ "chan1", "chan2" },
    UnsubscribeChannels = new[]{ "chan3", "chan4" },
});

client.SubscribeToChannels("chan1", "chan2");
client.UnsubscribeFromChannels("chan3", "chan4");

await client.SubscribeToChannelsAsync("chan1", "chan2");
await client.UnsubscribeFromChannelsAsync("chan3", "chan4");

onUpdate Notification

As this modifies the active subscription it also publishes a new onUpdate notification to all channel subscribers so they're able to maintain up-to-date info on each subscriber. This can be handled together with onJoin and onLeave events using OnCommand:

client.OnCommand = msg => ...; //= ServerEventJoin, ServerEventLeave or ServerEventUpdate

ServerEvent .NET Examples

Xamarin.Android Chat utilizes the .NET PCL Server Events Client to create an Android Chat App connecting to the existing chat.servicestack.net Server Events back-end where it's able to communicate with existing Ajax clients and other connected Android Chat Apps.

YouTube Video and AndroidXamarinChat Repo



  1. Getting Started

    1. Creating your first project
    2. Create Service from scratch
    3. Your first webservice explained
    4. Example Projects Overview
    5. Learning Resources
  2. Designing APIs

    1. ServiceStack API Design
    2. Designing a REST-ful service with ServiceStack
    3. Simple Customer REST Example
    4. How to design a Message-Based API
    5. Software complexity and role of DTOs
  3. Reference

    1. Order of Operations
    2. The IoC container
    3. Configuration and AppSettings
    4. Metadata page
    5. Rest, SOAP & default endpoints
    6. SOAP support
    7. Routing
    8. Service return types
    9. Customize HTTP Responses
    10. Customize JSON Responses
    11. Plugins
    12. Validation
    13. Error Handling
    14. Security
    15. Debugging
    16. JavaScript Client Library (ss-utils.js)
  4. Clients

    1. Overview
    2. C#/.NET client
      1. .NET Core Clients
    3. Add ServiceStack Reference
      1. C# Add Reference
      2. F# Add Reference
      3. VB.NET Add Reference
      4. Swift Add Reference
      5. Java Add Reference
    4. Silverlight client
    5. JavaScript client
      1. Add TypeScript Reference
    6. Dart Client
    7. MQ Clients
  5. Formats

    1. Overview
    2. JSON/JSV and XML
    3. HTML5 Report Format
    4. CSV Format
    5. MessagePack Format
    6. ProtoBuf Format
  6. View Engines 4. Razor & Markdown Razor

    1. Markdown Razor
  7. Hosts

    1. IIS
    2. Self-hosting
    3. Messaging
    4. Mono
  8. Security

    1. Authentication
    2. Sessions
    3. Restricting Services
    4. Encrypted Messaging
  9. Advanced

    1. Configuration options
    2. Access HTTP specific features in services
    3. Logging
    4. Serialization/deserialization
    5. Request/response filters
    6. Filter attributes
    7. Concurrency Model
    8. Built-in profiling
    9. Form Hijacking Prevention
    10. Auto-Mapping
    11. HTTP Utils
    12. Dump Utils
    13. Virtual File System
    14. Config API
    15. Physical Project Structure
    16. Modularizing Services
    17. MVC Integration
    18. ServiceStack Integration
    19. Embedded Native Desktop Apps
    20. Auto Batched Requests
    21. Versioning
    22. Multitenancy
  10. Caching

  11. Caching Providers

  12. HTTP Caching 1. CacheResponse Attribute 2. Cache Aware Clients

  13. Auto Query

  14. Overview

  15. Why Not OData

  16. AutoQuery RDBMS

  17. AutoQuery Data 1. AutoQuery Memory 2. AutoQuery Service 3. AutoQuery DynamoDB

  18. Server Events

    1. Overview
    2. JavaScript Client
    3. C# Server Events Client
    4. Redis Server Events
  19. Service Gateway

    1. Overview
    2. Service Discovery
  20. Encrypted Messaging

    1. Overview
    2. Encrypted Client
  21. Plugins

    1. Auto Query
    2. Server Sent Events
    3. Swagger API
    4. Postman
    5. Request logger
    6. Sitemaps
    7. Cancellable Requests
    8. CorsFeature
  22. Tests

    1. Testing
    2. HowTo write unit/integration tests
  23. ServiceStackVS

    1. Install ServiceStackVS
    2. Add ServiceStack Reference
    3. TypeScript React Template
    4. React, Redux Chat App
    5. AngularJS App Template
    6. React Desktop Apps
  24. Other Languages

    1. FSharp
      1. Add ServiceStack Reference
    2. VB.NET
      1. Add ServiceStack Reference
    3. Swift
    4. Swift Add Reference
    5. Java
      1. Add ServiceStack Reference
      2. Android Studio & IntelliJ
      3. Eclipse
  25. Amazon Web Services

  26. ServiceStack.Aws

  27. PocoDynamo

  28. AWS Live Demos

  29. Getting Started with AWS

  30. Deployment

    1. Deploy Multiple Sites to single AWS Instance
      1. Simple Deployments to AWS with WebDeploy
    2. Advanced Deployments with OctopusDeploy
  31. Install 3rd Party Products

    1. Redis on Windows
    2. RabbitMQ on Windows
  32. Use Cases

    1. Single Page Apps
    2. HTML, CSS and JS Minifiers
    3. Azure
    4. Connecting to Azure Redis via SSL
    5. Logging
    6. Bundling and Minification
    7. NHibernate
  33. Performance

    1. Real world performance
  34. Other Products

    1. ServiceStack.Redis
    2. ServiceStack.OrmLite
    3. ServiceStack.Text
  35. Future

    1. Roadmap
Clone this wiki locally