You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by mm...@apache.org on 2021/01/20 00:29:03 UTC

[geode-native] branch develop updated: GEODE-8562: Add new C# classaskey Example (#725)

This is an automated email from the ASF dual-hosted git repository.

mmartell pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode-native.git


The following commit(s) were added to refs/heads/develop by this push:
     new e9076f5  GEODE-8562: Add new C# classaskey Example  (#725)
e9076f5 is described below

commit e9076f5cfeaedc6fa7951a33bf4ea32f09f7fafc
Author: Michael Martell <mm...@pivotal.io>
AuthorDate: Tue Jan 19 16:28:46 2021 -0800

    GEODE-8562: Add new C# classaskey Example  (#725)
    
    * Add System.Drawing for classaskey example
    * Use DateTime in the key and add stopserver
    * Use smaller dataset to minimize output
    * Switch to CacheableDate for consistency
    
    Co-authored-by: Dave Barnes <da...@vmware.com>
---
 clicache/src/CacheableString.hpp                   |   2 +-
 examples/dotnet/CMakeLists.txt                     |   3 +
 examples/dotnet/CMakeLists.txt.dotnet_example.in   |   2 +-
 examples/dotnet/CMakeLists.txt.in                  |   1 +
 .../CMakeLists.txt}                                |  10 +-
 examples/dotnet/classaskey/PhotosKey.cs            | 117 ++++++++++++
 examples/dotnet/classaskey/PhotosValue.cs          | 121 ++++++++++++
 examples/dotnet/classaskey/Program.cs              | 212 +++++++++++++++++++++
 examples/dotnet/classaskey/README.md               |  95 +++++++++
 examples/dotnet/classaskey/startserver.ps1         |  45 +++++
 .../stopserver.ps1}                                |  37 ++--
 .../utilities/InstantiateDataSerializable.java     |  52 +++++
 examples/utilities/PhotosKey.java                  |  93 +++++++++
 13 files changed, 769 insertions(+), 21 deletions(-)

diff --git a/clicache/src/CacheableString.hpp b/clicache/src/CacheableString.hpp
index c15d7f3..b4f1c37 100644
--- a/clicache/src/CacheableString.hpp
+++ b/clicache/src/CacheableString.hpp
@@ -40,7 +40,7 @@ namespace Apache
       /// An immutable string wrapper that can serve as a distributable
       /// key object for caching as well as being a string value.
       /// </summary>
-      ref class CacheableString
+      public ref class CacheableString
         :  public IDataSerializablePrimitive, public CacheableKey
       {
       public:
diff --git a/examples/dotnet/CMakeLists.txt b/examples/dotnet/CMakeLists.txt
index fba2374..11ef580 100644
--- a/examples/dotnet/CMakeLists.txt
+++ b/examples/dotnet/CMakeLists.txt
@@ -49,6 +49,9 @@ endfunction()
 add_example(NAME authinitialize
 	SOURCE ExampleAuthInitialize.cs Program.cs)
 
+add_example(NAME classaskey
+	SOURCE PhotosKey.cs PhotosValue.cs Program.cs)
+
 add_example(NAME continuousquery
 	SOURCE Order.cs MyCqListener.cs Program.cs)
 
diff --git a/examples/dotnet/CMakeLists.txt.dotnet_example.in b/examples/dotnet/CMakeLists.txt.dotnet_example.in
index 978a013..f845522 100644
--- a/examples/dotnet/CMakeLists.txt.dotnet_example.in
+++ b/examples/dotnet/CMakeLists.txt.dotnet_example.in
@@ -31,4 +31,4 @@ target_link_libraries(${PROJECT_NAME}
 
 set_target_properties(${PROJECT_NAME} PROPERTIES
     VS_DOTNET_TARGET_FRAMEWORK_VERSION "v4.5.2"
-    VS_DOTNET_REFERENCES "System;${@PRODUCT_NAME_NOSPACE@_DOTNET_LIBRARY}")
+    VS_DOTNET_REFERENCES "System;System.Drawing;${@PRODUCT_NAME_NOSPACE@_DOTNET_LIBRARY}")
diff --git a/examples/dotnet/CMakeLists.txt.in b/examples/dotnet/CMakeLists.txt.in
index 19935c2..69ec90d 100644
--- a/examples/dotnet/CMakeLists.txt.in
+++ b/examples/dotnet/CMakeLists.txt.in
@@ -18,6 +18,7 @@ cmake_minimum_required(VERSION 3.10)
 project(@PRODUCT_DLL_NAME@.DotNet.Examples LANGUAGES NONE)
 
 add_subdirectory(authinitialize)
+add_subdirectory(classaskey)
 add_subdirectory(continuousquery)
 add_subdirectory(dataserializable)
 add_subdirectory(functionexecution)
diff --git a/examples/dotnet/CMakeLists.txt.dotnet_example.in b/examples/dotnet/classaskey/CMakeLists.txt
similarity index 80%
copy from examples/dotnet/CMakeLists.txt.dotnet_example.in
copy to examples/dotnet/classaskey/CMakeLists.txt
index 978a013..87365ed 100644
--- a/examples/dotnet/CMakeLists.txt.dotnet_example.in
+++ b/examples/dotnet/classaskey/CMakeLists.txt
@@ -15,20 +15,20 @@
 
 cmake_minimum_required(VERSION 3.10)
 
-project(dotnet-@ADD_EXAMPLE_NAME@ LANGUAGES CSharp)
+project(dotnet-classaskey LANGUAGES CSharp)
 
 set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../../cmake)
 
