How can I encrypt selected properties when serializing my objects? - c#

I'm using JSON to store certain settings within my application. Some of the settings contain sensitive information (e.g. passwords) while other settings are not sensitive. Ideally I'd like to be able to serialize my objects where the sensitive properties are encrypted automatically while keeping the non-sensitive settings readable. Is there a way to do this using Json.Net? I did not see any setting related to encryption.

Json.Net does not have built-in encryption. If you want to be able to encrypt and decrypt during the serialization process, you will need to write some custom code. One approach is to use a custom IContractResolver in conjunction with an IValueProvider. The value provider gives you a hook where you can transform values within the serialization process, while the contract resolver gives you control over when and where the value provider gets applied. Together, they can give you the solution you are looking for.
Below is an example of the code you would need. First off, you'll notice I've defined a new [JsonEncrypt] attribute; this will be used to indicate which properties you want to be encrypted. The EncryptedStringPropertyResolver class extends the DefaultContractResolver provided by Json.Net. I've overridden the CreateProperties() method so that I can inspect the JsonProperty objects created by the base resolver and attach an instance of my custom EncryptedStringValueProvider to any string properties which have the [JsonEncrypt] attribute applied. The EncryptedStringValueProvider later handles the actual encryption/decryption of the target string properties via the respective GetValue() and SetValue() methods.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
[AttributeUsage(AttributeTargets.Property)]
public class JsonEncryptAttribute : Attribute
{
}
public class EncryptedStringPropertyResolver : DefaultContractResolver
{
private byte[] encryptionKeyBytes;
public EncryptedStringPropertyResolver(string encryptionKey)
{
if (encryptionKey == null)
throw new ArgumentNullException("encryptionKey");
// Hash the key to ensure it is exactly 256 bits long, as required by AES-256
using (SHA256Managed sha = new SHA256Managed())
{
this.encryptionKeyBytes =
sha.ComputeHash(Encoding.UTF8.GetBytes(encryptionKey));
}
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> props = base.CreateProperties(type, memberSerialization);
// Find all string properties that have a [JsonEncrypt] attribute applied
// and attach an EncryptedStringValueProvider instance to them
foreach (JsonProperty prop in props.Where(p => p.PropertyType == typeof(string)))
{
PropertyInfo pi = type.GetProperty(prop.UnderlyingName);
if (pi != null && pi.GetCustomAttribute(typeof(JsonEncryptAttribute), true) != null)
{
prop.ValueProvider =
new EncryptedStringValueProvider(pi, encryptionKeyBytes);
}
}
return props;
}
class EncryptedStringValueProvider : IValueProvider
{
PropertyInfo targetProperty;
private byte[] encryptionKey;
public EncryptedStringValueProvider(PropertyInfo targetProperty, byte[] encryptionKey)
{
this.targetProperty = targetProperty;
this.encryptionKey = encryptionKey;
}
// GetValue is called by Json.Net during serialization.
// The target parameter has the object from which to read the unencrypted string;
// the return value is an encrypted string that gets written to the JSON
public object GetValue(object target)
{
string value = (string)targetProperty.GetValue(target);
byte[] buffer = Encoding.UTF8.GetBytes(value);
using (MemoryStream inputStream = new MemoryStream(buffer, false))
using (MemoryStream outputStream = new MemoryStream())
using (AesManaged aes = new AesManaged { Key = encryptionKey })
{
byte[] iv = aes.IV; // first access generates a new IV
outputStream.Write(iv, 0, iv.Length);
outputStream.Flush();
ICryptoTransform encryptor = aes.CreateEncryptor(encryptionKey, iv);
using (CryptoStream cryptoStream = new CryptoStream(outputStream, encryptor, CryptoStreamMode.Write))
{
inputStream.CopyTo(cryptoStream);
}
return Convert.ToBase64String(outputStream.ToArray());
}
}
// SetValue gets called by Json.Net during deserialization.
// The value parameter has the encrypted value read from the JSON;
// target is the object on which to set the decrypted value.
public void SetValue(object target, object value)
{
byte[] buffer = Convert.FromBase64String((string)value);
using (MemoryStream inputStream = new MemoryStream(buffer, false))
using (MemoryStream outputStream = new MemoryStream())
using (AesManaged aes = new AesManaged { Key = encryptionKey })
{
byte[] iv = new byte[16];
int bytesRead = inputStream.Read(iv, 0, 16);
if (bytesRead < 16)
{
throw new CryptographicException("IV is missing or invalid.");
}
ICryptoTransform decryptor = aes.CreateDecryptor(encryptionKey, iv);
using (CryptoStream cryptoStream = new CryptoStream(inputStream, decryptor, CryptoStreamMode.Read))
{
cryptoStream.CopyTo(outputStream);
}
string decryptedValue = Encoding.UTF8.GetString(outputStream.ToArray());
targetProperty.SetValue(target, decryptedValue);
}
}
}
}
Once you have the resolver in place, the next step is to apply the custom [JsonEncrypt] attribute to the string properties within your classes that you wish to be encrypted during serialization. For example, here is a contrived class that might represent a user:
public class UserInfo
{
public string UserName { get; set; }
[JsonEncrypt]
public string UserPassword { get; set; }
public string FavoriteColor { get; set; }
[JsonEncrypt]
public string CreditCardNumber { get; set; }
}
The last step is to inject the custom resolver into the serialization process. To do that, create a new JsonSerializerSettings instance, then set the ContractResolver property to a new instance of the custom resolver. Pass the settings to the JsonConvert.SerializeObject() or DeserializeObject() methods and everything should just work.
Here is a round-trip demo:
public class Program
{
public static void Main(string[] args)
{
try
{
UserInfo user = new UserInfo
{
UserName = "jschmoe",
UserPassword = "Hunter2",
FavoriteColor = "atomic tangerine",
CreditCardNumber = "1234567898765432",
};
// Note: in production code you should not hardcode the encryption
// key into the application-- instead, consider using the Data Protection
// API (DPAPI) to store the key. .Net provides access to this API via
// the ProtectedData class.
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Formatting = Formatting.Indented;
settings.ContractResolver = new EncryptedStringPropertyResolver("My-Sup3r-Secr3t-Key");
Console.WriteLine("----- Serialize -----");
string json = JsonConvert.SerializeObject(user, settings);
Console.WriteLine(json);
Console.WriteLine();
Console.WriteLine("----- Deserialize -----");
UserInfo user2 = JsonConvert.DeserializeObject<UserInfo>(json, settings);
Console.WriteLine("UserName: " + user2.UserName);
Console.WriteLine("UserPassword: " + user2.UserPassword);
Console.WriteLine("FavoriteColor: " + user2.FavoriteColor);
Console.WriteLine("CreditCardNumber: " + user2.CreditCardNumber);
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType().Name + ": " + ex.Message);
}
}
}
Output:
----- Serialize -----
{
"UserName": "jschmoe",
"UserPassword": "sK2RvqT6F61Oib1ZittGBlv8xgylMEHoZ+1TuOeYhXQ=",
"FavoriteColor": "atomic tangerine",
"CreditCardNumber": "qz44JVAoJEFsBIGntHuPIgF1sYJ0uyYSCKdYbMzrmfkGorxgZMx3Uiv+VNbIrbPR"
}
----- Deserialize -----
UserName: jschmoe
UserPassword: Hunter2
FavoriteColor: atomic tangerine
CreditCardNumber: 1234567898765432
Fiddle: https://dotnetfiddle.net/trsiQc

