如何将Linq中的自定义类型映射到Sql?

时间:2009-07-08 09:39:29

标签: c# linq-to-sql

我有一个Customer类,它包含一个属性MyProperty,它是一个自定义类型MyCustomType。我想将数据库中的属性值保存为文本。在设计器中,我将Type设置为'MyType',将Server Data Type设置为'varchar(10)'。当我构建项目时,我收到以下错误:

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

现在有意义,因为Linq to Sql无法知道如何转换我的自定义类型。所以我假设我必须在MyCustomType上实现某种Parse(string)和ToString()方法,但我找不到任何关于此的文档。

那么,如何将Linq中的自定义类型映射到Sql?

3 个答案:

答案 0 :(得分:4)

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属性)添加到部分类中。

答案 1 :(得分:4)

扩展上面的私有/公共数据访问模式,我做了以下内容。

1)选择要映射自定义类型的DB数据类型。在我的例子中,自定义对象实现了类型安全的枚举模式,因此存在自定义类型(常量枚举)和数据库类型整数类型(在我的情况下为tinyint)的自然映射。我可以将基础数据库类型看作字符串,然后使用JavaScriptSerializer类进行第2步。

2)在您的类中实现在自定义类型和DB数据类型之间进行转换的能力。转换必须是双向的:CustomObject - > DB类型和DBType - > CustomObject 对我而言,它正在实施:

2a)枚举类型的序数值作为我的类型安全枚举类的成员。这会处理从我的自定义类型转换为int。

2b)一个静态成员findByOrdinal(),它处理从int到我的自定义类型的强制转换。

3)使用Linq To SQL在数据列,此自定义数据类型和公共名称之间创建链接。这是通过将自定义对象的名称粘贴到数据成员属性的“类型”组合框中来完成的。

4)清理并重建项目以确保编译清洁。如果构建过程抱怨在< ProjectName> .dbml文件下面的< ProjectName> .designer.cs文件的上下文中不存在自定义类型。下面讨论再生问题。如果在DB上下文中执行代码,则会出现上面的转换错误。

Linq to SQL在创建.designer.cs文件时创建了许多部分类文件。我们需要添加这个部分类代码,当Linq to SQL重新生成< ProjectName> .designer.cs中的代码时,这些代码不会被销毁。

5)创建一个新的类文件,其中包含由Linq to SQL创建的分部类的源代码。为此,请在Solution Explore中右键单击< ProjectName> .dbml,然后从上下文菜单中选择View Code。这将在项目中创建一个新文件< ProjectName> .cs,继续定义在< ProjectName> .designer.cs

中找到的分部类

6)从< ProjectName> .designer.cs中剪切选定的代码,然后将代码粘贴到< ProjectName> .cs

SQL链接在< ProjectName> .designer.cs中创建了一个将表映射到类的部分类。

我们需要删除找到的数据访问代码< ProjectName> .designer.cs,并在< ProjectName> .cs。

中创建我们自己的此数据访问版本

我通过对Linq生成的代码进行直接剪切和粘贴,并在代码位于< ProjectName> .cs后将代码更改为我的目标。剪切,粘贴,变异操作是:

6a)将数据存储类型从MyCustomType更改为可轻松映射到数据库中该列的DB数据类型的内容。在我的例子中,我选择int作为来自DB列的tinyint的内部数据存储。

6b)将Linq创建的公开命名数据成员的getter和setter方法改为SQL。

有关原始Linq代码和最终变异版本的详细信息,请参阅下面的代码。

7)从< ProjectName> .designer.cs中删除步骤4中添加的using语句,以使编译器暂时接受Ling-Generated代码中自定义类型的存在。

8)清理并重建项目以确保编译清洁。 .designer.cs中的任何内容都不应该依赖于对定义自定义类的程序集的引用。这样,Linq to SQL可以随意生成< ProjectName> .designer.cs,这不会影响.cs中的代码。

注意:映射到数据库表的C#类通常完全由Linq to SQL代码生成器控制。通过在两个源文件之间拆分此类的定义,现在必须手动协调两个文件中的源代码。这意味着这些源文件之间的长期维护技术债务已被接受。

9)测试界面。 Getter / Setter代码将我的自定义类型映射到底层C#数据存储(以及从那里到DB表上的DB列)。因此,使用数据的代码可以依赖于C#类的强类型。

上述步骤更为复杂,并不是立即明白的。我之所以这样做是因为规则第一,使用了visual studio向导(al代码生成向导):

不要与巫师作战。

Linq to SQL代码生成器代表我生成了大量代码。这种方法让我声称只拥有C#/ Linq / SQL / DBColum代码链中的那些小块,它们处理的是我的自定义类型的C#类。如果重新生成Linq代码,则编译器将抛出编译错误,但此时代码维护是删除Linq to 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();
                }
            }
        }
    }
}

答案 2 :(得分:1)

正确的方法(来自MSDN)是:

  

如果类实现了Parse()和ToString(),则可以将对象映射到任何SQL文本类型(CHAR,NCHAR,VARCHAR,NVARCHAR,TEXT,NTEXT,XML)。通过将ToString()返回的值发送到映射的数据库列,该对象存储在数据库中。通过在数据库返回的字符串上调用Parse()来重构对象。