创建通过存储库执行数据库调用的服务的正确方法是什么?

时间:2012-01-12 22:25:16

标签: c# asp.net design-patterns repository

我正在开发一个服务,它通过存储库执行数据库操作。在服务中,我实例化了需要构造函数中的数据库上下文的存储库。我想知道上下文是否应该传递到服务中,或者下面的代码是否正常。或者将Repository对象传递给服务以供它使用会更好吗?使用Service类时,UI代码应该是什么样的?

public class Service
    {
        private IRepository<WWW> _repository;

        public Service()
        {
            _repository = new Repository<WWW>(new DBContext());
        }

        public WWW GetWWW(int wwwID)
        {
            return _repository.Get(x => x.WWWID == wwwID).FirstOrDefault();
        }

        public void AddWWW(WWW www)
        {
            _repository.Add(www);
        }

        public void DeleteWWWByID(int wwwID)
        {
            _repository.Delete(x => x.WWWID == wwwID);
        }

        public void SaveChanges()
        {
            _repository.SaveChanges();
        }
    }

2 个答案:

答案 0 :(得分:2)

事实上,最好通过服务的构造函数将存储库传递给服务,如下所示:

public class Service
{
    private readonly IRepository<WWW> _repository;

    public Service(IRepository<WWW> repository)
    {
        _repository = repository;
    }

    /* the rest is unchanged */
}

简而言之,表示UI的类将依赖于Service,因此代码可能如下所示:

public class UIClass : BaseClassDictatedByCurrentUIFramework
{
    private readonly Service _service;

    public UIClass(Service service)
    {
        _service = service;
    }

    /* UI code that will eventually call methods on the service */
}

接下来要做的是配置一个Inversion of Control容器,该容器将知道如何解析IRepository的实例(并在需要时为它们提供适当的DataContext实例)。

例如,如果我们的UI代码是MVC3控制器,我们会告诉容器解析此控制器的实例。这就是:

  • 容器注意到对Service的依赖(在构造函数中)并尝试解决它。

  • 由于Service是一个具体类,容器将尝试解析它 注意对IRepository<WWW>的依赖。

  • IRepository的分辨率,因为这是一个界面,要求容器先前已设置为“知道”当它被要求提供它的实例时要返回的内容。通常,这只是接口与其具体实现之间的映射。在我们的例子中,具体实现是Repository<WWW>,容器还负责“知道”如何为它实例化所需的DataContext实例(这也必须事先配置)

  • 拥有一个存储库实例,然后容器能够首先正确地实例化Service,然后是控制器类。

请注意,具体类的自动解析是一个并非所有IoC容器都具有的功能;有些需要显式配置才能这样做。

除此之外,我认为Service类在你的案例中没有增加太多价值。它仅包含对存储库实现的方法的委派。在这种情况下,让UI直接依赖IRepository<WWW>并简单地删除Service类可能会更好。 但是,如果这个Service类只是一个示例,并且在您的实际项目中它实现了实际的业务规则,则应该保留它。

更新:如何解决ASP.Net Webforms中的依赖关系

上面介绍的示例是理想的依赖注入方案。它可以在ASP.NET MVC中工作,其中BaseClassDictatedByCurrentUIFramework将是Controller - 在这种情况下,框架允许我们控制实例化控制器的组件,因此我们可以在构造函数中注入我们自己的依赖项。

然而,ASP.Net WebForms不是一个非常友好的框架。它要求每个Page都需要一个默认的构造函数,这使得所有构造函数注入的想法都不合适。

在这种情况下,一种可能的解决方案是以下妥协:

  • 在应用程序启动时(在Globals.asax中)实例化IoC容器,并通过某种全局状态(例如,应用程序上下文)使其可用
  • Page类中将依赖关系声明为private readonly字段,但构造函数将没有相应的参数(它根本没有参数)
  • 在构造函数体中:
    • 获取对IoC容器的引用(可从全局状态获得)
    • 使用容器来解析类具有的所有依赖项

请注意,这种方法需要规范 - 在构造函数解析其他组件的其他地方容易使用容器。这种方法称为服务定位器,它被认为是反模式(http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx),因此应该避免使用。

答案 1 :(得分:0)

我建议一次性依赖倒置。就个人而言,我认为这不是你是否应该将服务传递给DBContext,而是你应该将服务传递给存储库本身。