How to deal with a Hashtable property of an object in a database using EntityFramework

I have an old project that used ADO.NET to access persistent storage. Currently, I want to migrate it to EF (6.1.3, if that matters) in order to support multiple database providers with minimal code duplication.

There is an object that contains a Hashtable property:

 public class Record { ... public Hashtable data { get; set; } } 

With ADO.NET, BinaryFormatter used to convert this data property to 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 this property.

What i tried

I could save another property in essence:

 public class Record { public byte[] dataRaw { get; set; } [NotMapped] public Hashtable data { get {/*deserialize dataRaw */ } set { /*Serialize to dataRaw*/} } } 

But this solution is error prone, and you must follow a special workflow with this property.

PS Actually, this question concerns not only the Hashtable, but also each user class that must be saved and retrieved in a special way.

+6
source share
1 answer

Here is a complete solution based on the answer I mentioned above.
I tested it on linqpad and it works pretty well.

You do not need a special workflow, as property owners will take care of saving and loading the hash table if necessary.

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 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"); } } 
+5
source

All Articles