在C#中管理多对多关系

时间:2012-11-19 22:20:47

标签: c# asp.net many-to-many

我正在尝试在应用程序级别中以多对多关系管理一些简单列表。我希望每次在它们之间建立连接时更新两个对象中的关系(例如teacher.AddStudent()student.RemoveTeacher())。

我知道如果我使用SQL,我会使用StudentTeacher表之类的东西来管理关系,但我正在尝试理解如何在应用程序中执行此操作。

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace StudentTeacher
{
    // create simple interface
    public interface IPerson {}

    // abstract class
    public abstract class Person : IPerson 
    {
        public int Id { get; set; }
    }

    // concrete "Teacher"
    public class Teacher : Person
    {
        // private list of student ids
        private List<int> _studentIds { get; set; }

        // readonly accessor
        public ReadOnlyCollection<int> StudentIds {
            get { return _studentIds.AsReadOnly(); }
        }

        // constructor
        public Teacher(int id) {
            Id = id;
            _studentIds = new List<int>();    
        }

        // add a student
        public void AddStudent(Student student)
        {
            if (!_studentIds.Contains(student.Id)) {
                _studentIds.Add(student.Id);
                student.AddTeacher(this);
            }
        }

        // remove a student
        public void RemoveStudent(Student student)
        {
            if (_studentIds.Contains(student.Id)) {
                _studentIds.Remove(student.Id);
                student.RemoveTeacher(this);
            }
        }

        public string ToString() {
            return String.Format("Id:{0} | StudentIds:{1}", Id, String.Join(",", StudentIds));
        }
    }

    /// concrete "Student"
    public class Student : Person
    {

        // private list of teacher ids
        private List<int> _teacherIds { get; set; }

        // readonly accessor
        public ReadOnlyCollection<int> TeacherIds {
            get { return _teacherIds.AsReadOnly(); }
        }

        // constructor
        public Student(int id) {
            Id = id;
            _teacherIds = new List<int>();    
        }

        // add a teacher
        public void AddTeacher(Teacher teacher)
        {
            if (!_teacherIds.Contains(teacher.Id)) {
                _teacherIds.Add(teacher.Id); 
                teacher.AddStudent(this);
            }
        }

        // remove teacher
        public void RemoveTeacher(Teacher teacher)
        {
            if (_teacherIds.Contains(teacher.Id)) {
                _teacherIds.Remove(teacher.Id);
                teacher.RemoveStudent(this);
            }
        }

        // simple override
        public string ToString() {
            return String.Format("Id:{0} | TeacherIds:{1}", Id, String.Join(",", TeacherIds));
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // create teachers
            var teacher1 = new Teacher(101);
            var teacher2 = new Teacher(102);

            // create students
            var student1 = new Student(501);
            var student2 = new Student(502);
            var student3 = new Student(503);

            // create some relationships
            teacher1.AddStudent(student1);
            teacher1.AddStudent(student2);
            teacher1.AddStudent(student3);

            // see what's happening
            Console.WriteLine(teacher1.ToString()); // "Id:101 | StudentIds:501,502,503"
            Console.WriteLine(student1.ToString()); // "Id:501 | TeacherIds:101"
            Console.WriteLine(student2.ToString()); // "Id:502 | TeacherIds:101"
            Console.WriteLine(student3.ToString()); // "Id:503 | TeacherIds:101"

            // update student1, student2
            student1.AddTeacher(teacher2);
            student2.AddTeacher(teacher2);

            // -- Outputs 
            Console.WriteLine(student1.ToString()); // "Id:501 | TeacherIds:101,102"
            Console.WriteLine(student2.ToString()); // "Id:502 | TeacherIds:101,102"

            Console.ReadLine();
        }
    }
}

对我来说,直接调用反向添加/删除方法是不对的;

if (!_studentIds.Contains(student.Id)) {
    _studentIds.Add(student.Id);
    student.AddTeacher(this); // calling this directly feels bad
}

我觉得这很快就会变得非常混乱。我的问题是;

  • 我是否应该寻找像静态观察者这样的东西 StudentTeacherObserver并提出相关事件,例如 OnTeacherAddedOnStudentAdded
  • 是否有管理此类关系的特定模式?
  • 是否有更适合此目的的数据结构,如果有,它是什么以及如何使用它?

2 个答案:

答案 0 :(得分:0)

嵌套对象图(Teacher.Students[0].Teacher...)通常会感觉“混乱”,尤其是在延迟加载关系(chatty)或预先加载所有对象,然后映射引用(脂肪)之间。

但是,我不认为你过度思考它。如果没有对数据访问做出某种类型的让步,多对多很难正确表示。我认为您需要更深入地评估您的业务问题,以发现更具体的方案,以确定您是否需要更改任何内容。

答案 1 :(得分:0)

最简单的事情就是挑选一名胜利者。所以不要实现这两种方案。这是一个有一点背景的链接:

http://www.ebenroux.co.za/post/2009/09/09/Many-to-Many-Aggregate-Roots.aspx

你必须想出适合自己的东西。表示真实物体并不总是一个好主意。简单地存储相关ID可能就足够了。在Order / OrderItemProduct示例中。可能没有理由在Product中拥有实际的OrderItem对象,因为它不属于Order聚合。您只使用Product中的数据,然后OrderItem上的数据变为非规范化状态。直到Order被操作(某人实际开始填充Order)为止,它可以被更改。当然,关于停止流程(取消订单)和退回产品等的业务规则可能很多但是从一种情况/业务变为下一种情况。

重点是尝试“剪切”您的对象图,以便您有明确定义的聚合。

这有助于延迟加载位。我从不使用延迟加载,并会敦促我遇到的任何人尽最大努力避免它。这个想法实际上不是要查询你的对象/域模型,而是完全是另一个讨论。

我希望这有道理:)

相关问题