使用接口实现CRUD操作

16
什么是在业务逻辑层实现CRUD的最佳方法,使用接口来抽象数据访问层操作?我需要你们的意见。
这是我的草稿。
映射在数据库表中的数据实体。
 public class Student
 {
    public string StudentId { get; set; }
    public string StudentName { get; set; }
    public Course StudentCourse { get; set; }
 }

 public class Course
 {
    public string CourseCode { get; set; }
    public string CourseDesc { get; set; }
 }

我创建了一个CRUD接口来抽象对象的操作。
public interface IMaintanable<T>
{
   void Create(T obj);
   T Retrieve(string key);
   void Update(string key);
   void Delete(string key);
 }

接下来是通过实现接口来管理实体及其操作的组件。

public class StudentManager : IMaintainable<Student>
{
    public void Create(Student obj)
    {
        // inserts record in the DB via DAL
    }

    public Student Retrieve(string userId)
    {
        // retrieveds record from the DB via DAL
    }

    public void Update()
    {
        // should update the record in the DB
    }

    public void Delete(string userId)
    {
        // deletes record from the DB
    }
}

使用示例

    public void Button_SaveStudent(Event args, object sender)
    {
        Student student = new Student()
        {
           StudentId = "1", StudentName = "Cnillincy"
        }

        new StudentManager().Create(student);   
     }

正如您所看到的,更新方法存在相当不正常的情况。

    public void Update()
    {
        // should update the record in the DB
    }

这个方法需要更新对象的属性,我应该继承 Student 吗?

    public class StudentManager : Student , IMaintainable<Student>
    {
        public void Update()
        {
            //update record via DAL
         }
    }


    public void Button_SaveStudent(Event args, object sender)
    {
        Student student = new StudentManager();
        student.StudentId = "1";
        student.StudentName = "Cnillincy"
        student.Update()
    }

我应该将学生类仅作为学生管理员的属性,还是应该将其作为独立的类?
     public class StudentManager : IMaintainable<Student>
    {
        public Student student { get; private set };

        public void Create() {}
        public void Update() {}
        public void Retrieve() {}
        public void Delete() {}
    }

哪个更合适?接口怎么样?还有其他建议吗?谢谢..C
6个回答

19
您的CRUD界面应该长成这样:
public interface IMaintanable<T>
{
    string Create(T obj);
    T Retrieve(string key);
    void Update(T obj);
    void Delete(string key);
}

也就是说,CreateUpdate 都会复制要更新的对象。不同之处在于,Update 可以从 obj 中获取 key,从而知道要更改的对象。一般情况下,Create 会创建 key,并将其作为返回值传递回去。希望这可以帮到你。
Update 也可能会传回 key。)

你认为这比继承对象更有效率吗?因为这意味着你会创建对象的另一个副本而不是仅更新它。 - CSharpNoob
2
继承并没有意义。你的StudentManager不是一个学生。 - Matthieu
马修所说的。把学生想象成一张唱片,把你的StudentManager想象成校长秘书,随着记录的到来,将其打字记录下来。单一职责原则胜利。 - Lunivore
为什么key的类型是stringDelete()id不应该是uintint吗?我可能错了,请纠正我。 - Jo Smo
@tastro 我正在使用原始问题中提出的约定。 - Lunivore
1
@tastro 别担心。你说得对;ids / keys 可以是任何东西,只要它们是唯一的。我个人会选择 Guids。 - Lunivore

