我创建了一些用于向数据库中的存储过程提供数据的类。存储过程中的varchar
参数具有长度规范(例如varchar(6)
,我想在将所有字符串属性传递给存储过程之前验证其长度。
有一种简单的,声明性的方法吗?
到目前为止,我有两个概念性的想法:
属性
public class MyDataClass
{
[MaxStringLength = 50]
public string CompanyName { get; set; }
}
我不确定我需要使用哪些程序集/命名空间来实现这种声明性标记。我认为这已经存在,但我不知道在哪里以及它是否是最佳方式。
在属性中验证
public class MyDataClass
{
private string _CompanyName;
public string CompanyName
{
get {return _CompanyName;}
set
{
if (value.Length > 50)
throw new InvalidOperationException();
_CompanyName = value;
}
}
}
这看起来像很多工作,并且会让我当前简单的课看起来很难看,但我想它会完成工作。为了做到这一点,还需要进行大量的复制和粘贴。
答案 0 :(得分:2)
嗯,无论你走到哪里,执行的东西看起来都像是你的第二种方法。所以诀窍是让你的第一种方法成为你的第二种方法。
首先,它需要[MaxStringLength(50)]
。接下来,所有操作都是向此类的Type对象添加一些数据。你仍然需要一种方法来使用这些数据。
一种方法是二进制重写。在编译之后(但在执行之前),重写器将读取程序集,查找该属性,并在找到它时,添加代码以进行检查。零售产品PostSharp旨在完成那种类型的事情。
或者,您可以在运行时触发它。类似的东西:
public class MyDataClass
{
private string _CompanyName;
[MaxStringLength(50)]
public string CompanyName
{
get {return _CompanyName;}
set
{
ProcessValidation()
_CompanyName = value;
}
}
}
这仍然很难看,但如果你有许多验证属性,它会好一些。
答案 1 :(得分:2)
我会将此作为不同的答案发布,因为它的特征与代码合同不同。
可用于声明性验证的一种方法是使用字典或散列表作为属性存储,并共享实用程序方法以执行验证。
例如:
// Example attribute class for MaxStringLength
public class MaxStringLengthAttribute : Attribute
{
public int MaxLength { get; set; }
public MaxStringLengthAttribute(int length) { this.MaxLength = length; }
}
// Class using the dictionary store and shared validation routine.
public class MyDataClass
{
private Hashtable properties = new Hashtable();
public string CompanyName
{
get { return GetValue<string>("CompanyName"); }
[MaxStringLength(50)]
set { SetValue<string>("CompanyName", value); }
}
public TResult GetValue<TResult>(string key)
{
return (TResult)(properties[key] ?? default(TResult));
}
public void SetValue<TValue>(string key, TValue value)
{
// Example retrieving attribute:
var attributes = new StackTrace()
.GetFrame(1)
.GetMethod()
.GetCustomAttributes(typeof(MaxStringLengthAttribute), true);
// With the attribute in hand, perform validation here...
properties[key] = value;
}
}
您可以通过将堆栈跟踪设置为demonstrated here来使用反射来获取调用属性。反映属性属性,运行验证,瞧!共享一个通用验证例程的单行getter / setter。
另外,这种模式也很方便,因为您可以通过仅更新{来设计一个类来使用类似的类似字典的属性存储,例如ViewState或Session (在ASP.NET中) {1}}和GetValue
。
另外需要注意的是,如果您使用此方法,您可以考虑将验证逻辑重构为验证实用程序类,以便在所有类型之间共享使用。这应该有助于防止您的数据类在SetValue
方法中变得过于庞大。
答案 2 :(得分:1)
这听起来像是一个商业规则。所以我会将它放在公司类中(因为它是CompanyName),并在那里进行验证。如果你把它封装起来,我不明白为什么它需要复制和粘贴。
属性或第二个例子应该没问题。但是,该属性允许在具有字符串长度约束的其他类中重用。
答案 3 :(得分:1)
使用属性的第一种方法听起来不错。
通过继承System.Attribute类来实现您的属性,并使用AttributeUsage属性标记您的类,以便在字段上设置您的属性。
然后,使用反射,在将值发送到SP之前检查属性的存在和值。
这比第二种方法提供了更多的灵活性。如果tomorow你决定让你的SP收到一个太长的字符串的前N个字符,你将不必修改所有代码,只修改那个解释属性的代码。
框架中确实存在一些验证属性,但我不会使用这些属性,因为你可能会暗示一些你不期望的行为,因为你无法以任何方式修改它们(如果你想要的话,请说谎)像[MaxLength(50,true)]这样的东西来指定使用前50个字符就可以了。
答案 4 :(得分:0)
虽然不完全相同,但我最近在MSDN文章中发现了.NET 4 Code Contracts。它们提供了一种方便而优雅的编码和分析代码假设的方法。值得一看。