简体   繁体   English

如何使用EntityFramework处理数据库中实体的Hashtable属性

[英]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. 我有一个使用ADO.NET访问持久存储的旧项目。 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. 目前,我想将其迁移到EF(6.1.3,如果重要的话),以便支持几个数据库提供程序,并且代码重复最少。

There is an entity, which contains Hashtable property: 有一个实体,其中包含Hashtable属性:

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: 使用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);
}

Now I need to tell EF how it should store and retrieve that property. 现在我需要告诉EF它应该如何存储和检索该属性。

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. 但是这种解决方案容易出错,必须遵循具有该属性的特殊工作流程。

PS Actually this question is not about the Hashtable only, but about every custom class which must be stored and retrived in a special way. PS实际上这个问题不是关于Hashtable,而是关于每个必须以特殊方式存储和重新检索的自定义类。

Here is a complete solution based on the answer I mentioned above. 这是基于我上面提到的答案的完整解决方案。
I have tested it in linqpad, and it works rather well. 我已经在linqpad中测试了它,它运行得相当好。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM