13

我知道有一个 AssociationChanged 事件,但是,在建立关联后会触发此事件。没有 AssociationChanging 事件。那么,如果我出于某种验证原因想抛出异常,我该如何执行此操作并恢复到我的原始值?

另外,我想根据来自其他实体的信息为我的实体设置默认值,但只有在我知道实体被实例化以插入数据库时​​才这样做。我如何区分它与实例化的对象之间的区别,因为它即将根据现有数据进行填充?我应该知道吗?是否应该在我的实体业务逻辑之外进行考虑的业务逻辑?

如果是这样,那么我应该设计控制器类来包装所有这些实体吗?我担心的是,如果我返回一个实体,我希望客户端能够访问属性,但我希望对它们的设置、默认等验证保持严格控制。我看到的每个示例都引用上下文,这超出了我的实体部分类验证,对吗?

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

4

5 回答 5

2

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

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

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

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

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

要从 PropertyChanging/PropertyChanged 事件参数中获取属性,您需要使用 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 继承的事件,但使用一个参数,该参数包含一个表示先前值的对象以及属性名称。这会将被触发的事件数量减少到一个,具有类似的功能,并且与 INotifyPropertyChanged 具有向后兼容性。

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

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

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

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

顺便提一句。提供的代码不会编译并且只是伪代码(混合 vb 和 c#)。但我相信它比单独的 c# 更具描述性——准确地显示正在处理的内容。

于 2010-12-23T19:11:45.390 回答
0

It's a serious lack not having an AssociationChanging (that inherits from CancelEventArgs) event.

It bothers me also very much, therefore I reported this to Microsoft Connect Please vote here!

And BTW, I also think this is also stupid that the PropertyChangingEventArgs doesn't inherit CancelEventArgs, since cancelling with an exception is not always the elegant solution, besides, throwing exceptions cost more performance than calling the OnPropertyChangingEvent then check for the returned e.Cancel, so does it cost less than raising the PropertyChangingEvent, which you anyway call them both.
Also an exception can be thrown at the handler anyway instead of marking e.Cancel as true, for those who insist to go the Exception way. Vote Here.

于 2010-08-11T23:19:14.210 回答
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 是“新”,那么您可以应用获取实际值的逻辑。

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

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

祝你好运

于 2009-08-19T17:36:20.483 回答
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);
}
于 2009-09-23T21:46:10.083 回答
0

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

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

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

上下文还有一个 ObjectMaterialized 事件。

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

于 2010-11-08T15:21:16.440 回答