2016-09-15 55 views
6

我有一個使用ADO.NET訪問持久存儲的舊項目。目前,我想將它遷移到EF(6.1.3,如果它很重要),以支持幾個數據庫提供者,並且代碼重複最少。如何使用EntityFramework處理數據庫中實體的Hashtable屬性

有一個實體,它包含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.實際上這個問題不僅僅是關於Hashtable,而是關於每個必須以特殊方式存儲和回溯的自定義類。

+0

老實說,我不認爲這是可能的。我不認爲有任何鉤子可以使值強制進出sql原語。我急切地看着這個問題,看看是否有任何答案接近。 – Jim

+0

還有一個類似的問題http://stackoverflow.com/questions/16135642/in-entity-framework-is-there-a-cleaner-way-of-converting-an-object-type-to-a-str,它使用像你的支持領域。我不認爲有另一種方式。 – Jim

回答

5

這是一個基於我上面提到的answer的完整解決方案。
我已經在linqpad中測試過了,它工作的很好。

您不需要特殊的工作流程,因爲屬性訪問器負責在需要時保存和加載散列表。

主要方法

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

設置類

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

我想知道是否_Hash將與實體框架 – Monah

+0

@hadi是一起工作,它的工作原理,我已經嘗試過,將它保存到varbinary(max) - 這是生成的,並將其加載回來 - 沒有問題。 – Jim

+0

@Jim,這是更優雅的解決方案。 – stukselbax

相關問題