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");
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With