-find_package(@PRODUCT_NAME_NOSPACE@ REQUIRED COMPONENTS dotnet)
+find_package(GeodeNative REQUIRED COMPONENTS dotnet)
 
-add_executable(${PROJECT_NAME} @ADD_EXAMPLE_SOURCE@)
+add_executable(${PROJECT_NAME} PhotosKey.cs;PhotosValue.cs;Program.cs)
 
 configure_file("startserver.ps1" ${CMAKE_CURRENT_BINARY_DIR} COPYONLY)
 configure_file("stopserver.ps1" ${CMAKE_CURRENT_BINARY_DIR} COPYONLY)
 
 target_link_libraries(${PROJECT_NAME}
-    @PRODUCT_NAME_NOSPACE@::dotnet)
+    GeodeNative::dotnet)
 
 set_target_properties(${PROJECT_NAME} PROPERTIES
     VS_DOTNET_TARGET_FRAMEWORK_VERSION "v4.5.2"
-    VS_DOTNET_REFERENCES "System;${@PRODUCT_NAME_NOSPACE@_DOTNET_LIBRARY}")
+    VS_DOTNET_REFERENCES "System;System.Drawing;${GeodeNative_DOTNET_LIBRARY}")
diff --git a/examples/dotnet/classaskey/PhotosKey.cs b/examples/dotnet/classaskey/PhotosKey.cs
new file mode 100644
index 0000000..eb8bb9a
--- /dev/null
+++ b/examples/dotnet/classaskey/PhotosKey.cs
@@ -0,0 +1,117 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License.  You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+using Apache.Geode.Client;
+using System;
+using System.Collections.Generic;
+
+namespace Apache.Geode.Examples.ClassAsKey
+{
+  public class PhotosKey : IDataSerializable, ICacheableKey
+  {
+    public List<CacheableString> people;
+    public CacheableDate rangeStart;
+    public CacheableDate rangeEnd;
+
+    // A default constructor is required for deserialization
+    public PhotosKey() { }
+
+    public PhotosKey(List<CacheableString> names, CacheableDate start, CacheableDate end)
+    {
+      people = names;
+      rangeStart = start;
+      rangeEnd = end;
+    }
+
+    public override string ToString()
+    {
+      string result = "{";
+      for (int i = 0; i < people.Count; i++)
+      {
+        result += people[i];
+        if (i<people.Count-1)
+          result += ", ";
+      }
+      result += "} from ";
+      return result + rangeStart.ToString() + " to " +
+        rangeEnd.ToString();
+    }
+
+    public void ToData(DataOutput output)
+    {
+      output.WriteObject(people);
+      output.WriteObject(rangeStart);
+      output.WriteObject(rangeEnd);
+    }
+
+    public void FromData(DataInput input)
+    {
+      people = (List<CacheableString>)input.ReadObject();
+      rangeStart = (CacheableDate)input.ReadObject();
+      rangeEnd = (CacheableDate)input.ReadObject();
+    }
+
+    public ulong ObjectSize
+    {
+      get { return 0; }
+    }
+
+    public bool Equals(ICacheableKey other)
+    {
+      return Equals((object)other);
+    }
+
+    public override bool Equals(object obj)
+    {
+      if (this == obj)
+      {
+        return true;
+      }
+
+      if (GetType() != obj.GetType())
+      {
+        return false;
+      }
+
+      PhotosKey otherKey = (PhotosKey)obj;
+      return (people == otherKey.people &&
+        rangeStart == otherKey.rangeStart &&
+        rangeEnd == otherKey.rangeEnd);
+    }
+
+    public override int GetHashCode()
+    {
+      int prime = 31;
+      int result = 1;
+      foreach (CacheableString cs in people)
+      {
+        result = result * prime + cs.GetHashCode();
+      }
+
+      result = result * prime + rangeStart.GetHashCode();
+      result = result * prime + rangeEnd.GetHashCode();
+
+      return result;
+    }
+
+    public static ISerializable CreateDeserializable()
+    {
+      return new PhotosKey();
+    }
+  }
+}
+
+
diff --git a/examples/dotnet/classaskey/PhotosValue.cs b/examples/dotnet/classaskey/PhotosValue.cs
new file mode 100644
index 0000000..db16d57
--- /dev/null
+++ b/examples/dotnet/classaskey/PhotosValue.cs
@@ -0,0 +1,121 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License.  You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+using Apache.Geode.Client;
+using System;
+using System.Drawing;
+using System.Collections.Generic;
+
+namespace Apache.Geode.Examples.ClassAsKey
+{
+  public class PhotoMetaData : IDataSerializable
+  {
+    public int fullResId;
+    public Bitmap thumbnailImage;
+
+    public const int THUMB_WIDTH = 32;
+    public const int THUMB_HEIGHT = 32;
+
+    // A default constructor is required for deserialization
+    public PhotoMetaData() { }
+
+    public PhotoMetaData(int id, Bitmap thumb)
+    {
+      fullResId = id;
+      thumbnailImage = thumb;
+    }
+
+    public void ToData(DataOutput output)
+    {
+      output.WriteInt32(fullResId);
+
+      for (int i=0; i<thumbnailImage.Height; i++)
+      {
+        for (int j=0; j<thumbnailImage.Width; j++)
+        {
+          output.WriteInt32(thumbnailImage.GetPixel(i, j).ToArgb());
+        }
+      }
+    }
+
+    public void FromData(DataInput input)
+    {
+      fullResId = input.ReadInt32();
+
+      thumbnailImage = new Bitmap(THUMB_WIDTH, THUMB_HEIGHT);
+      for (int i = 0; i < thumbnailImage.Height; i++)
+      {
+        for (int j = 0; j < thumbnailImage.Width; j++)
+        {
+          thumbnailImage.SetPixel(i, j, Color.FromArgb(input.ReadInt32()));
+        }
+      }
+    }
+
+    public ulong ObjectSize
+    {
+      get { return 0; }
+    }
+
+    public static ISerializable CreateDeserializable()
+    {
+      return new PhotoMetaData();
+    }
+  }
+
+  public class PhotosValue : IDataSerializable
+  {
+    public List<PhotoMetaData> photosMeta;
+
+    // A default constructor is required for deserialization
+    public PhotosValue() { }
+
+    public PhotosValue(List<PhotoMetaData> metaData)
+    {
+      photosMeta = metaData;
+    }
+
+    public void ToData(DataOutput output)
+    {
+      output.WriteObject(photosMeta);
+    }
+
+    public void FromData(DataInput input)
+    {
+      photosMeta = new List<PhotoMetaData>();
+      var pmd = input.ReadObject() as IList<object>;
+      if (pmd != null)
+      {
+        foreach (var item in pmd)
+        {
+          photosMeta.Add((PhotoMetaData)item);
+        }
+      }
+    }
+
+    public ulong ObjectSize
+    {
+      get { return 0; }
+    }
+
+    public static ISerializable CreateDeserializable()
+    {
+      return new PhotosValue();
+    }
+  }
+}
+
+
diff --git a/examples/dotnet/classaskey/Program.cs b/examples/dotnet/classaskey/Program.cs
new file mode 100644
index 0000000..e51093d
--- /dev/null
+++ b/examples/dotnet/classaskey/Program.cs
@@ -0,0 +1,212 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License.  You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using Apache.Geode.Client;
+
+namespace Apache.Geode.Examples.ClassAsKey
+{
+  public class Program
+  {
+    static Cache cache;
+    static Random rand;
+
+    public static void Main(string[] args)
+    {
+      const int MAXPHOTOKEYS = 10;
+      const int MAXPHOTOSPERKEY = 5;
+
+      IRegion<PhotosKey, PhotosValue> photosMetaData = CreateRegion();
+
+      Console.WriteLine("Populating the photos region\n");
+
+      PhotosKey[] keys = new PhotosKey[MAXPHOTOKEYS];
+      PhotosValue[] values = new PhotosValue[MAXPHOTOKEYS];
+
+      CacheableDate start;
+      CacheableDate end;
+
+      rand = new Random();
+      int numPhotos;
+      int photoId = 0;
+
+      for (int i=0; i<MAXPHOTOKEYS; i++)
+      {
+        ChooseDateRange(out start, out end);
+        keys[i] = new PhotosKey(ChoosePeople(), start, end);
+
+        numPhotos = rand.Next(0, MAXPHOTOSPERKEY+1);
+        List<PhotoMetaData> metaData = new List<PhotoMetaData>();
+        for (int j=0; j<numPhotos; j++)
+        {
+          PhotoMetaData meta = new PhotoMetaData();
+          meta.fullResId = photoId++;
+          meta.thumbnailImage = ChooseThumb();
+          metaData.Add(meta);
+        }
+        values[i] = new PhotosValue(metaData);
+
+        Console.WriteLine("Inserting " + numPhotos + " photos for key: " + keys[i].ToString());
+
+        photosMetaData.Put(keys[i], values[i]);
+      }
+
+      // Verify the region was populated properly
+      photoId = 0;
+      Console.WriteLine();
+      for (int k = 0; k < MAXPHOTOKEYS; k++)
+      {
+        Console.WriteLine("Fetching photos for key: " + keys[k].ToString());
+
+        PhotosValue value = photosMetaData.Get(keys[k]);
+        PhotoMetaData meta;
+        for (int p = 0; p < value.photosMeta.Count; p++)
+        {
+          Console.WriteLine("   Fetching photo number " + p);
+
+          meta = value.photosMeta[p];
+          if (meta.fullResId != photoId)
+            Console.WriteLine("      ERROR: Expected fullResId = " + photoId + " but actual = " + meta.fullResId);
+
+          bool thumbValid = true;
+          for (int i=0; i<PhotoMetaData.THUMB_HEIGHT; i++)
+          {
+            for (int j=0; j<PhotoMetaData.THUMB_WIDTH; j++)
+            {
+              if (meta.thumbnailImage.GetPixel(i,j) != values[k].photosMeta[p].thumbnailImage.GetPixel(i,j))
+              {
+                Console.WriteLine("      ERROR: Unexpected thumb for photoId = " + photoId);
+                thumbValid = false;
+                break;
+              }
+            }
+
+            if (!thumbValid)
+              break;
+          }
+
+          photoId++;
+        }
+      }
+
+      cache.Close();
+      Console.ReadLine();
+    }
+
+    public static IRegion<PhotosKey, PhotosValue> CreateRegion()
+    {
+      cache = new CacheFactory()
+          .Set("log-level", "debug")
+          .Set("log-file", "c:/temp/classaskey.log")
+          .Create();
+
+      Console.WriteLine("Registering for data serialization");
+
+        cache.TypeRegistry.RegisterType(PhotosKey.CreateDeserializable, 500);
+        cache.TypeRegistry.RegisterType(PhotosValue.CreateDeserializable, 501);
+        cache.TypeRegistry.RegisterType(PhotoMetaData.CreateDeserializable, 502);
+
+        cache.GetPoolManager()
+            .CreateFactory()
+            .AddLocator("localhost", 10334)
+            .Create("pool");
+
+      var regionFactory = cache.CreateRegionFactory(RegionShortcut.PROXY)
+          .SetPoolName("pool");
+      IRegion<PhotosKey, PhotosValue> photosMetaData = regionFactory.Create<PhotosKey, PhotosValue>("photosMetaData");
+      return photosMetaData;
+    }
+
+    public static List<CacheableString> ChoosePeople()
+    {
+      List<CacheableString> availablePeople = new List<CacheableString> {
+        new CacheableString("Alice"),
+        new CacheableString("Bob"),
+        new CacheableString("Carol"),
+        new CacheableString("Ted")
+      };
+
+      List<CacheableString> chosenPeople = new List<CacheableString>();
+
+      // Choose at least one person
+      int numChosen = rand.Next(1, availablePeople.Count+1);
+
+      int index;
+      int numAvailable = availablePeople.Count;
+
+      for (int i=0; i<numChosen; i++)
+      {
+        // Choose someone not already chosen
+        index = rand.Next(numAvailable);
+        chosenPeople.Add(availablePeople[index]);
+
+        // Update available people
+        availablePeople.RemoveAt(index);
+        numAvailable--;
+      }
+
+      // Sort the chosen. We only care who is chosen, not the order they're chosen.
+      IComparer<CacheableString> comparer = new CacheableStringComparer();
+      chosenPeople.Sort(comparer);
+      return chosenPeople;
+    }
+
+    public static void ChooseDateRange(out CacheableDate start, out CacheableDate end)
+    {
+      //Choose start and end dates between Jan 1, 1970 and now
+      var earliestStart = new DateTime(1970, 1, 1);
+      int numAvailableDays = (int)(DateTime.Now - earliestStart).TotalDays;
+
+      var startIndex = rand.Next(numAvailableDays);
+      var startDate = earliestStart.AddDays(startIndex);
+      start = new CacheableDate(startDate);
+
+      int numRemainingDays = (int)(DateTime.Now - startDate).TotalDays;
+      end = new CacheableDate(startDate.AddDays(rand.Next(numRemainingDays)));
+    }
+
+    public static Bitmap ChooseThumb()
+    {
+      int thumbWidth = 32;
+      int thumbHeight = 32;
+
+      Bitmap thumb = new Bitmap(thumbWidth, thumbHeight);
+      for (int j=0; j<thumbHeight; j++)
+      {
+        for (int i = 0; i < thumbWidth; i++)
+        {
+          thumb.SetPixel(i, j, Color.FromArgb(rand.Next(256), rand.Next(256), rand.Next(256)));
+        }
+      }
+
+      return thumb;
+    }
+  }
+
+  public class CacheableStringComparer : IComparer<CacheableString>
+  {
+    public int Compare(CacheableString a, CacheableString b)
+    {
+      return a.Value.CompareTo(b.Value);
+    }
+  }
+
+}
+
+
diff --git a/examples/dotnet/classaskey/README.md b/examples/dotnet/classaskey/README.md
new file mode 100644
index 0000000..2cf63ff
--- /dev/null
+++ b/examples/dotnet/classaskey/README.md
@@ -0,0 +1,95 @@
+# classaskey example
+
+Many applications are best served by using compound keys to store and retrieve data. Geode Native fills this need by allowing users to define their own custom class to be used as a key. By leveraging the Geode Native Cacheable data types, it is very easy to implement the ICacheableKey interface and design classes that can be used as keys.
+
+This example shows how to design a photo filter as the key for storing metadata for a photo library. The photo filter class (called PhotoKeys in the code) provides for storing and retrieving all photos containing a group of people and that were taken during a date range. The photo metadata class (called PhotoValues in the code) contains the full resolution photoId and thumbnail image for the photo key. In this example, the photoId is an integer representing an index into a photo library. [...]
+
+## Prerequisites
+
+* Install [Apache Geode](https://geode.apache.org)
+* Build and install [Apache Geode Native](https://github.com/apache/geode-native)
+* Apache Geode Native examples, built and installed
+* Set `GEODE_HOME` to the install directory of Apache Geode
+
+## Running
+
+1. From a command shell, set the current directory to the `classaskey` directory in your example workspace.
+
+    ```console
+    $ cd workspace/examples/build/dotnet/classaskey
+    ```
+
+1. Run the `startserver.ps1` script to start the Geode cluster with the example.jar file and create a region.
+
+   For Windows cmd:
+
+    ```console
+    $ powershell.exe -File startserver.ps1
+    ```
+
+   For Windows Powershell:
+
+    ```console
+    $ startserver.ps1
+    ```
+
+1. Execute `Debug\dotnet-classaskey.exe`. Expect output similar to below. Since the keys are generated using random numbers, your output will differ.
+
+    ```console
+    Registering for data serialization
+    Populating the photos region
+
+    Inserting 5 photos for key: {Alice, Bob, Carol, Ted} from 7/3/1999 12:00:00 AM to 3/17/2011 12:00:00 AM
+    Inserting 2 photos for key: {Alice, Bob, Ted} from 9/22/1980 12:00:00 AM to 12/5/1980 12:00:00 AM
+    Inserting 5 photos for key: {Alice, Carol} from 3/6/1990 12:00:00 AM to 9/26/1993 12:00:00 AM
+    Inserting 1 photos for key: {Alice, Bob} from 1/29/1999 12:00:00 AM to 2/1/2002 12:00:00 AM
+    Inserting 0 photos for key: {Alice, Bob, Carol, Ted} from 6/5/1989 12:00:00 AM to 7/7/1989 12:00:00 AM
+    Inserting 1 photos for key: {Alice, Bob} from 4/8/1979 12:00:00 AM to 5/26/2011 12:00:00 AM
+    Inserting 0 photos for key: {Alice, Carol, Ted} from 5/25/1977 12:00:00 AM to 5/15/1997 12:00:00 AM
+    Inserting 0 photos for key: {Bob} from 2/20/1981 12:00:00 AM to 3/11/2020 12:00:00 AM
+    Inserting 0 photos for key: {Alice, Bob, Carol, Ted} from 7/9/1982 12:00:00 AM to 4/20/1998 12:00:00 AM
+    Inserting 3 photos for key: {Alice, Bob, Carol, Ted} from 11/24/2007 12:00:00 AM to 4/30/2012 12:00:00 AM
+
+    Fetching photos for key: {Alice, Bob, Carol, Ted} from 7/3/1999 12:00:00 AM to 3/17/2011 12:00:00 AM
+       Fetching photo number 0
+       Fetching photo number 1
+       Fetching photo number 2
+       Fetching photo number 3
+       Fetching photo number 4
+    Fetching photos for key: {Alice, Bob, Ted} from 9/22/1980 12:00:00 AM to 12/5/1980 12:00:00 AM
+       Fetching photo number 0
+       Fetching photo number 1
+    Fetching photos for key: {Alice, Carol} from 3/6/1990 12:00:00 AM to 9/26/1993 12:00:00 AM
+       Fetching photo number 0
+       Fetching photo number 1
+       Fetching photo number 2
+       Fetching photo number 3
+       Fetching photo number 4
+    Fetching photos for key: {Alice, Bob} from 1/29/1999 12:00:00 AM to 2/1/2002 12:00:00 AM
+       Fetching photo number 0
+    Fetching photos for key: {Alice, Bob, Carol, Ted} from 6/5/1989 12:00:00 AM to 7/7/1989 12:00:00 AM
+    Fetching photos for key: {Alice, Bob} from 4/8/1979 12:00:00 AM to 5/26/2011 12:00:00 AM
+       Fetching photo number 0
+    Fetching photos for key: {Alice, Carol, Ted} from 5/25/1977 12:00:00 AM to 5/15/1997 12:00:00 AM
+    Fetching photos for key: {Bob} from 2/20/1981 12:00:00 AM to 3/11/2020 12:00:00 AM
+    Fetching photos for key: {Alice, Bob, Carol, Ted} from 7/9/1982 12:00:00 AM to 4/20/1998 12:00:00 AM
+    Fetching photos for key: {Alice, Bob, Carol, Ted} from 11/24/2007 12:00:00 AM to 4/30/2012 12:00:00 AM
+       Fetching photo number 0
+       Fetching photo number 1
+       Fetching photo number 2
+    [fine 2021/01/16 23:55:35.040714 Pacific Standard Time FirstPro:26196 20716] Cache closed.
+    ```
+    
+1. Run the `stopserver.ps1` script to gracefully shutdown the Geode cluster.
+
+   For Windows cmd:
+
+    ```console
+    $ powershell.exe -File stopserver.ps1
+    ```
+
+   For Windows Powershell:
+
+    ```console
+    $ stopserver.ps1
+    ```
diff --git a/examples/dotnet/classaskey/startserver.ps1 b/examples/dotnet/classaskey/startserver.ps1
new file mode 100644
index 0000000..7f352be
--- /dev/null
+++ b/examples/dotnet/classaskey/startserver.ps1
@@ -0,0 +1,45 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+$GFSH_PATH = ""
+if (Get-Command gfsh -ErrorAction SilentlyContinue)
+{
+    $GFSH_PATH = "gfsh"
+}
+else
+{
+    if (-not (Test-Path env:GEODE_HOME))
+    {
+        Write-Host "Could not find gfsh.  Please set the GEODE_HOME path. e.g. "
+        Write-Host "(Powershell) `$env:GEODE_HOME = <path to Geode>"
+        Write-Host " OR"
+        Write-Host "(Command-line) set %GEODE_HOME% = <path to Geode>"
+    }
+    else
+    {
+        $GFSH_PATH = "$env:GEODE_HOME\bin\gfsh.bat"
+    }
+}
+
+if ($GFSH_PATH -ne "")
+{
+   $expression = "$GFSH_PATH " + `
+	     "-e 'start locator --name=locator --dir=$PSScriptRoot\locator' " + `
+	     "-e 'deploy --jar=$PSScriptRoot/../../utilities/example.jar' " + `
+		 "-e 'start server --name=server --dir=$PSScriptRoot\server' " + `
+         "-e 'create region --name=photosMetaData --type=PARTITION' " + `
+		 "-e 'execute function --id=InstantiateDataSerializable --member=server'";
+   Invoke-Expression $expression
+}
\ No newline at end of file
diff --git a/examples/dotnet/CMakeLists.txt.in b/examples/dotnet/classaskey/stopserver.ps1
similarity index 54%
copy from examples/dotnet/CMakeLists.txt.in
copy to examples/dotnet/classaskey/stopserver.ps1
index 19935c2..164e0c6 100644
--- a/examples/dotnet/CMakeLists.txt.in
+++ b/examples/dotnet/classaskey/stopserver.ps1
@@ -13,18 +13,27 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-cmake_minimum_required(VERSION 3.10)
+$GFSH_PATH = ""
+if (Get-Command gfsh -ErrorAction SilentlyContinue)
+{
+    $GFSH_PATH = "gfsh"
+}
+else
+{
+    if (-not (Test-Path env:GEODE_HOME))
+    {
+        Write-Host "Could not find gfsh.  Please set the GEODE_HOME path. e.g. "
+        Write-Host "(Powershell) `$env:GEODE_HOME = <path to Geode>"
+        Write-Host " OR"
+        Write-Host "(Command-line) set %GEODE_HOME% = <path to Geode>"
+    }
+    else
+    {
+        $GFSH_PATH = "$env:GEODE_HOME\bin\gfsh.bat"
+    }
+}
 
-project(@PRODUCT_DLL_NAME@.DotNet.Examples LANGUAGES NONE)
-
-add_subdirectory(authinitialize)
-add_subdirectory(continuousquery)
-add_subdirectory(dataserializable)
-add_subdirectory(functionexecution)
-add_subdirectory(pdxautoserializer)
-add_subdirectory(pdxserializable)
-add_subdirectory(putgetremove)
-add_subdirectory(remotequery)
-add_subdirectory(transaction)
-
-add_subdirectory(sslputget)
+if ($GFSH_PATH -ne "")
+{
+   Invoke-Expression "$GFSH_PATH -e 'connect' -e 'destroy region --name=photosMetaData' -e 'stop server --name=server' -e 'stop locator --name=locator'"
+}
\ No newline at end of file
diff --git a/examples/utilities/InstantiateDataSerializable.java b/examples/utilities/InstantiateDataSerializable.java
new file mode 100644
index 0000000..80b0139
--- /dev/null
+++ b/examples/utilities/InstantiateDataSerializable.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package javaobject;
+
+
+import java.util.*;
+import java.io.*;
+import org.apache.geode.*; // for DataSerializable
+import org.apache.geode.cache.Declarable;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.execute.FunctionAdapter;
+import org.apache.geode.cache.execute.FunctionContext;
+import org.apache.geode.cache.execute.ResultSender;
+import org.apache.geode.cache.execute.RegionFunctionContext;
+import org.apache.geode.cache.partition.PartitionRegionHelper;
+
+public class InstantiateDataSerializable extends FunctionAdapter implements Declarable{
+
+public void execute(FunctionContext context) {
+
+  Instantiator.register(new Instantiator(javaobject.PhotosKey.class, 500) {
+    public DataSerializable newInstance() {
+      return new javaobject.PhotosKey();
+    }
+  });
+
+  ResultSender sender = context.getResultSender();
+    sender.lastResult(0);
+  } 
+
+  public String getId() {
+    return "InstantiateDataSerializable";
+  }
+
+  public void init(Properties arg0) {
+  }
+}
diff --git a/examples/utilities/PhotosKey.java b/examples/utilities/PhotosKey.java
new file mode 100644
index 0000000..35c9520
--- /dev/null
+++ b/examples/utilities/PhotosKey.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package javaobject;
+
+import java.util.*;
+import java.io.*;
+import org.apache.geode.*;
+import org.apache.geode.cache.Declarable;
+import org.apache.geode.DataSerializer;
+
+
+public class PhotosKey implements DataSerializable {
+  public List<String> people;
+  public Date rangeStart;
+  public Date rangeEnd;
+
+  static {
+     Instantiator.register(new Instantiator(javaobject.PhotosKey.class, 500) {
+     public DataSerializable newInstance() {
+        return new PhotosKey();
+     }
+   });
+  }
+
+  /* public no-arg constructor required for DataSerializable */  
+  public PhotosKey() {}
+
+  public PhotosKey(List<String> names, Date start, Date end){
+    people = names;
+	rangeStart = start;
+	rangeEnd = end;
+  }
+  
+  public void fromData(DataInput in) throws IOException, ClassNotFoundException {
+    this.people = DataSerializer.readObject(in);
+	this.rangeStart = DataSerializer.readDate(in);
+	this.rangeEnd = DataSerializer.readDate(in);
+  }
+  
+  public void toData(DataOutput out) throws IOException {
+    DataSerializer.writeObject(this.people, out);
+    DataSerializer.writeDate(this.rangeStart, out);
+    DataSerializer.writeDate(this.rangeEnd, out);
+  } 
+  
+
+  public int hashCode() {
+	final int prime = 31;
+	int result = 1;
+	for( String s : people )
+	{
+		result = result * prime + s.hashCode();
+	}
+
+	result = result * prime + rangeStart.hashCode();
+	result = result * prime + rangeEnd.hashCode();
+	return result;
+  }
+
+
+  public boolean equals(final Object obj)
+  {
+    if (this == obj)
+      return true;
+    if (obj == null)
+      return false;
+    if (getClass() != obj.getClass())
+      return false;
+    final PhotosKey other = (PhotosKey) obj;
+
+    if (!people.equals(other.people))
+      return false;
+
+	if (!rangeStart.equals(other.rangeStart) || !rangeEnd.equals(other.rangeEnd))
+	  return false;
+
+    return true;
+  }
+}