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

使用 LINQ ColumnAttribute 从数据库中获取字段长度

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.40/5 (11投票s)

2008年7月1日

CPOL

3分钟阅读

viewsIcon

77734

反射 LINQ 属性可以获取有关数据库字段长度的信息

引言

使用 LINQ,现在可以非常容易地在 SQL Server Management Studio 等交互式工具中设计数据库,将表拖到 Visual Studio 的 DBML 中,然后开始处理为你创建的所有类和关系。

这非常有效,并确保关于数据持久化的“一个事实,一个地点”:你无需维护数据层 *和* 数据库,并且努力保持它们同步。但是,当涉及到数据库中有关列的元数据时,到目前为止,你必须在两个(或更多)地方维护该信息。例如,UI 中文本字段的长度应限制为存储在数据库中的列的长度。

今天,你可能在数据库中定义了长度,并且在 UI 中定义了长度。你可能还在某些在存储数据时会截断数据的代码中定义了长度。在数据库中更改它,你必须去其他地方更改它。

这篇简短的文章向你展示了如何从 LINQ 对象的属性中获取列元数据,允许你拥有一个单一的主数据源(数据库),它定义了每个属性允许的长度。现在,你的 UI、你的业务层、你的数据层和你的数据库可以始终保持同步。

注释

自动截断数据很少是正确的做法;通常你只会使用这里介绍的两种方法中的第一种来获取长度限制,然后通过你的业务层将其传递到你的 UI,以便 UI 可以验证用户的输入。 自动截断可能在某些批处理输入过程中使用,例如,有一个可以截断的字段,无论是否向用户发出警告,例如,一个注释字段。

另请注意,本文并未规定任何特定的系统设计,它仅作为如何获取列元数据的说明;如何使用它取决于你。在高级的分布式系统中,UI 不直接与 LINQ 对象对话,这段代码可能会在你的测试人员手中找到用处,他们可以自动生成最大长度和最大长度+1 的输入,以确保最大长度的数据可以通过系统的所有层,并且最大长度+1 的数据在你的验证代码和你的业务层中被正确拒绝。

Using the Code

将这两个 static 方法添加到你的 Utilities 程序集中

/// <summary>
/// Gets the length limit for a given field on a LINQ object ... or zero if not known
/// </summary>
/// <remarks>
/// You can use the results from this method to dynamically 
/// set the allowed length of an INPUT on your web page to
/// exactly the same length as the length of the database column.  
/// Change the database and the UI changes just by
/// updating your DBML and recompiling.
/// </remarks>
public static int GetLengthLimit(object obj, string field)
{
    int dblenint = 0;   // default value = we can't determine the length

    Type type = obj.GetType();
    PropertyInfo prop = type.GetProperty(field);
    // Find the Linq 'Column' attribute
    // e.g. [Column(Storage="_FileName", DbType="NChar(256) NOT NULL", CanBeNull=false)]
    object[] info = prop.GetCustomAttributes(typeof(ColumnAttribute), true);
    // Assume there is just one
    if (info.Length == 1)
    {
        ColumnAttribute ca = (ColumnAttribute)info[0];
        string dbtype = ca.DbType;

        if (dbtype.StartsWith("NChar") || dbtype.StartsWith("NVarChar"))
        {
            int index1 = dbtype.IndexOf("(");
            int index2 = dbtype.IndexOf(")");
            string dblen = dbtype.Substring(index1 + 1, index2 - index1 - 1);
            int.TryParse(dblen, out dblenint);
        }
    }
    return dblenint;
}

/// <summary>
/// If you don't care about truncating data that you are setting on a LINQ object, 
/// use something like this ...
/// </summary>
public static void SetAutoTruncate(object obj, string field, string value)
{
    int len = GetLengthLimit(obj, field);
    if (len == 0) throw new ApplicationException("Field '" + field + 
            "'does not have length metadata");

    Type type = obj.GetType();
    PropertyInfo prop = type.GetProperty(field);
    if (value.Length > len)
    {
        prop.SetValue(obj, value.Substring(0, len), null);
    }
    else
        prop.SetValue(obj, value, null);
} 

使用它们很容易。 假设你有一个 LINQ 类型为 'Customer' 的实例 'customer',并且你想要获取 'Name' 字段的长度

int len = GetLengthLimit (customer, "Name"); 

你可能会在解决方案的最低层实现这一点,然后提供方法将长度元数据通过你的业务逻辑传递到你的 UI。 LINQ 的部分类可能是实现此目的的合适位置。 例如,你可以添加一个 int NameLength 属性来补充你的 Name 属性。

或者,假设你不关心截断(这是一种非常罕见且通常不明智的方法),并且你想要以一种保证成功的方式将一个值存储在数据库中(没有关于数据被截断的令人讨厌的异常)

SetAutoTruncate (song, "Comments", "Really long comments about the song 
  that someone else put in to the song metadata but which you really don't care about");

关注点

添加一个缓存很简单,可以从 <类型 + 字段名> 转换到 <长度> 而无需每次都反射类型,但与往常一样,像这样的优化几乎总是最好等到你 *需要* 它时才进行。

不要忘记包含适当的 using 语句

using System.Reflection;
using System.Data.Linq;
using System.Data.Linq.Mapping;

历史

  • 2008 年 6 月 30 日:第一个版本
  • 2008 年 7 月 1 日:第二个版本,包含初始反馈
© . All rights reserved.