实体框架验证&用法

时间:2008-11-19 18:41:16

标签: entity-framework validation

我知道有一个AssociationChanged事件,但是,在关联之后会触发此事件。没有AssociationChanging事件。因此,如果我想为某些验证原因抛出异常,我该如何做并恢复原始值?

此外,我想基于来自其他实体的信息来默认我的实体的值,但只有当我知道权利被插入数据库的实例时才这样做。如何区分它和实例化对象,因为它将基于现有数据填充?我应该知道吗?是否应该考虑我的实体业务逻辑之外的业务逻辑?

如果是这种情况,那么我应该设计控制器类来包装所有这些实体吗?我担心的是,如果我交还一个实体,我希望客户端能够访问这些属性,但是我想要对如何设置,默认等的验证保持严格控制。我看到的每个例子都引用了上下文,这是我的enity部分类验证之外,对吧?

顺便说一句,我看了看EFPocoAdapter并且因为我的生活无法确定如何在我的POCO课程中填充列表...任何人都知道我如何从EFPoco类中获取上下文?

5 个答案:

答案 0 :(得分:2)

这是对我留下的评论的回复。希望这能回答你的问题,Shimmy。只需评论,如果它没有回答您的问题,我会缩短或删除它。

您需要在您的类上实现INotifyPropertyChanging和INotifyPropertyChanged接口(除非它类似于实体框架对象,我相信它在内部实现)。

在为此属性设置值之前,您需要使用PropertyChangingEventArgs构造函数中的属性名称引发NotifyPropertyChanging.PropertyChanging事件。

设置此值后,您需要引发NofityPropertyChanged.PropertyChanged事件,再次使用PropertyChangedEventArgs构造函数中引发的属性名称。

然后你必须处理PropertyChanging和PropertyChanged事件。在PropertyChanging事件中,您需要缓存该值。在PropertyChanged事件中,您可以比较并抛出异常。

要从PropertyChanging / PropertyChanged事件args获取属性,您需要使用relfection。

// PropertyName is the key, and the PropertyValue is the value.
Dictionary <string, object> propertyDict = new Dictionary<object, object>();

    // Convert this function prototype to C# from VBNet.  I like how Handles is descriptive.
    Public Sub PropertyChanging(sender As object, e As PropertyChangingEventArgs) Handles Foo.PropertyChanging
    {
      if (sender == null || preventRecursion)
      {
        return;
      } // End if

      Type senderType = sender.GetType();
      PropertyInfo info = senderType.GetProperty(e.PropertyName);
      object propertyValue = info.GetValue(sender, null);

      // Change this so it checks if e.PropertyName already exists.
      propertyDict.Add(e.PropertyName, propertyValue);
    } // End PropertyChanging() Event

     // Convert this function prototype to C# from VBNet.  I like how Handles is descriptive.
    Public Sub PropertyChanged(sender As object, e As PropertyChangedEventArgs) Handles Foo.PropertyChanged
    {
      if (sender == null || preventRecursion)
      {
        return;
      } // End if

      Type senderType = sender.GetType();
      PropertyInfo info = senderType.GetProperty(e.PropertyName);
      object propertyValue = info.GetValue(sender, null);

      // Change this so it makes sure e.PropertyName exists.
      object oldValue = propertyDict(e.PropertyName);
      object newValue = propertyValue;

      // No longer needed.
      propertyDict.Remove(e.PropertyName);

      if (/* some condition */)
      {
        try {
          preventRecursion = true;
          info.SetValue(oldValue, null);
          Throw New Exception();
        } finally {
          preventRecursion = false;
        } // End try
      } // End if
    } // End PropertyChanging() Event

注意我是如何使用PreventRecursion的,这是一个布尔值,我忘了在这些方法上面添加?将属性重置为先前的值时,将调用这些事件。

TL;博士

现在您可以派生一个继承自INotifyPropertyChanged的事件,但使用一个参数来保存表示前一个值的Object以及Property Name。这样可以减少被触发的事件数量,具有类似的功能,并且与INotifyPropertyChanged具有向后兼容性。

