65.9K
CodeProject 正在变化。 阅读更多。
Home

在 Entity Framework 中读取 sql_variant

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (10投票s)

2010年12月18日

CPOL

3分钟阅读

viewsIcon

97124

downloadIcon

842

处理 Entity Framework 排除 sql_variant 的方法

引言

尽管 Entity Framework 不支持内置的 SQL Server sql_variant 数据类型 [1][2],但可以通过变通方法来处理。本文介绍了一种方法,可让您使用 Entity Framework EDM 读取 sql_variant 类型的列。

背景

我们需要一个只读访问数据库的方式,该数据库在一个使用 Entity Framework 的应用程序中相对大量地使用了 sql_variant。不幸的是,我们很快发现它不支持此数据类型的列:所有 sql_variant 列都被排除在向导生成的 EDM 之外,并带有烦人的警告。幸运的是,Entity Framework 提供了两个可以结合使用的功能来读取 sql_variant复杂类型DefiningQuery。此实现将 sql_variant 转换为 varbinary,并将二进制表示转换为对象,这需要一套冗长但直观的规则。

将 sql_variant 添加到您的模型

以下步骤假设您正在使用 Visual Studio,并且您的数据库表已存在,并且您已导入了一个 EDM 文件,其中仅缺少 sql_variant 列。这些步骤还假定您对 Entity Framework 的概念和工具有基本的了解。

  1. 模式浏览器 中,添加一个名为 SqlVariant 的新复杂类型,它有两个属性:一个名为 BaseTypeprivate string 属性,以及一个名为 Representationprivate Binary 属性。
  2. 在 EDM 编辑器中,为 EDM 向导跳过的每个 sql_variant 字段添加一个类型为 SqlVariant 的只读复杂属性。根据您采用的命名约定命名这些属性,以匹配它们在数据库中对应列的名称。
  3. 在 XML 编辑器中打开您的 EDM 文件,并为包含 sql_variant 列的每个表上的实体集添加一个 DefiningQuery 标签。除了列出您表的所有“常规”列之外,该查询还需要为每个 sql_variant 列包含一对表达式
    cast([mytable].[mycolumn] as varbinary) as [RepMyColumn]
    sql_variant_property([mytable].[mycolumn], 'BaseType') as [BaseTypeMyColumn].
  4. 将 "mytable" EntitySetSchema="dbo" 属性替换为 store:Name="mytable"
  5. 为每个 sql_variant 列在每个实体中添加两个列。称它们为 RepMyColumn BaseTypeMyColumn,其中 MyColumn 是您的 sql_variant 列的名称。
  6. 切换回 EDM 编辑器,并按如下方式映射 SqlVariant 属性:BaseType 映射到 BaseTypeMyColumnRepresentation 映射到 RepMyColumn
  7. 下载本文附带的源文件,并将其命名空间更改为与您的项目匹配。这将为 SqlVariant 类型添加一个名为 'Converted' 的 object 类型属性。使用此列来访问您的 sql_variant 的值。

实现

该实现的概要描述可以浓缩为一句话:它是一个基于 sql_variant 的基本类型进行 switch 语句判断,然后根据二进制表示构造 .NET 对象。

public object Converted {
    get {
        if (Representation == null || BaseType == null) {
            return null;
        }
        switch (BaseType) {
            case "uniqueidentifier":
                return new Guid(Representation);
            case "char":
            case "varchar":
                return GetString(Representation);
            case "nvarchar":
            case "nchar": 
                return GetNlString(Representation);
            // More cases covering other base types
            ...
        }
        throw new InvalidOperationException("Unsupported SQL type: '" + BaseType + "'");
    }
}

考虑到本文提供的实现支持十五种基本类型,因此在各个转换器中编写了大量细节并不令人意外。但是,所有转换器都遵循相同的基本模式:首先,它们检查字节数组是否具有预期的长度,然后使用 BitConverter 来构造结果的元素,最后构造结果。将 nvarchar string 转换为 .NET 字符串的代码很好地说明了各个转换器的工作原理。

private static string GetNlString(byte[] src) {
    if (src.Length % 2 != 0) {
        throw new InvalidOperationException("NLS format is invalid.");
    }
    var buf = new char[src.Length / 2];
    for (var pos = 0 ; pos != src.Length ; pos += 2) {
        buf[pos/2] = BitConverter.ToChar(src, pos);
    }
    return new string(buf);
}

Using the Code

您现在可以通过访问您在步骤 2 中添加的复杂属性的 Converted 属性来读取 SqlVariant 列中的数据。

foreach (var x in myEdm.myTableWithSqlVariantDatas) {
    var obj = x.MySqlVariantColumn.Converted;
    ...
}

限制

  • 该代码缺少对某些 SQL Server 基本类型的支持。但是,您可以遵循实现的一般模式来添加您感兴趣的类型。
  • 由于此实现仅提供对 sql_variant 列的只读访问,因此您既不能写入这些列,也不能对它们的值运行数据库查询。
  • 该实现“理解”的二进制格式高度依赖于 SQL Server 2008,因此在未来版本的数据库中可能无法正常工作。但是,我希望 Entity Framework 团队能在 SQL Server 团队发布二进制不兼容版本之前为 sql_variant 添加原生支持 :-)
  • 毋庸置疑,本文提供的代码可能存在 bug。它被提供为实现起点,而非即插即用库。
© . All rights reserved.