2009-07-08 138 views
2

我有一個Customer類,它包含一個屬性MyProperty,它是一個自定義類型MyCustomType。我想將文本中的屬性值保存在數據庫中。在設計器中,我將Type設置爲'MyType',將服務器數據類型設置爲'varchar(10)'。當我建立的項目,我得到以下錯誤:如何將Linq中的自定義類型映射到Sql?

DBML1005: Mapping between DbType 'varchar(10)' and Type 'MyType' in 
Column 'MyProperty' of Type 'Customer' is not supported. 

現在意義,因爲LINQ到SQL沒有知道如何我自定義類型轉換的方式。所以我假設我必須在MyCustomType上實現某種類型的Parse(string)和ToString()方法,但是我找不到任何文檔。

那麼,如何將Linq中的自定義類型映射到Sql?

回答

3

一個類到一個varchar?我不知道任何支持LINQ to SQL的功能。你最好的選擇可能是一個簡單的屬性(如果需要也可以是私有的):

[Column(Name="ColumnName", DbType="varchar(10) NULL", CanBeNull=true)] 
private string MyPropertyString { 
    get { /* serialize MyProperty yourself */ } 
    set { /* deserialize MyProperty yourself */ } 
} 
public MyCustomType MyProperty {get;set;} 

(即讀取或更新您的實際MyProperty屬性)

我也希望你不得不離開這關閉設計師,並將其自己(和MyProperty屬性)添加到部分類中。

+0

一個好的變通,實現了我所需要的。 – 2009-07-09 09:37:02

+0

感謝您讓我遠離複雜的解決方法。這是完美的 – 2012-02-19 16:40:40

3

擴展上面的私人/公共數據訪問模式我做了以下。

1)選擇要將自定義類型映射到的DB數據類型。在我的情況下,自定義對象實現了類型安全的枚舉模式,所以自定義類型(常量枚舉)和數據庫類型整型(在我的情況下是tinyint)有一個自然映射。我可以看到底層數據庫類型爲字符串,然後使用JavaScriptSerializer Class進行步驟2.

2)在您的類中實現在自定義類型和DB數據類型之間進行轉換的功能。轉換必須是雙向的:CustomObject - > DB型和DBTYPE - > CustomObject 對我來說是實現:

2A)的枚舉類型爲我喜歡的類型安全的枚舉成員的順序值類。這將處理從我的自定義類型轉換爲int。

2b)靜態成員findByOrdinal(),它處理從int到我自定義類型的強制轉換。

3)使用Linq To SQL創建數據列,此自定義數據類型和公用名之間的鏈接。這是通過將自定義對象的名稱粘貼到數據成員屬性的類型組合框中來完成的。

4)清理並重建項目以確保編譯是乾淨的。如果構建過程中抱怨<項目名稱> .designer.cs文件在<項目名稱> .dbml文件下發現該自定義類型不存在。再生問題在下面討論。如果您在數據庫上下文中執行代碼,您將會看到上面的轉換錯誤。

Linq to SQL在創建.designer.cs文件時創建了大量的部分類文件。當Linq to SQL重新生成<ProjectName> .designer.cs中的代碼時,我們需要添加到此部分類代碼中,這些代碼不會被銷燬。

5)創建一個新的類文件,其中包含由Linq to SQL創建的部分類的源代碼。爲此,請右鍵單擊Solution Explore中的<ProjectName> .dbml,然後從上下文菜單中選擇查看代碼。這將創建一個新文件,<項目名>的.cs,它繼續在<項目名>了.Designer.cs

發現的部分類的定義中的項目內

6)切割選自<項目名>了.Designer.cs代碼並將代碼粘貼到<項目名>的.cs

鏈接到SQL已經內<項目名>了.Designer.cs創建了一個表映射到一個類的分部類。

我們需要刪除的數據訪問代碼中發現<項目名>了.Designer.cs和內<項目名>的.cs創建我們自己的這個數據訪問版本。

我是通過對Linq生成的代碼進行直剪和粘貼,並在代碼位於<ProjectName> .cs中時將代碼變爲我的位置來實現的。剪切,粘貼,變異操作如下:

6a)將數據存儲類型從MyCustomType更改爲可輕鬆映射到數據庫中該列的DB數據類型的內容。在我的情況下,我選擇int作爲來自DB列的tinyint的內部數據存儲。

6b)將Linq創建的公共命名數據成員的getter和setter方法更改爲SQL。

有關原始Linq代碼和最終變種版本的詳細信息,請參閱以下代碼。

7)從<中刪除ProjectName > .designer.cs在步驟4中添加的使用語句,以便讓編譯器臨時接受在生成代碼中存在自定義類型。