Though #Brian's solution is quite clever, I don't like the complexity of a custom ContractResolver. I converted Brian's code to a JsonConverter, so your code would become
public class UserInfo
{
public string UserName { get; set; }
[JsonConverter(typeof(EncryptingJsonConverter), "My-Sup3r-Secr3t-Key")]
public string UserPassword { get; set; }
public string FavoriteColor { get; set; }
[JsonConverter(typeof(EncryptingJsonConverter), "My-Sup3r-Secr3t-Key")]
public string CreditCardNumber { get; set; }
}
I've posted the (quite lengthy) EncryptingJsonConverter as a Gist and also blogged about it.

My solution:
public string PasswordEncrypted { get; set; }
[JsonIgnore]
public string Password
{
get
{
var encrypted = Convert.FromBase64String(PasswordEncrypted);
var data = ProtectedData.Unprotect(encrypted, AdditionalEntropy, DataProtectionScope.LocalMachine);
var res = Encoding.UTF8.GetString(data);
return res;
}
set
{
var data = Encoding.UTF8.GetBytes(value);
var encrypted = ProtectedData.Protect(data, AdditionalEntropy, DataProtectionScope.LocalMachine);
PasswordEncrypted = Convert.ToBase64String(encrypted);
}
(can be made less verbose)

Related

How to signal YamlDotNet to serialize byte[] as !!binary type in .NET 5 (previous answer is not working)

In question YamlDotNet !!binary type the answer provided by Antoine Aubry and the example in A fully working example can be tried here do not work with Version="11.2.1".
Is there any way to add a decorator in object properties or an alternative procedure like the former referenced?.
Our trouble is that byte array properties byte[] serialize in a very long (too many lines) file, and base64 encoding will improve that substantially.
We inspect the source repository at GitHub to find out the new way to do it.
And found in the release log the SerializerBuilder and DeserializerBuilder, so we make changes in the example referenced above (A fully working example can be tried here) and include a fully running project example, but the core changes are these:
To serialize
change:
var serializer = new Serializer();
serializer.RegisterTypeConverter(new ByteArrayConverter());
for:
var serializer = new SerializerBuilder()
.WithTypeConverter(new ByteArrayConverter())
.Build();
To deserialize
change:
var deserializer = new Deserializer();
deserializer.RegisterTagMapping("tag:yaml.org,2002:binary", typeof(byte[]));
deserializer.RegisterTypeConverter(new ByteArrayConverter());
for:
var deserializer = new DeserializerBuilder()
.WithTypeConverter(new ByteArrayConverter())
.WithTagMapping("tag:yaml.org,2002:binary", typeof(byte[]))
.Build();
Full demo example
Main program Program.cs (using a Demo.cs class for testing)
using System;
using System.Linq;
using System.Text.Json;
using YamlDotNet.Serialization;
namespace SerializeByteArrayAsBinary
{
class Program
{
static void Main()
{
var demoToSerialize = new Demo { Data = new byte[40] };
// Serialization
var serializer = new SerializerBuilder()
.WithTypeConverter(new ByteArrayConverter())
.Build();
var demoSerialized = serializer.Serialize(demoToSerialize);
Console.WriteLine($"--- After serialization the {nameof(Demo)} object is:\n{demoSerialized}\n");
// Deserialization
var deserializer = new DeserializerBuilder()
.WithTypeConverter(new ByteArrayConverter())
.WithTagMapping("tag:yaml.org,2002:binary", typeof(byte[]))
.Build();
var demoDeserialized = deserializer.Deserialize<Demo>(demoToDeserialize);
var demoDeserializedJson = JsonSerializer.Serialize(demoDeserialized, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine($"--- After deserialization the {nameof(Demo)} object viewed as JSON is:\n{demoDeserializedJson}");
// Verifications
if (demoToSerialize.Name != demoDeserialized.Name ||
demoToSerialize.Address != demoDeserialized.Address ||
!demoToSerialize.Data.SequenceEqual(demoDeserialized.Data))
throw new Exception($"Fail, {nameof(demoToSerialize)} is different from {nameof(demoDeserialized)}");
if (demoSerialized != demoToDeserialize)
throw new Exception($"Fail, {nameof(demoSerialized)} is different from {nameof(demoToDeserialize)}");
}
const string demoToDeserialize = #"Name: Jhon Doe
Address: 1200 Somewhere St
Data: !!binary AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
";
}
}
Demostration class Demo.cs
using System;
namespace SerializeByteArrayAsBinary
{
/// <summary>
/// Demo class to demonstrate how to serialize and deserialize with YamlDotNet.
/// </summary>
public class Demo
{
public string Name { get; set; } = "Jhon Doe";
public string Address { get; set; } = "1200 Somewhere St";
public byte[] Data { get; set; } = Array.Empty<byte>();
}
}
Original ByteArrayConverter.cs (untouched)
using System;
using YamlDotNet.Core;
using YamlDotNet.Serialization;
namespace SerializeByteArrayAsBinary
{
public class ByteArrayConverter : IYamlTypeConverter
{
public bool Accepts(Type type)
{
return type == typeof(byte[]);
}
public object ReadYaml(IParser parser, Type type)
{
var scalar = (YamlDotNet.Core.Events.Scalar)parser.Current;
var bytes = Convert.FromBase64String(scalar.Value);
parser.MoveNext();
return bytes;
}
public void WriteYaml(IEmitter emitter, object value, Type type)
{
var bytes = (byte[])value;
emitter.Emit(new YamlDotNet.Core.Events.Scalar(
null,
"tag:yaml.org,2002:binary",
Convert.ToBase64String(bytes),
ScalarStyle.Plain,
false,
false
));
}
}
}

C# comfortable way to construct byte array from different objects

I am looking for a way to fast and simple implementation of this paradigm:
MyByteArray mb = new MyByteArray();
mb.Add<byte>(bytevalue);
mb.Add<float>(floatvalue);
mb.Add<string>(str);
mb.Add<MyClass>(object);
And then get byte[] from mb to send it as a byte packet via RPC call (to be decoded on the other side using the same technique).
I've found MemoryStream, but it looks like too overheaded for this simple operation.
Can you help me? Thank you.
What are you looking for is BinaryWritter. But it still needs a Stream to write on for pure logic reason. And the only Stream that fits in your need is MemoryStream.
Are you afraid of performance overhead ? You can create your MemoryStream from an existing byte array ;
byte [] buffer = new byte[1024];
using (var memoryStream = new MemoryStream(buffer))
{
using (var binaryWriter = new BinaryWriter(memoryStream))
{
binaryWriter.Write(1.2F); // float
binaryWriter.Write(1.9D); // double
binaryWriter.Write(1); // integer
binaryWriter.Write("str"); // string
}
}
// buffer is filled with your data now.
A tricky way to achieve this is to use a combination of builtin class in .net
class Program
{
static void Main(string[] args)
{
Program program = new Program();
var listBytes = new List<byte>();
listBytes.Add( program.CastToBytes("test"));
listBytes.Add(program.CastToBytes(5));
}
Note
for a custom object you have to define an implicit operator on how the properties or all the object should be converted
public byte[] CastToBytes<T>(T value)
{
//this will cover most of primitive types
if (typeof(T).IsValueType)
{
return BitConverter.GetBytes((dynamic)value);
}
if (typeof(T) == typeof(string))
{
return Encoding.UTF8.GetBytes((dynamic) value);
}
//for a custom object you have to define the rules
else
{
var formatter = new BinaryFormatter();
var memoryStream = new MemoryStream();
formatter.Serialize(memoryStream, value);
return memoryStream.GetBuffer();
}
}
}
This looks like the case for Protocol Buffers, you could look at at protobuf-net.
First, let's decorate the classes.
[ProtoContract]
class User
{
[ProtoMember(1)]
public int Id { get; set; }
[ProtoMember(2)]
public string Name { get; set; }
}
[ProtoContract]
class Message
{
[ProtoMember(1)]
public byte Type { get; set; }
[ProtoMember(2)]
public float Value { get; set; }
[ProtoMember(3)]
public User Sender { get; set; }
}
Then we create our message.
var msg = new Message
{
Type = 1,
Value = 1.1f,
Sender = new User
{
Id = 8,
Name = "user"
}
};
And now, we can use ProtoBuf's serializer to do all our work.
// memory
using (var mem = new MemoryStream())
{
Serializer.Serialize<Message>(mem, msg);
var bytes = mem.GetBuffer();
}
// file
using (var file = File.Create("message.bin")) Serializer.Serialize<Message>(file, msg);

C# Binary formatter properties have same value

I have hit a small snag when I user the binaryformatter to serialize objects. The whole point for the serialization is so that the values can be passed into a hash function which requires a byte array.
The process I have is, I read the file, using newtonsoft Jsonconvert the json file into a POCO object, do some checks, update values as required and save back to same file in the Json format.
The checks include verifying that the hash value matches from the file to what is regenerated at the beginning of the process. The steps I take are, read file, convert to POCO, serialize using binary formatter, generate hash value, compare both values, if correct, update data and save both the new hash value and object as Json into the file.
However, I have hit a snag when i serialize the object using the binary formatter. If the object has properties where the values are same, the byte output from the serializer is different from when the data is read in from the file to when it is written out. As the values of the byte arrays are different, so are the hash values. Moreover, if the values for the properties are different, then the same hash values are generated and therefore no issues.
My question is why does having the same value causes the byte value to be different when the object is read and written to file.
[Serializable]
public class UserAuthorisationData
{
public string surname { get; set; }
public string forename { get; set; }
public string initials { get; set; }
public UserAuthorisationData()
{
surname = "";
forename = "";
initials = "";
}
}
E.g
var objectA = new UserAuthorisationData()
objectA.surname = "Fred";
objectA.forename = "John";
objectA.initials = "FJ";
var objectB = new UserAuthorisationData()
objectB.surname = "John";
objectB.forename = "John";
objectB.initials = "JJ";
In the example above, the value of the byte array for objectA is the same, when the hash values are generated both during when data is written out and when the file is read back in.
However, for objectB, the values differ.
Method below to convert object to byte:
protected virtual byte[] ObjectToByteArray(object objectToSerialize)
{
BinaryFormatter formatter = new BinaryFormatter();
MemoryStream fs = new MemoryStream();
try
{
lock (locker)
{
formatter.Serialize(fs, objectToSerialize);
Logger.Debug($"Successfully converted object to bytes for serialization.");
}
File.WriteAllBytes(#"C:\ali.kamal\User1.txt", fs.ToArray());
return fs.ToArray();
}
}
Call the method on the object
ObjectToByteArray(objectA);
ObjectToByteArray(objectB);
Update 1
Thanks Hadi. The hash code is generated using Microsoft's HMACSHA256.computeHash method.
protected override string ComputeHash(byte[] objectAsBytes, byte[] key)
{
byte[] hashValue = null;
try
{
using (HMACSHA256 hmac = new HMACSHA256(key))
{
hashValue = hmac.ComputeHash(objectAsBytes);
}
}catch(Exception ex)
{
EventLogger.LogEvent($"Could not generate SHA256 hash value, exception:{ex.Message}", EventEntryType.Error);
}
return Convert.ToBase64String(hashValue);
}
e.g.
string hashvalue = ComputeHash(ObjectToByteArray(objectA), ObjectToByteArray("abcd"))
I would suggest you to compare the bytes in different way rather than the Hash code method: Array1.SequenceEqual(Array2), Its not clear how you are comparing the Hash Code of both arrays in your question, but I will assume that you are using GetHashCode method, using Array1.GetHashCode() == Array2.GetHashCode() is not suitable for your case unless you override the HasCode function. Below is the sample using a Console application for using the SequenceEqual method:
using System;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var a = new UserData();
var b = new UserData();
var aS = ObjectToByte(a);
var bS = ObjectToByte(b);
Console.WriteLine("A : {0}", a);
Console.WriteLine("B : {0}", b);
// result for empty objects are equal
Console.WriteLine("A == B ? => {0} \n\n", aS.SequenceEqual(bS));
a = new UserData()
{
ForeName = "A",
Initials = "CC",
SurName = "B",
};
b = new UserData()
{
ForeName = "AX",
Initials = "CC",
SurName = "B",
};
aS = ObjectToByte(a);
bS = ObjectToByte(b);
Console.WriteLine("A : {0}", a);
Console.WriteLine("B : {0}", b);
// result for same data type with same object values are equal
Console.WriteLine("A == B ? => {0} \n\n", aS.SequenceEqual(bS));
a = new UserData()
{
ForeName = "AX",
Initials = "CC",
SurName = "B",
};
b = a;
aS = ObjectToByte(a);
bS = ObjectToByte(b);
Console.WriteLine("A : {0}", a);
Console.WriteLine("B : {0}", b);
// result for different objects are not equal
Console.WriteLine("A == B ? => {0} \n\n", aS.SequenceEqual(bS));
}
static byte[] ObjectToByte(object item)
{
var formatter = new BinaryFormatter();
using (var memory = new MemoryStream())
{
formatter.Serialize(memory, item);
return memory.ToArray();
}
}
}
[Serializable]
public class UserData
{
public string SurName { get; set; }
public string ForeName { get; set; }
public string Initials { get; set; }
public override string ToString()
{
return string.Format("{{SurName: {0} , ForeName:{1}, Initials:{2}}}", SurName ?? "Empty", ForeName ?? "Empty", Initials ?? "Empty");
}
}
}
Here a working Demo
Hope this will help you

