# Publishing a Binary subject from a .NET Caplin DataSource

The page provides an overview of how to publish a subject of type [Binary](datasource-binary.md) from a .NET DataSource.

## Publishing a Binary subject from a .NET DataSource

The instructions on this page assume you already know how to create a .NET DataSource project. For more information on creating a .NET DataSource, see the examples in the .NET DSDK kit.

## Requirements

To publish binary messages, you require:

* A Caplin DataSource .NET 8.0.12+
* Caplin Liberator 8.0.15+ (with Caplin StreamLink 8.0.7+)
* [Optional] Caplin Transformer 8.0.8+

## Overview

Binary messages are published via a [`ICachingPublisher`](https://docs.caplin.com/developer/api/datasource_dotnet/latest/interfaceCaplin_1_1DataSource_1_1Publisher_1_1ICachingPublisher.html), which caches the last published value for each subject it serves. The `ICachingPublisher` uses its cache to serve new requests for subjects it has already served, and, in the case of Binary data, to determine if it is more efficient to publish an update as a [Binary patch](https://www.rfc-editor.org/rfc/rfc3284.txt) or in full.

You provide data for Binary subjects by implementing the [`ICachingDataProvider`](https://docs.caplin.com/developer/api/datasource_dotnet/latest/interfaceCaplin_1_1DataSource_1_1Publisher_1_1ICachingDataProvider.html) interface, which you then register with a [`DataSource`](https://docs.caplin.com/developer/api/datasource_dotnet/latest/classCaplin_1_1DataSource_1_1DataSource.html) instance using `DataSource.CreateCachingPublisher(__INamespace__, __ICachingDataProvider__)`. This call returns a [`ICachingPublisher`](https://docs.caplin.com/developer/api/datasource_dotnet/latest/interfaceCaplin_1_1DataSource_1_1Publisher_1_1ICachingPublisher.html), which you must make available to your `ICachingDataProvider` in order for it to publish [`IBinaryMessage`](https://docs.caplin.com/developer/api/datasource_dotnet/latest/interfaceCaplin_1_1DataSource_1_1Messaging_1_1Binary_1_1IBinaryMessage.html) objects.

When a DataSource receives a Binary subject request, if the `ICachingPublisher` for the subject namespace does not have a cached object for the subject, then the `ICachingPublisher` routes the request to its associated `ICachingDataProvider`. The `ICachingDataProvider` subscribes to data in the backend and publishes a `IBinaryMessage`. 

**Serving a request for an uncached object**

```plantuml
participant StreamLink
participant Liberator
box ".NET DataSource"
  participant DataSource
  participant "ICachingPublisher (/FX/...)" as ICachingPublisher
  participant ICachingDataProvider
end box
participant "Backend System" as BackendSystem

StreamLink -> Liberator : /FX/GBPUSD
Liberator -> DataSource : /FX/GBPUSD
DataSource -> ICachingPublisher : /FX/GBPUSD
note over ICachingPublisher
Object not in cache
end note
ICachingPublisher -> ICachingDataProvider : ICachingDataProvider.ReceiveRequest(//subject//)
ICachingDataProvider -> BackendSystem : Subscribe to GBPUSD
ICachingDataProvider <- BackendSystem
ICachingPublisher <- ICachingDataProvider : ICachingPublisher.Publish(//binaryMessage//)
ICachingPublisher -> ICachingPublisher : Cache Binary data
DataSource <- ICachingPublisher
Liberator <- DataSource
StreamLink <- Liberator
```

When a DataSource receives a Binary subject request, if the `ICachingPublisher` for the subject namespace has a cached object for the subject, then the `ICachingPublisher` serves the request directly from its cache:

**Serving a request for a cached object**

```plantuml
participant StreamLink
participant Liberator
box ".NET DataSource"
  participant DataSource
  participant "CachingPublisher (/FX/...)" as ICachingPublisher
  participant ICachingDataProvider
end box
participant "Backend System" as BackendSystem

StreamLink -> Liberator : /FX/GBPUSD
Liberator -> DataSource : /FX/GBPUSD
DataSource -> ICachingPublisher : /FX/GBPUSD
note over ICachingPublisher
Object in cache
end note
DataSource <- ICachingPublisher
Liberator <- DataSource
StreamLink <- Liberator
```

The `ICachingDataProvider` implementation should also publish updates resulting from its subscription to backend data events. When the `ICachingDataProvider` publishes an update to a subject that the `ICachingPublisher` has already cached, the `ICachingPublisher` chooses the most efficient format in which to publish the update: as a [Binary patch](https://www.rfc-editor.org/rfc/rfc3284.txt) or as the full image.

**Serving updates for a cached object**

```plantuml
collections "Subscribed Peers" as Peers
box ".NET DataSource"
  participant DataSource
  participant "ICachingPublisher (/FX/...)" as ICachingPublisher
  participant ICachingDataProvider
end box
participant "Backend System" as BackendSystem

BackendSystem -> ICachingDataProvider : Update for GBPUSD
ICachingDataProvider -> ICachingPublisher : ICachingPublisher.Publish(//binaryMessage//)
note over ICachingPublisher
Compare new binary data to cached object.
Update message to be published to be a Binary
patch or full image ?
end note
ICachingPublisher -> ICachingPublisher : Update cache
ICachingPublisher -> DataSource
DataSource -> Peers
```

## Example

In this example, we’ll create a `ICachingDataProvider` that handles binary data.

**ExampleBinaryAdapter.cs**

```csharp
using Caplin.DataSource;
using Caplin.DataSource.Namespace;
using Caplin.DataSource.Publisher;
using Caplin.Logging;

namespace ExampleBinaryAdapter.NET
{
    public class ExampleBinaryAdapter
    {
        private IDataSource dataSource;

        public ExampleBinaryAdapter(string[] args)
        {
            ILogger logger = new ConsoleLogger();
            dataSource = new DataSource("demosource.conf", args, logger);
 
            ICachingDataProvider provider = new PricingCachingDataProvider(); //<1>
            dataSource.CreateCachingPublisher(new PrefixNamespace("/FX"), provider);//<2> 

        }

        public void Run()
        {
            dataSource.Start();
        }

        public static void Main(string[] args) //<3>
        {
            new ExampleBinaryAdapter(args).Run();
        }
    }
}
```

1. Instantiate a `ICachingDataProvider`. In this example we instantiate a `PricingCachingDataProvider` (see source code below).
2. Register the `PricingCachingDataProvider` with the `DataSource` to create a `ICachingPublisher`. The `DataSource` injects the `ICachingPublisher` into the provider via its `SetPublisher` method.
3. The program entry point: create the adapter and start it.

**PricingCachingDataProvider.cs**

The `ICachingDataProvider` that responds to subject requests and discards.

```csharp
using Caplin.DataSource.Messaging.Binary;
using Caplin.DataSource.Publisher;
using System;
using System.Collections.Concurrent;
using System.Threading;

public class PricingCachingDataProvider : ICachingDataProvider
{
    private readonly Random random = new Random();
    private readonly ConcurrentDictionary<string, Timer> subscriptions =
        new ConcurrentDictionary<string, Timer>();
    private ICachingPublisher publisher;

    public void ReceiveRequest(string subject) //<1>
    {
        // Subscribe to the back end system. In this example we simulate it by
        // publishing a new random price for the subject every second.
        Timer timer = new Timer(_ =>
        {
            Price price = CreateRandomPrice(subject.Split('/')[2]);
            IBinaryMessage message = publisher.CachedMessageFactory.CreateBinaryMessage(subject, price.ToBinary());
            publisher.Publish(message);
        }, null, TimeSpan.Zero, TimeSpan.FromSeconds(1));
        subscriptions[subject] = timer;
    }

    public void ReceiveDiscard(string subject) //<2>
    {
        // Unsubscribe from the back end system.
        if (subscriptions.TryRemove(subject, out Timer timer))
        {
            timer.Dispose();
        }
    }
    
    public void SetPublisher(ICachingPublisher publisher) //<3>
    {
        this.publisher = publisher;
    }

    private Price CreateRandomPrice(string currencyPair)
    {
        return new Price(
            random.Next().ToString(),
            random.NextDouble().ToString(),
            random.NextDouble().ToString(),
            currencyPair);
    }
}
```
1. On subject request, subscribe to a backend process to provide updates. This method is called by the `ICachingPublisher` when it does not have cached data for a subject; thereafter, requests for the same subject are served from the `ICachingPublisher` cache. In this example, we start a simulation that publishes a random price for the subject every second.
2. On subject discard, cancel the backend process that generates updates for the subject. In this example, we cancel the timer that publishes a random price every second.
3. The `SetPublisher` method is part of the `ICachingDataProvider` interface; the `DataSource` calls it to give the provider the `ICachingPublisher` it uses to publish.

**Price.cs**

A class that represents the data to send as a Binary message.

```csharp
using System;
using System.IO;
using System.Text;

public class Price
{
    private readonly string id;
    private readonly string bid;
    private readonly string ask;
    private readonly string currencyPair;

    public Price(string id, string bid, string ask, string currencyPair)
    {
        this.id = id;
        this.bid = bid;
        this.ask = ask;
        this.currencyPair = currencyPair;
    }

    public string Id => id;
    public string Bid => bid;
    public string Ask => ask;
    public string CurrencyPair => currencyPair;

    public byte[] ToBinary()
    {
        using (var ms = new MemoryStream())
        using (var writer = new BinaryWriter(ms, Encoding.UTF8, true))
        {
            try
            {
                writer.Write(id);
                writer.Write(bid);
                writer.Write(ask);
                writer.Write(currencyPair);
                writer.Flush();
            }
            catch (IOException)
            {
                // Ignored
            }
            return ms.ToArray();
        }
    }
}
```

---

**See also**:

* [Binary data type](datasource-binary.md)
* [Subscribing to a Binary subject](../streamlink/streamlink-subscribing-to-binary.md)
