有没有更好的方法来处理LINQ to SQL验证?
我可以理解,你不想在一个属性设置为无效值后直接抛出异常。这种方法使得向用户正确传达实际错误的难度。但是,我认为最好避免使用这些部分验证方法。国际海事组织你想抛出一个异常,当你的模型是无效的,但只是在你坚持你的模型到数据库。
我建议您使用验证框架并将其与您的LINQ to SQL DataContext类集成。这里有一个如何与Enterprise Library Validation Application Block做到这一点的例子,但这个概念会为你选择的每一个验证框架的工作:
public partial class NorthwindDataContext
{
public override void SubmitChanges(ConflictMode failureMode)
{
ValidationResult[] = this.Validate();
if (invalidResults.Length > 0)
{
// You should define this exception type
throw new ValidationException(invalidResults);
}
base.SubmitChanges(failureMode);
}
private ValidationResult[] Validate()
{
// here we use the Validation Application Block.
return invalidResults = (
from entity in this.GetChangedEntities()
let type = entity.GetType()
let validator = ValidationFactory.CreateValidator(type)
let results = validator.Validate(entity)
where !results.IsValid
from result in results
select result).ToArray();
}
private IEnumerable<object> GetChangedEntities()
{
ChangeSet changes = this.GetChangeSet();
return changes.Inserts.Concat(changes.Updates);
}
}
[Serializable]
public class ValidationException : Exception
{
public ValidationException(IEnumerable<ValidationResult> results)
: base("There are validation errors.")
{
this.Results = new ReadOnlyCollection<ValidationResult>(
results.ToArray());
}
public ReadOnlyCollection<ValidationResult> Results
{
get; private set;
}
}
有如DataAnnotations和 的Enterprise Library Validation Application Block(VAB)提供了一些验证框架。 VAB非常适合这样做。使用LINQ to SQL,您的实体会生成,因此您需要使用VAB提供的基于配置的方法(不要尝试使用属性装饰您的实体)。通过重写SubmitChanges方法,您可以确保在实体持久化之前触发验证。我的答案是here和here包含有关使用VAB的有用信息。
我写了一些关于将VAB与LINQ to SQL here和here集成在一起的有趣文章。 LINQ to SQL(与Entity Framework 1.0相比)的好处是生成了很多有用的元数据。将此与VAB结合使用时,您可以使用此元数据来验证模型,而无需手动连接每个验证。可以从模型中提取最大字符串长度和非空值的特别验证。阅读here如何做到这一点。
VAB来拯救!
最终这表明你在的最后一道防线(在任何数据库限制之前,至少)你的数据是无效的。如果你想做一些除了大声尖叫之外的事情,那么可以在之前验证数据(通过多种方法中的任何一种),将其添加到插入列表中。
作为一个额外的想法,你可能尝试覆盖SubmitChanges
(在数据上下文);获取变更集,验证插入和删除(删除 - 提交,哪个IIRC检查插入列表并删除它们)任何你已经决定的错误。然后致电base.SubmitChanges
。但对我而言,这有点倒退。
为了说明,这只做了一个插入(不是按要求填写两个),但我不喜欢这种方法。完全一样。只要我们清楚; -p
namespace ConsoleApplication1 {
partial class DataClasses1DataContext { // extends the generated data-context
public override void SubmitChanges(
System.Data.Linq.ConflictMode failureMode) {
var delta = GetChangeSet();
foreach (var item in delta.Inserts.OfType<IEntityCheck>()) {
if (!item.IsValid()) {
GetTable(item.GetType()).DeleteOnSubmit(item);
}
}
base.SubmitChanges(failureMode);
}
}
public interface IEntityCheck { // our custom basic validation interface
bool IsValid();
}
partial class SomeTable : IEntityCheck { // extends the generated entity
public bool IsValid() { return this.Val.StartsWith("d"); }
}
static class Program {
static void Main() {
using (var ctx = new DataClasses1DataContext()) {
ctx.Log = Console.Out; // report what it does
ctx.SomeTables.InsertOnSubmit(new SomeTable { Val = "abc" });
ctx.SomeTables.InsertOnSubmit(new SomeTable { Val = "def" });
ctx.SubmitChanges();
}
}
}
}
我从来没有说过我想要执行两个插入:)我喜欢这种方法。我会放弃这一点,让你知道我是如何去的。谢谢:) – 2010-03-08 11:45:18
@Marc Gravell:虽然它回答了他的问题,但我认为在保存之前静静地删除无效实体有点可怕。我知道Phycho询问不使用异常,但IMO最好用异常消息抛出异常。 – Steven 2010-03-08 13:33:01
Steven,这不是关于沉默,而是关于在例外情况下使用例外。验证不是一个意想不到的事情,因此不需要抛出异常...... IMO :-) – 2010-03-09 10:35:24
如果我理解了这个问题,我想你可以使用TransactionScope:http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope.aspx并提交或处理更改。 – Jacob 2010-03-07 10:56:22