}  // end AvroEncode

        /// <summary>
        /// Make a copy of an object as an Avro GenericRecord
        /// </summary>
        /// <param name="obj">Original object</param>
        /// <param name="ktype">An associated KineticaType object that
        /// describes the original object.</param>
        /// <returns>GenericRecord object which is a copy of the specified object</returns>
        private Avro.Generic.GenericRecord MakeGenericRecord(object obj, KineticaType ktype)
        {
            // Get the schema
            var schema = KineticaData.SchemaFromType(obj.GetType(), ktype);

            // Create a new GenericRecord for this schema
            var recordToSend = new Avro.Generic.GenericRecord(schema);

            // Copy each field from obj to recordToSend
            foreach (var field in schema.Fields)
            {
                var property = obj.GetType()
                               .GetProperties()
                               .FirstOrDefault(prop => prop.Name.ToLowerInvariant() == field.Name.ToLowerInvariant());

                if (property == null)
                {
                    continue;
                }

                recordToSend.Add(field.Name, property.GetValue(obj, null));
            }

            // Return the newly created object
            return(recordToSend);
        }
        }  // lookupKineticaType()

        /// <summary>
        /// Encode specified object using Avro
        /// </summary>
        /// <param name="obj">Object to encode</param>
        /// <returns>Byte array of binary Avro-encoded data</returns>
        internal byte[] AvroEncode(object obj)
        {
            // Create a stream that will allow us to view the underlying memory
            using (var ms = new MemoryStream())
            {
                // Write the object to the memory stream
                // If obj is an ISpecificRecord, this is more efficient
                if (obj is Avro.Specific.ISpecificRecord)
                {
                    var schema = (obj as Avro.Specific.ISpecificRecord).Schema;
                    Avro.Specific.SpecificDefaultWriter writer = new Avro.Specific.SpecificDefaultWriter(schema);
                    writer.Write(schema, obj, new BinaryEncoder(ms));
                }
                else // Not an ISpecificRecord - this way is less efficient
                {
                    // Get the KineticaType associated with the object to be encoded
                    Type         obj_type = obj.GetType();
                    KineticaType ktype    = lookupKineticaType(obj_type);
                    if (ktype == null)
                    {
                        throw new KineticaException("No known KineticaType associated with the given object.  " +
                                                    "Need a known KineticaType to encode the object.");
                    }

                    // Make a copy of the object to send as a GenericRecord, then write that to the memory stream
                    var schema       = KineticaData.SchemaFromType(obj.GetType(), ktype);
                    var recordToSend = MakeGenericRecord(obj, ktype);
                    var writer       = new Avro.Generic.DefaultWriter(schema);
                    writer.Write(schema, recordToSend, new BinaryEncoder(ms));
                }

                // Get the memory from the stream
                return(ms.ToArray());
            }
        }  // end AvroEncode
        }     // end AvroDecode<T>

        /// <summary>
        /// Decode binary Avro data from a stream into an object
        /// </summary>
        /// <typeparam name="T">Type of expected object</typeparam>
        /// <param name="stream">Stream to read for object data</param>
        /// <returns>New object</returns>
        private T AvroDecode <T>(Stream stream) where T : Avro.Specific.ISpecificRecord, new()
        {
            // T obj = new T(); // Activator.CreateInstance<T>();
            var schema = KineticaData.SchemaFromType(typeof(T), null);
            var reader = new Avro.Specific.SpecificReader <T>(schema, schema);

            return(reader.Read(default(T), new BinaryDecoder(stream)));
        }
        /// <summary>
        /// Decode binary Avro data into an object.
        /// </summary>
        /// <typeparam name="T">Type of expected object</typeparam>
        /// <param name="bytes">Binary Avro data</param>
        /// <param name="ktype">An optional KineticaType object to help in decoding the object.</param>
        /// <returns>New object</returns>
        private T AvroDecode <T>(byte[] bytes, KineticaType ktype = null) where T : new()
        {
            // Get the schema
            var schema = KineticaData.SchemaFromType(typeof(T), ktype);

            // Create a stream to read the binary data
            using (var ms = new MemoryStream(bytes))
            {
                // Create a new object to return
                T obj = new T();
                if (obj is Avro.Specific.ISpecificRecord)
                {
                    var reader = new Avro.Specific.SpecificDefaultReader(schema, schema);
                    reader.Read(obj, new BinaryDecoder(ms));
                }
                else
                {
                    // Not ISpecificRecord, so first read into a new GenericRecord
                    var reader = new Avro.Generic.DefaultReader(schema, schema);
                    Avro.Generic.GenericRecord recordToReceive = new Avro.Generic.GenericRecord(schema);
                    reader.Read(recordToReceive, new BinaryDecoder(ms));

                    // Now, copy all the fields from the GenericRecord to obj
                    foreach (var field in schema.Fields)
                    {
                        var property = obj.GetType()
                                       .GetProperties()
                                       .FirstOrDefault(prop => prop.Name.ToLowerInvariant() == field.Name.ToLowerInvariant());

                        if (property == null)
                        {
                            continue;
                        }

                        object val;
                        // Try to get the property
                        if (recordToReceive.TryGetValue(field.Name, out val))
                        {
                            // If successful, write the property to obj
                            property.SetValue(obj, val);
                        }
                    } // end foreach
                }     // end if-else

                // Return the new object
                return(obj);
            } // end using
        }     // end AvroDecode<T>