12
  • 这里缺少的是恰当的术语。其实这是一种非常有用的模式,称为存储库模式。就类型感知而言,实现将被称为通用存储库。
  • 个人过去实现的方式是定义一个存储库接口,例如IRepository<T>,和一个特定于存储库类型的基类,例如SqlRepositoryBase<T>。我这样做的原因是可以将实现特定代码放在基类中,这样就完成了与底层数据存储的交互。然后,在最终的存储库中,我们只需要关注领域特定的实现,它可能是StudentRepository、SqlRepository<Student>(如果您为实体定义接口,则可能是SqlRepository<IStudent>)。
  • 似乎您担心实例化了多少对象,但我可以告诉您,以这种方式实现不会对资源造成足够大的负担,因此不必过于担心。老一辈的程序员可能会对此感到不适,但我们不再试图为64k或RAM进行优化。;-)现在更多的是关注可维护性、代码合同等方面。
  • 如果您想将多个不同类型的实体合并到原子事务中,请查看工作单元模式。
  • 以下是这些主题的一些好引用:

    总的来说,这里有两个要点(仅代表本人观点):

    • 我个人不同意一个仓储模式的假设,即只有在大型项目中才有用处,尤其是通用仓储模式。如果你开始将这样的代码放入一个可重复使用的库中,你会惊讶地发现你可以快速创建一个宝贵的构建块资源。

    • 这种方法最大的优点是它极易进行测试,甚至比可重用性还要高。如果您打算为任何类型的TDD方法模拟您的存储库,您可以轻松地做到这一点。这将允许您编写更丰富的测试,围绕你代码中存储库的用法。


    1
    像这样的CRUD模式基本上就是一个存储库模式,无论你如何称呼它。我同意你关于即使对于小项目也很有用的评论。从CRUD的角度来看,好处在于它非常适合转换为REST。即使做得不好,REST也相当高效和可扩展。对于像学生这样的领域 - 每年仅在几天内大量访问 - 性能实际上可能是一个考虑因素,即使它不是差异化因素。 - Lunivore
    Joseph,多年后仍然有价值,因此“工作单元模式”的链接已经失效,你能否更新一下? - Edward
    @Edward - 完成了。使用Wayback Machine拉取了一个存档。谢谢! - Joseph Ferris

    3

    我看到了Rob Conery的一篇文章,非常喜欢。它的强大之处在于您可以传递给方法的参数的灵活性。在我看来,您的实现还不够健壮。在这里查看他的MVC入门套件:http://mvcstarter.codeplex.com/(在那里称为ISession)。

    public interface IMaintainable : IDisposable
        {
           T Single<T>(Expression<Func<T, bool>> expression) where T : class, new();
           System.Linq.IQueryable<T> All<T>() where T : class, new();
           void Add<T>(T item) where T : class, new();
           void Update<T>(T item) where T : class, new();
           void Delete<T>(T item) where T : class, new();
           void Delete<T>(Expression<Func<T, bool>> expression) where T : class, new();
           void DeleteAll<T>() where T : class, IEntity, new();
           void CommitChanges();
        }
    

    别担心...一旦你看了他的解决方案,你就会发现大多数CRUD操作所需的代码非常少。 - James South

    2

    我不会让StudentManager继承Student,我会像create方法一样使我的Update方法无状态,例如:

    public interface IMaintanable<T>
    {
      void Create(T obj);
      T Retrieve(string key);
      void Update(T obj);
      void Delete(string key);
    }
    

    并且

    public void Update(T obj)
    {
      // should update the record in the DB
    }
    

    2
    你能解释一下为什么这比继承或属性化DataEntity更合适吗?谢谢。 - CSharpNoob
    1
    对我来说,这归结于OO“正确性”和关注点分离;在OO意义下,学生管理员(StudentManager)不是学生(Student)。学生类的职责是对数据进行建模,而学生管理员的职责是将该数据持久化,我认为这两者不应混合。但这只是我的观点,可能不正确 :) - JonoW

    0

    我在这里稍微修改了回复,变成了这样:

    public interface IMaintanable<T>
    {
        Guid Create(T obj);
        T Read(Guid key);
        bool Update(T obj);
        bool Delete(Guid key);
    }
    

    这个接口基于我的数据库结构。我使用Guid作为主键。


    0

    看一看最近发布的 Entity Framework 4。他们推出了一个“按约定编码”的模型,可以让您将业务对象直接映射到数据库,而无需担心数据访问层(DAL)。

    “The Gu”有一系列文章详细介绍如何轻松地映射您的对象,甚至在使用它的DbContext模型连接到数据库时进行一些简单的修改。

    值得注意的是,当前版本为CTP4,但我预计该框架的大部分问题已得到解决,并且应该能够很好地服务于您。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接