【问题标题】:How to figure out which repository to call for different implementations of an interface?如何确定为接口的不同实现调用哪个存储库?
【发布时间】:2012-08-11 05:25:54
【问题描述】:

我刚开始使用 DDD,并且对对象和存储库的接口有疑问。假设我有以下对象

public interface IPerson { ... }

public class Student 
{
    double gpa;
    ...
}

public class Teacher
{
    double salary; ...
}

那么我也有两个存储库,例如

public class StudentRepository :IRepository { public void Save(Student) }

public class TeacherRepository :IRepository { public void Save(Teacher) }

我的问题是,假设我有一个名为 people 的 IPerson 对象列表,有没有一种方法可以让我做一些类似 repository.Save(persons) 的事情?无需使用反射来确定 IPerson 实际是什么类型。

我现在还有一门课

PersonRepository :IRepository 
{
     public void Save(IPerson person)
     {
          if(Person is Student)
          {
               new StudentRepository.Save(person as Student);
          }
          else if(Person is Teacher)
          { ....}
      }
}

然后我可以调用 personRepository.Save(persons); 然而,这并不像是构建事物的最佳方式。我该如何改进这个设计?

谢谢

编辑:

我正在寻找的是,假设我收到一个名为 person 的 IPerson 对象。我不一定知道它是什么实现,我只想调用 repository.Save(person) 并让它调用正确的存储库。有没有办法在不使用某种带有反射的 switch 语句的情况下做到这一点?

【问题讨论】:

    标签: c# domain-driven-design ddd-repositories


    【解决方案1】:

    您可能有一个带有 C# 泛型的方法

    interface Repository<TEntity> where TEntity : class {
    
        void Save(TEntity entity);    
    }
    

    但我不鼓励使用泛型(如泛型,而不是 C# 泛型)存储库。存储库接口应该是域驱动的并且特定于您的实体。请考虑 Greg Young 的 article

    也不清楚为什么您有实体接口 (IPerson)。接口通常在应用程序的seam 创建。您是否计划实施不止一种 IPerson?

    【讨论】:

    • 我已经成功地用实现者实现了通用存储库基类。无需重复管道。
    • Repository 接口不是管道,它是一个域概念。实施是管道,您可以避免数据访问层中的代码重复。请看一下我提到的文章。
    • 我想我们想说的是同一件事。数据访问不需要重复。优秀的文章,我现在认为作文会更好。
    • 我认为StudentTeacher 应该是IPerson 的两个实现者。
    【解决方案2】:

    考虑使用通用存储库

    class Repository<T> :IRepository<T>
    {
         public void Save(T entity)
         {
             ...
         }
    }
    

    用法

    IRepository<Student> repo1 = new Repository<Student>();
    repo1.Save(new Student());
    
    IRepository<Teacher> repo2 = new Repository<Teacher>();
    repo2.Save(new Teacher());
    


    接下来,您可以使用 IoC 容器和 DI 来传递存储库而不是创建它们

    顶层,比如在main方法或global.asax

    IRepository<Student> studentRepo = IoC.Current.Resolve<IRepository<Student>>();
    

    稍后在需要保存数据的类中,将IRepository&lt;Student&gt; studentRepo传递给构造函数

    class Foo
    {
        private IRepository<Student> repo
    
        Foo(IRepository<Student> repo)
        {
            this.repo = repo;
        }
    
        public void Save(Student s)
        {
            repo.Save(s);
        }
    }
    

    编辑

    您可以将保存操作移至IPerson&lt;T&gt;

    class Person<T> : IPerson<T>
    {
        private IRepository<T> repo;
    
        Person(IRepository<T> repo)
        {
            this.repo = repo;
        }
    
        public void Save()
        {
            repo.Save<T>();
        }
    }
    

    所以当你从Person&lt;T&gt; 派生出TeacherStudent 时,你传递了对应的T,就像

    class Student : Person<Student>
    {
        private IRepository<Student> repo;
    
        Person(IRepository<Student> repo):base(repo)
        {
           ...
        }    
    }
    

    这将使您能够在没有反射的情况下使用 List 或切换功夫。

    【讨论】:

    • 也许我遗漏了一些东西,但是我将如何保存像 List 这样既包含学生又包含老师的东西?我是否必须声明两个单独的存储库并过滤列表以输入到正确的存储库?
    • 基本上,我们有一个课程网格。我们希望能够将学生/教师与课程联系起来。添加到课程的每个人都添加到 List 中。当教师与诸如加薪之类的课程相关联时,我们还会做一些其他事情。然后,我们要将 IPerson 的整个列表更新到数据库中。我不愿意维护两个单独的学生/教师列表,因为我们将来可能会添加其他 IPerson 实现,例如 TA 等。
    • @user1599120 啊哈知道了。看看你是否将此逻辑移动到 Person 类,然后你的派生类可以使用对应的存储库并保存在需要的地方。代码是用记事本写的,但希望大家可以借鉴一下。
    • 域对象应该尽可能持久无知。在实体上使用“保存”方法违反了持久无知和 SRP。在这一点上,它更像是一个 ActiveRecord,而不是域模型。 en.wikipedia.org/wiki/Active_record_pattern
    • @Dmitry 它实际上是持久无知的。如果您从 Person 类中读取此代码 public void Save() { repo.Save&lt;T&gt;(); },您将找不到对特定数据库代码的任何引用。
    【解决方案3】:

    两种可能的方法。

    首先,特定于域类型的接口

    interface IStudentRepository
    interface ITeacherRepository
    class StudentRepository : IStudentRepository
    class TeacherRepository : ITeacherRepository
    

    二、泛型接口

    interface IRepository<T>
    class StudentRepository : IRepository<Student>
    class TeacherRepository : IRepository<Teacher>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-02-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-30
      • 1970-01-01
      • 2015-02-13
      • 2014-07-29
      • 1970-01-01
      相关资源
      最近更新 更多