8)清理並重建項目以確保編譯清潔。 .designer.cs中的任何內容都不應該依賴於定義自定義類的程序集引用。通過這種方式,Linq to SQL可以隨意生成<ProjectName> .designer.cs,這不會影響在.cs中找到的代碼。

注意:映射到數據庫表的C#類通常處於Linq到SQL代碼生成器的完全控制之下。通過在兩個源文件之間拆分這個類的定義,並且現在必須手動協調這兩個文件中的源代碼。這意味着這些源文件之間長期維護的技術債務已被接受。

9)測試接口。 Getter/Setter代碼將我的自定義類型映射到底層C#數據存儲(並從此到數據庫表的數據庫列)。正因爲如此,使用數據的代碼可以依賴於C#類的強大類型。

上述步驟更多涉及,並不立即obvvious。我做了上述,因爲規則第一與視覺工作室嚮導(代碼生成嚮導的事情):

不要與嚮導戰鬥。

Linq to SQL代碼生成器正在爲我生成大量代碼。這種方法讓我只能聲明C#/ Linq/SQL/DBColum代碼鏈中那些處理C#類的小塊,這些C#類是我的自定義類型。如果Linq代碼被重新生成,那麼編譯器會拋出編譯錯誤,但此時代碼維護將刪除Linq到SQL生成的代碼,該代碼現在是我的代碼<ProjectName> .cs中的代碼的副本。

不理想,但應該長期工作。

生成的代碼是:

namespace TestRepository 
{ 
    using System; 
    using Framework; // Contains definition of the TestPropertyDataType class 

    /* 
    ******************************************************************************************************* 
    * See: http://stackoverflow.com/questions/1097090/how-do-i-map-custom-types-in-linq-to-sql 
    * 
    * Expanding on the private/public interface mentioned here and using the fact that Linq auto generates a 
    * public/private pairing with the getter/setter code, I mutated the Linq gneerated code to 
    * quietly convert from type System.Byte to the custom type, TestPropertyDataType. 
    * 
    * Original code as generated by Linq to SQL which I lifted from the file: TestRepository.designer.cs 
    * ***************************************************************************************************** 
    * [global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.TEST_ACTION_PROPERTIES")] 
    * public partial class TestActionPropertyMetadata : INotifyPropertyChanging, INotifyPropertyChanged 
    * { 
    *  // <Lots of Linq-generated code> 
    *   
    *  private TestPropertyDataType _DataType; 
    * 
    *  // more Linq-generated code> 
    *  
    *  #region Extensibility Method Definitions 
    *  partial void OnDataTypeChanging(TestPropertyDataType value); 
    *  #endregion 
    *  
    *  // <Lots more Linq-generated code> 
    * 
    *  [global::System.Data.Linq.Mapping.ColumnAttribute(Name="DATA_TYPE", Storage="_DataType", DbType="TinyInt NOT NULL", CanBeNull=false)] 
    *  public TestPropertyDataType DataType 
    *  { 
    *   get 
    *   { 
    *    return this._DataType; 
    *   } 
    *   set 
    *   { 
    *    if ((this._DataType != value)) 
    *    { 
    *     this.OnDataTypeChanging(value); 
    *     this.SendPropertyChanging(); 
    *     this._DataType = value; 
    *     this.SendPropertyChanged("DataType"); 
    *     this.OnDataTypeChanged(); 
    *    } 
    *   } 
    *  } 
    * } 
    * ***************************************************************************************************** 
    */ 
    public partial class TestActionPropertyMetadata 
    { 
     private byte _DataType; 
     partial void OnDataTypeChanging(TestPropertyDataType value); 

     [global::System.Data.Linq.Mapping.ColumnAttribute(Name = "DATA_TYPE", Storage = "_DataType", DbType = "TinyInt NOT NULL", CanBeNull = false)] 
     public TestPropertyDataType DataType 
     { 
      get 
      { 
       return TestPropertyDataType.findByOrdinal(this._DataType); 
      } 
      set 
      { 
       if ((this.DataType != value)) 
       { 
        this.OnDataTypeChanging(value); 
        this.SendPropertyChanging(); 
        this._DataType = (byte)value.Ordinal; 
        this.SendPropertyChanged("DataType"); 
        this.OnDataTypeChanged(); 
       } 
      } 
     } 
    } 
} 
1

正確的做法(從MSDN)是:

If a class implements Parse() and ToString(), you can map the object to any SQL text type (CHAR, NCHAR, VARCHAR, NVARCHAR, TEXT, NTEXT, XML). The object is stored in the database by sending the value returned by ToString() to the mapped database column. The object is reconstructed by invoking Parse() on the string returned by the database.

相關問題