【问题标题】:Entity Framework: Object design with relationships and more实体框架:具有关系等的对象设计
【发布时间】:2013-10-31 19:36:33
【问题描述】:

我有一个关于大学管理的 .net 解决方案(使用实体框架,使用 CodeFirst 方法)。项目的架构是这样的:

  • DataAccess(DbContext 和通用存储库)
  • 实体(代码第一类)
  • 服务(服务类)
  • Web(一个 MVC 项目,对解释不重要)

为了让问题更简单,我将只讨论我域中的两个类(在 Entities 层上):

  • 课程
  • 学生

课程可以有很多学生。解决方案的目标是将学生添加到课程中(中间有一些验证)。

Course.cs

public class Course
{
    [Key]
    public int IdCourse { get; protected set; }

    public string Name { get; set; }

    public virtual ICollection<Student> Students { get; protected set; }

    public void AddStudent(Student student)
    {
        // Some Validation
        Students.Add(student);
    }
}

如您所见,该课程具有用于将学生添加到课程的属性。 情况是验证越来越多,现在非常复杂,需要对数据库进行一些查询等等......所以课程有很多责任!

所以我想,我需要一项服务来处理“添加”并进行所有验证,并将这种责任从 Course 课程中移除。问题是,由于 Students CollectionCourse私人成员,我无法将学生添加到 Service 类的课程中。

我认为解决问题的另一种方法是将相关实体(学生)添加到上下文中(来自服务),但这会破坏我的 存储库(因为我必须DbContext 公开)。如果我直接使用 Service 类中的 DbContext,那么存储库就没有意义了,不是吗?

那么,我应该如何设计呢?或者我应该怎么做才能解决这个问题? 任何建议都会很受欢迎!

谢谢!

【问题讨论】:

    标签: .net entity-framework object-design


    【解决方案1】:

    这并不能直接回答您的问题,尤其是与代码优先 EF 等相关的问题(不是我的专业领域)。但看起来您这里确实存在设计问题。

    我是 DDD 或领域驱动设计的忠实粉丝。您可能想问的一些问题包括:

    • “AddStudents”真的应该是一种方法吗?一个对象应该有一个行为,而“添加学生”是一个数据持久性概念,而不是域/实体概念。也许行为会是“注册”?注册可能有自己的逻辑,因此有对象来封装该逻辑。
    • 其次,学生是否存在于他们正在学习的课程的概念之外?例如,他们可以参加多门课程吗?那么这里的集合可能不应该是私有的......应该有一个名为 StudentEnrollment 的新对象吗?

    还有其他问题,但一定要全面审视您的问题以及您的对象的设计方式。添加学生的课程与将课程添加到该学生的课程相同吗?

    也许您的服务可以进行验证并将学生添加到课程中,或者将课程添加到学生中(或两者都添加)并将它们保存在自己的存储库中。

    无论如何,我向面向数据的 EF 人员推荐 Julie Lerman 的 DDD 系列:http://msdn.microsoft.com/en-us/magazine/dn342868.aspx

    无论如何,我建议不要公开您的 DbContext!

    【讨论】:

    • 我会回答你的问题: 1) 不,它不一定是一种方法。是的,我想要的是“注册”学生的概念。但我不知道如何设计类来进行验证并正确保存数据。我清楚了吗?您将如何使用“注册”(不是详细,而是更高级别)来做到这一点? 2) 是的,学生存在于课程概念之外。确切地说,他们可以参加多门课程。但是,为什么它不应该是私有的呢?我不希望客户端应用程序(程序员)能够在未进行正确验证的情况下将学生添加到课程或将课程添加到学生。
    【解决方案2】:

    我不同意实体中的AddStudent(...) 之类的方法。让实体成为进出 EntityFramework 的简单数据传输对象(即 POCO)。我更喜欢让我的业务逻辑来处理这些事情。

    您可以将 IdCourseStudents 设置器设置为私有。我也看到其他人也使用protected……但是为什么呢?实体有后代吗?

    Julie Lerman 等人推荐的 ICollection&lt;T&gt; 属性的一种解决方案是:

    private ICollection<Student> _students;  
    
    public virtual ICollection<Student> Students { 
       get; { _students ?? (_students = new Collection<Student>()); }
       private set { _students = value; }
    }  
    

    更新来自评论

    如果您使用存储库或业务层来查询课程,您可以有如下方法:

       // this is how I would approach a business layer method
       void AddStudentToCourse(string CourseName, Student NewStudent) {
           // may employ a using block for repository
           // also validate student
           using (var repo = new CourseRepository()) {
              var course = repo.GetCourse(CourseName);
              course.Students.Add(NewStudent);
    
              repo.Save();
           }
       }
    

    有几种不同的方法可以完成此操作,此示例远未完成,但应该为您提供一个基本概念。

    基础DbContext(由CourseRepository 继承或封装)将跟踪Course 的更改以及StudentCourse 的新学生或关系的添加。

    就私有或受保护的设置器的使用而言,我想限制开发人员访问更改 ID/键或重新分配整个集合引用。

    【讨论】:

    • 关于“AddStudent”方法,我同意你的看法,这就是为什么我想把这个方法从“Course”课程中去掉! IdCourse 和学生可能是私人的,我无法回答您的问题,因为我没有理由对其进行保护,但这不是我在这里的主要原因,所以也许我们可以跳过这个话题:P(但是如果您有任何意见,我想知道!)关于 Julie Lerman 的推荐,很好,我会使用它,但它无助于解决我的问题:我如何从外部将学生添加到课程中(知道学生集合设置器是私有的)?
    • 感谢您的帮助,但有一个问题。如果您可以从服务中添加这样的学生,您可以从应用程序中以相同的方式添加学生,并且我想避免能够在不进行验证的情况下将学生添加到课程中!我怎样才能避免这种情况?
    • 不要在应用层使用POCO。在存储库/业务层和表示层之间使用 DTO 或 DataTransferObjects。此外,对添加过程提供某种形式的验证,以确保:正确的凭据、正确的方法/方法顺序等。这有帮助吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多