<< Click to Display Table of Contents >> Navigation: Development > Client > .NET Library > Custom Implementation Example |
This example shows a more complex implementation of a DTSClient, where we extend the class and customize elements to suit our purposes.
First, the Client (CustomClient.cs):
using Alloy.DTS.Base.Model;
using Alloy.DTS.Base.Record;
using Alloy.DTS.Base.Type;
using Alloy.DTS.Client;
namespace Alloy.DTS.ClientTest
{
public class CustomClient : DTSClient
{
// Our client will be a singleton for ease of use throughout the application
private static CustomClient instance;
public static CustomClient Instance
{
get {
if (instance == null)
{
instance = new CustomClient();
}
return instance;
}
}
// We will only use one collection, so let's keep it here
DTSRemoteCollection? _collection;
public CustomClient() : base()
{
// we also want to override some root types in our client
initSpecialTypes();
}
private void initSpecialTypes()
{
// overriding types is as simple as calling this static method on DTSTypesClusterManager
// but be aware that you will most likely need to define and implement
// custom Serialization/Deserialization routines
DTSTypesCluster.OverrideRootType(DTSTypes.GEOMETRY_TYPE, typeof(CustomGeometry));
DTSTypesCluster.OverrideRootType(DTSTypes.POINT_TYPE, typeof(CustomGeometry));
DTSTypesCluster.OverrideRootType(DTSTypes.LINE_TYPE, typeof(CustomGeometry));
DTSTypesCluster.OverrideRootType(DTSTypes.AREA_TYPE, typeof(CustomGeometry));
// Register the CustomGeometry serializer
JsonEngine.Options.Converters.Add(new CustomGeometrySerializer());
}
// We override the open() method to automatically wait for our endpoint and set our collection
public override void Open(string projectName) {
base.Open(projectName);
DTSDatasource ds = GetDatasource(projectName);
if (!ds.WaitForEndpoints(new List<string> { "myOnlyConnector" }, 60 * 1000))
throw new Exception("No endpoint");
_collection = ds.GetEndpoint("myOnlyConnector").Collections["public.all_things"];
}
// We decide we want our client to react in some way when a stream times out
protected override void OnClosedStream(string streamId)
{
Console.WriteLine("Stream " + streamId + " has timed out!");
/*
do something
*/
}
// and here is the point of our implementation - a nice shortcut to getting the geometric center of an object
public double[] GetObjectCenter(string objectId)
{
// we only need 1 record, but we still need a stream
// we'll use another way of managing it this time
// first we get the DTSRemoteRecordStream object using createStream on the collection (which gets fed our query predicate)
DTSRemoteRecordStream stream = _collection.CreateStream(DTSPredicate.Eq("id", objectId));
// then we advance the stream by 1 so that the record we're looking for makes its way into the buffer
stream.Advance(1);
// then we grab the record from the buffer
IValuesContainer obj = stream.RecordContainer.Records[0];
// we use the getFieldValue() method to extract the geometry (which will be in our CustomGeometry format because we overrode the types)
CustomGeometry geom = (CustomGeometry)obj.AsMap()["geom"];
// and finally call a method we created on CustomGeometry that calculates its center
// while this particular result could easily be obtained without overriding root types and defining custom deserializers
// we are using it to illustrate a point and it is easy to see how this can be a powerful tool in more complex scenarios
return geom.GetCenter();
}
}
}
The CustomGeometry class we're using (CustomGeometry.cs):
using Alloy.DTS.Base.Model.Geometry;
namespace Alloy.DTS.ClientTest
{
public class CustomGeometry
{
// We'll hold the GeoJson that comes through
public GeoJson OriginalGeom { get; set; }
public CustomGeometry(GeoJson geoJson)
{
OriginalGeom = geoJson;
}
// And calculate the center when needed
public double[] GetCenter()
{
if (OriginalGeom is GeoJsonPoint) {
// trivial...
GeoJsonPoint point = (GeoJsonPoint)OriginalGeom;
return point.Coordinates;
} else if (OriginalGeom is GeoJsonLineString) {
// naive...
GeoJsonLineString line = (GeoJsonLineString)OriginalGeom;
List<double[]> coords = line.Coordinates;
return new double[] {
(coords[0][0] - coords[coords.Count-1][0]) / 2,
(coords[0][1] - coords[coords.Count-1][1]) / 2
};
} else if (OriginalGeom is GeoJsonPolygon) {
// eh...
throw new Exception("Not worth it...");
}
throw new Exception("Unsupported geometry");
}
}
}
And the custom Deserializer (CustomGeometrySerializer.cs):
using Alloy.DTS.Base.Model;
using Alloy.DTS.Base.Model.Geometry;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Alloy.DTS.ClientTest
{
public class CustomGeometrySerializer : JsonConverter<CustomGeometry>
{
// We will need some clean JsonSerializerOptions as well
private JsonSerializerOptions _cleanSerializerOptions = new JsonSerializerOptions
{
WriteIndented = true,
Converters =
{
new ResourceIdentifierSerializer(),
new GeoJsonSerializer()
}
};
public override CustomGeometry? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
// We simply use the serializer options to deserialize the object to the class it was meant to be
GeoJson geoJson = (GeoJson)JsonSerializer.Deserialize(
JsonDocument.ParseValue(ref reader).RootElement.GetRawText(),
typeof(GeoJson),
_cleanSerializerOptions);
// The we create our custom object and place the original geometry inside it
CustomGeometry geom = new CustomGeometry(geoJson);
return geom;
}
public override void Write(Utf8JsonWriter writer, CustomGeometry value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
}