Custom Implementation Example

<< 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();

       }

   }

}