但是如果你想在属性设置之前处理任何事情(比如属性做了不可逆转的更改,或者你需要在设置该变量之前设置其他属性,否则会抛出异常)你将无法做到这一点。

总的来说,这种方法是一种非常古老的做事方式。我会接受Poker Villian的回答,并且可以输入无效数据。但是不允许保存到数据库。

实体框架有一些优秀的验证代码。您可以通过属性向属性添加验证。然后它负责处理这些属性的工作。然后,您可以创建一个名为IsValid的属性,该属性调用Entity Framework特定的验证。它还区分了两个字段错误(比如键入错误的字符或字符串太长)和类错误(如丢失数据或冲突的密钥)。

然后,您可以将IsValid绑定到控件验证,并在输入无效数据时显示红色气泡。或者您可以自己实现IsValid验证。但是如果IsValid为false,则SaveChanges事件需要取消保存。

顺便说一句。提供的代码不会编译,只是伪代码(混合使用vb和c#)。但我相信它比c#更具描述性 - 显示正在处理的内容。

答案 1 :(得分:0)

关于您的第一个问题,我只是将关联的更改实现为业务逻辑。例如,如果您添加一个包含多个学生的教师课程,请不要添加像

这样的学生
aTeacher.Students.Add(new Student)

而是创建一个AddStudent方法

public Student AddNewStudent(string name, string studentID)
{

    Student s = new Student( name, studentID);
    s.Teacher = this; // changes the association
    return s;
}

通过这种方式,您可以完全控制何时更改关联。那当然是什么阻止了另一个程序员直接添加学生?在学生方面,您可以将教师设置器设置为私有(并将构造函数更改为接受教师或类似人员)。在教师方面,如何使学生集合不可插入?我不确定......可能会在不接受插入的自定义集合中对其进行转换。

关于问题的第二部分,您可以使用OnVarNameChanging事件。如果EntityState为“New”,那么您可以应用获取实际值的逻辑。

保存更改(OnSavingChanges?)时会触发一个事件,您可以使用该事件来确定哪些对象是新的并设置了一些值。

但也许最简单的解决方案是始终在构造函数中设置默认值,如果从数据库加载数据,它们将被覆盖。

祝你好运

答案 2 :(得分:0)

根据您的需要创建一个为您生成实例的工厂,如:

getStudent(String studentName, long studentId, Teacher teacher) {
    return new Student(studentName, studentId);
}

getStudentForDBInseration(String studentName, long studentId, Teacher teacher) {
    Student student = getStudent(studentName, studentId);
    student = teacher;
    //some entity frameworks need the student to be in the teachers student list
    //so you might need to add the student to the teachers student list
    teacher.addStudent(student);
}

答案 3 :(得分:0)

严重缺乏没有AssociationChanging(继承自CancelEventArgs)事件。

我也非常困扰我,因此我将此报告给Microsoft Connect Please vote here!

顺便说一句,我也认为PropertyChangingEventArgs不会继承CancelEventArgs也是愚蠢的,因为取消异常并不总是优雅的解决方案,此外,抛出异常比调用OnPropertyChangingEvent需要花费更多的性能然后检查返回e.Cancel,它的成本低于提升PropertyChangingEvent的成本,你无论如何都要称它们为。左右。
对于那些坚持采用Exception方式的人来说,也可以在处理程序中抛出异常,而不是将e.Cancel标记为true。的 Vote Here

答案 4 :(得分:0)

要回答部分问题或阐述ADB的答案,您可以使用ObjectStateManager.GetObjectStateEntry来查找实体的状态并编写自定义默认逻辑。

SaveChanges是您可以使用的上下文的方法,或者SavingChanges是在调用SaveChanges之前发生的事件。

如果您不想中止更改,您可以覆盖SaveChanges并仅调用base.SaveChanges

上下文还有一个ObjectMaterialized事件。

在两者之间,您可以将所有验证和创建代码粘贴在一个位置,如果它们很复杂并且包含其他对象的值等,则可能是合适的。