How to deal with Hashtable property of entity in database using EntityFramework

I have old project which used ADO.NET to access the persistent store. Currently, I want to migrate it to EF (6.1.3, if it matters), in order to support several DB providers with minimal code duplicate.
There is an entity, which contains Hashtable property:
public class Record
{
...
public Hashtable data { get; set; }
}
With ADO.NET, the BinaryFormatter was used to convert this data property to the BLOB, and vice versa:
using (MemoryStream stream = new MemoryStream())
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, data);
result = stream.GetBuffer();
}
//----------
using (MemoryStream serializationStream = new MemoryStream((byte[])value))
{
BinaryFormatter formatter = new BinaryFormatter();
result = (Hashtable)formatter.Deserialize(serializationStream);
}
Now I need to tell EF how it should store and retrieve that property.
What have I tried
I could store one more property in the entity:
public class Record
{
public byte[] dataRaw { get; set; }
[NotMapped]
public Hashtable data {
get {/*deserialize dataRaw */ }
set { /*Serialize to dataRaw*/}
}
}
But this solution is prone to errors, and special workflow with that property must be followed.
P.S. Actually this question is not about the Hashtable only, but about every custom class which must be stored and retrived in a special way.
Here is a complete solution based on the answer I mentioned above.
I have tested it in linqpad, and it works rather well.
You don't need a special workflow, as the property accessors take care of saving and loading the hash table when needed.
Main Method
void Main()
{
using (var ctx = new TestContext())
{
var hash = new Hashtable();
hash.Add("A", "A");
ctx.Settings.Add(new Settings { Hash = hash });
ctx.SaveChanges();
// load them up...
ctx.Settings.ToArray().Select(_ => _.Hash).Dump();
}
}
Settings Class
public class Settings
{
// a primary key is necessary.
public int Id { get; set; }
[NotMapped]
public Hashtable Hash
{
get;
set;
}
// the backing field can be protected, this helps 'hide' it.
protected virtual byte[] _Hash
{
get
{
return Hash.ToBinary();
}
set
{
Hash = value.FromBinary<Hashtable>();
}
}
}
Extensions to Convert the Values
public static class Extensions
{
public static BinaryPropertyConfiguration BinaryProperty<T>(
this EntityTypeConfiguration<T> mapper,
String propertyName) where T : class
{
Type type = typeof(T);
ParameterExpression arg = Expression.Parameter(type, "x");
Expression expr = arg;
PropertyInfo pi = type.GetProperty(propertyName,
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
expr = Expression.Property(expr, pi);
LambdaExpression lambda = Expression.Lambda(expr, arg);
Expression<Func<T, byte[]>> expression = (Expression<Func<T, byte[]>>)lambda;
return mapper.Property(expression);
}
public static byte[] ToBinary<T>(this T instance)
{
if (instance == null)
return null;
using (var stream = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, instance);
return stream.ToArray();
}
}
public static T FromBinary<T>(this byte[] buffer)
{
if (buffer == null)
return default(T);
using (var stream = new MemoryStream(buffer, false))
{
var formatter = new BinaryFormatter();
var instance = formatter.Deserialize(stream);
return (T)instance;
}
}
}
Data Context
public class TestContext : DbContext
{
public DbSet<Settings> Settings { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder
.Entity<Settings>()
.BinaryProperty("_Hash")
.HasColumnName("Hashtable");
}
}

