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

发布于 2024-12-26 13:09:20 字数 828 浏览 2 评论 0原文

我正在开发一项服务,该服务通过存储库执行数据库操作。在服务中,我实例化了存储库,该存储库需要构造函数中的数据库上下文。我想知道上下文是否应该传递到服务中,或者下面的代码是否正确。或者将 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();
        }
    }

I am working on a service, which performs database operations through a repository. In the service I instantiate the repository which requires a database context in the constructor. I wanted to know if the context should be passed into the Service, or if the code below is fine. Or would it be better to pass a Repository object to the service for it to use? What should the UI code look like when using the Service class?

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();
        }
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

影子是时光的心 2025-01-02 13:09:20

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

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 */
}

接下来您需要做的是配置一个控制反转容器,它将知道如何解析 IRepository 实例(并在需要时向它们提供适当的 DataContext 实例)。

例如,如果我们的 UI 代码是 MVC3 控制器,我们会告诉容器解析该控制器的实例。发生的情况如下:

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

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

  • 由于这是一个接口,因此 IRepository 的解析要求容器事先已设置为“知道”当请求其实例时要返回什么。通常,这只是接口与其具体实现之间的映射。在我们的例子中,具体实现是 Repository 并且容器还负责“知道”如何实例化它所需的 DataContext 实例(这也必须是由于

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

请注意,具体类的自动解析并不是所有 IoC 容器都具备的功能;有些需要显式配置才能执行此操作。

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

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

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

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

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

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

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

Indeed, it would be better to pass a repository to the Service via the Service's constructor, like so:

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

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

    /* the rest is unchanged */
}

Tipically, the class representing the UI would take a dependency on Service, so the code might look like this:

public class UIClass : BaseClassDictatedByCurrentUIFramework
{
    private readonly Service _service;

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

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

What you have to do next is configure an Inversion of Control container that will know how to resolve instances of IRepository (and feeding them appropriate DataContext instance, if needed).

For example, if our UI code would be an MVC3 controller, we'd tell the container to resolve an instance of this controller. This is what happens:

  • The container notices the dependency on Service (in the constructor) and tries to resolve it.

  • Since Service is a concrete class, the container will attempt to resolve it and then
    notice the dependency on IRepository<WWW>.

  • The resolution of IRepository, since this is an interface, requires that the container has been previously set up to "know" what to return when it's asked for an instance of it. Normally, this is just a mapping between the interface and a concrete implementation of it. In our case the concrete implementation is Repository<WWW> and the container is also responsible for "knowing" how to instantiate the needed DataContext instance for it (this also has to be previously configured)

  • Having a repository instance, the container is then able to properly instantiate first the Service, then the controller class.

Note that the automatic resolution of concrete classes is a feature not all IoC containers have; some require explicit configuration in order to do so.

Apart from this, I think the Service class does not add much value in your case. It only contains delegations to methods implemented by the repository. In this case it could be better to have the UI take a dependency on the IRepository<WWW> directly and simply delete the Service class.
However, if this Service class was just an example and in your real project it implements actual business rules, then it should be kept.

Update: How to resolve dependencies in ASP.Net Webforms

The example I presented above is the ideal Dependency Injection scenario. It would work for example in ASP.NET MVC, where BaseClassDictatedByCurrentUIFramework would be Controller - in that case the framework allows us to control the components that instantiate Controllers, so we can inject our own dependencies in the constructor.

However, ASP.Net WebForms is not a very DI-friendly framework. It requires that every Page is required to have a default constructor, which makes all the constructor injection idea not suitable.

In this case one possible solution would be the following compromise:

  • instantiate the IoC container at application startup (in Globals.asax) and make it available via some global state (for example, the Application context)
  • in a Page class declare dependencies as private readonly fields, but the constructor will not have corresponding parameters (it will have no parameters at all)
  • in the constructor body:
    • get a reference to the IoC container (available from global state)
    • use the container to resolve all dependencies the class has

Please note that this approach requires discipline - it is easy to use the container somewhere else that the constructor to resolve other components. This approach is called Service Locator, it's considered an antipattern (http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx) and should therefore be avoided.

℡Ms空城旧梦 2025-01-02 13:09:20

我建议加大依赖倒置。就我个人而言,我认为这不是您是否应该向服务传递 DBContext 的问题,而是您应该向服务传递 Repository 本身的问题。

I would suggest one-upping the dependency inversion. Personally, I view it not as whether you should pass the service the DBContext or not, but that you should pass the service the Repository itself.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文