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






4.40/5 (11投票s)
反射 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 日:第二个版本,包含初始反馈