Transmitting complex objects using TCP

I have a client server application in which I need to transmit a user defined object from Client to Server using TCP connection. My object is of the following structure:
class Conversation
{
private string convName, convOwner;
public ArrayList convUsers;
public string getConvName()
{
return this.convName;
}
public string getConvOwner()
{
return this.convOwner;
}
}
Please help me how to transmit this object at from client and again de-serialize it into appropriate object at server side.
As answered, you should make your object serializable. Once you did that with the Serializable attribute, you can use the famous BinaryFormatter to convert your object into a byte array.
You can find many examples out there for using the BinaryFormatter, just use your favorite search engine. Here's a short example:
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
public class SerializationUtils
{
public static byte[] SerializeToByteArray(object request)
{
byte[] result;
BinaryFormatter serializer = new BinaryFormatter();
using (MemoryStream memStream = new MemoryStream())
{
serializer.Serialize(memStream, request);
result = memStream.GetBuffer();
}
return result;
}
public static T DeserializeFromByteArray<T>(byte[] buffer)
{
BinaryFormatter deserializer = new BinaryFormatter();
using (MemoryStream memStream = new MemoryStream(buffer))
{
object newobj = deserializer.Deserialize(memStream);
return (T)newobj;
}
}
}
As for your class, it includes two private fields. I can't see where you set values for them, so I changed your code a bit, so that they can be set in the constructor. In addition, I added the needed Serializable attribute:
using System;
using System.Collections;
[Serializable]
public class Conversation
{
public Conversation(string convName, string convOwner)
{
this.convName = convName;
this.convOwner = convOwner;
}
public Conversation()
{
}
private string convName, convOwner;
public ArrayList convUsers;
public string getConvName()
{
return this.convName;
}
public string getConvOwner()
{
return this.convOwner;
}
}
Now let's put it all together, and see your class serialized and then deserialized, in a Console Application:
using System;
using System.Collections;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace Capishi
{
[Serializable]
public class Conversation
{
public Conversation(string convName, string convOwner)
{
this.convName = convName;
this.convOwner = convOwner;
}
public Conversation()
{
}
private string convName, convOwner;
public ArrayList convUsers;
public string getConvName()
{
return this.convName;
}
public string getConvOwner()
{
return this.convOwner;
}
}
public class SerializationUtils
{
public static byte[] SerializeToByteArray(object request)
{
byte[] result;
BinaryFormatter serializer = new BinaryFormatter();
using (MemoryStream memStream = new MemoryStream())
{
serializer.Serialize(memStream, request);
result = memStream.GetBuffer();
}
return result;
}
public static T DeserializeFromByteArray<T>(byte[] buffer)
{
BinaryFormatter deserializer = new BinaryFormatter();
using (MemoryStream memStream = new MemoryStream(buffer))
{
object newobj = deserializer.Deserialize(memStream);
return (T)newobj;
}
}
}
class Program
{
static void Main(string[] args)
{
// create and initialize a conversation object
var convName = "Capishi";
var convOwner = "Ice Cream";
Conversation myConversation = new Conversation(convName, convOwner);
myConversation.convUsers = new ArrayList();
myConversation.convUsers.Add("Ron Klein");
myConversation.convUsers.Add("Rakesh K");
// serialize to a byte array
byte[] data = SerializationUtils.SerializeToByteArray(myConversation);
// print the resulting byte array if you want
// PrintArray(data);
// deserialize the object (on the other side of the communication
Conversation otherConversation = SerializationUtils.DeserializeFromByteArray<Conversation>(data);
// let's see if all of the members are really there
Console.WriteLine("*** start output ***");
Console.WriteLine("otherConversation.getConvName() = " + otherConversation.getConvName());
Console.WriteLine("otherConversation.getConvOwner() = " + otherConversation.getConvOwner());
Console.WriteLine("otherConversation.convUsers:");
foreach (object item in otherConversation.convUsers)
{
Console.WriteLine(item);
}
Console.WriteLine("*** done output ***");
// wait before close
Console.ReadLine();
}
/// <summary>
/// just a helper function to dump an array to the console's output
/// </summary>
/// <param name="data"></param>
private static void PrintArray(byte[] data)
{
for (int i = 0; i < data.Length; i++)
{
Console.Write("{0:000}", data[i]);
if (i < data.Length - 1)
Console.Write(", ");
}
Console.WriteLine();
}
}
}
The result is:
*** start output ***
otherConversation.getConvName() = Capishi
otherConversation.getConvOwner() = Ice Cream
otherConversation.convUsers:
Ron Klein
Rakesh K
*** done output ***
And a final note:
I'd use the generic List instead of the outdated ArrayList, unless you're bound to .NET 1.*.
One good course of action is to expose this object as a DataContract to a framework like WCF, and use the appropriate transports available in that framework.
For example:
[DataContract]
class Conversation
{
private string convName, convOwner;
public ArrayList convUsers;
[DataMember]
public string ConvName
{
get { return this.convName; }
}
[DataMember]
public string ConvOwner
{
get { return this.convOwner; }
}
}
You need to make your object serializable.

Categories

Resources