如何使用 EntityFramework 处理数据库中实体的哈希表 属性
How to deal with Hashtable property of entity in database using EntityFramework
我有一个旧项目使用 ADO.NET 访问持久存储。目前,我想将它迁移到 EF(6.1.3,如果重要的话),以便以最少的代码重复支持多个数据库提供程序。
有一个实体,其中包含 Hashtable
属性:
public class Record
{
...
public Hashtable data { get; set; }
}
使用 ADO.NET,BinaryFormatter
用于将此 data
属性 转换为 BLOB,反之亦然:
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);
}
现在我需要告诉 EF 它应该如何存储和检索 属性。
我尝试了什么
我可以在实体中再存储一个 属性:
public class Record
{
public byte[] dataRaw { get; set; }
[NotMapped]
public Hashtable data {
get {/*deserialize dataRaw */ }
set { /*Serialize to dataRaw*/}
}
}
但是这个解决方案容易出错,必须遵循属性的特殊工作流程。
P.S. 其实这个问题不仅仅是关于哈希表,而是关于每个必须以特殊方式存储和检索的自定义 class .
这是基于我上面提到的 answer 的完整解决方案。
我已经在 linqpad 中对其进行了测试,并且效果很好。
您不需要特殊的工作流程,因为 属性 访问器会在需要时负责保存和加载哈希 table。
主要方法
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();
}
}
设置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>();
}
}
}
转换值的扩展
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;
}
}
}
数据上下文
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");
}
}
我有一个旧项目使用 ADO.NET 访问持久存储。目前,我想将它迁移到 EF(6.1.3,如果重要的话),以便以最少的代码重复支持多个数据库提供程序。
有一个实体,其中包含 Hashtable
属性:
public class Record
{
...
public Hashtable data { get; set; }
}
使用 ADO.NET,BinaryFormatter
用于将此 data
属性 转换为 BLOB,反之亦然:
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);
}
现在我需要告诉 EF 它应该如何存储和检索 属性。
我尝试了什么
我可以在实体中再存储一个 属性:
public class Record
{
public byte[] dataRaw { get; set; }
[NotMapped]
public Hashtable data {
get {/*deserialize dataRaw */ }
set { /*Serialize to dataRaw*/}
}
}
但是这个解决方案容易出错,必须遵循属性的特殊工作流程。
P.S. 其实这个问题不仅仅是关于哈希表,而是关于每个必须以特殊方式存储和检索的自定义 class .
这是基于我上面提到的 answer 的完整解决方案。
我已经在 linqpad 中对其进行了测试,并且效果很好。
您不需要特殊的工作流程,因为 属性 访问器会在需要时负责保存和加载哈希 table。
主要方法
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();
}
}
设置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>();
}
}
}
转换值的扩展
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;
}
}
}
数据上下文
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");
}
}