草庐IT

c# - 依赖注入(inject)组合根和装饰器模式

coder 2023-07-13 原文

在使用依赖注入(inject)时,我在装饰器模式的实现中遇到了 StackoverflowException。我认为这是因为我对 DI/IoC 的理解“遗漏”了一些东西。

例如,我目前有 CustomerServiceCustomerServiceLoggingDecorator。这两个类都实现了 ICustomerService,装饰器类所做的只是使用注入(inject)的 ICustomerService 但添加了一些简单的 NLog 日志记录,这样我就可以在不影响 中的代码的情况下使用日志记录CustomerService 同时也不违反单一职责原则。

但是这里的问题是,因为 CustomerServiceLoggingDecorator 实现了 ICustomerService,它还需要注入(inject) ICustomerService 的实现才能工作,Unity将继续尝试将其解析回自身,这会导致无限循环,直到它溢出堆栈。

这些是我的服务:

public interface ICustomerService
{
    IEnumerable<Customer> GetAllCustomers();
}

public class CustomerService : ICustomerService
{
    private readonly IGenericRepository<Customer> _customerRepository;

    public CustomerService(IGenericRepository<Customer> customerRepository)
    {
        if (customerRepository == null)
        {
            throw new ArgumentNullException(nameof(customerRepository));
        }

        _customerRepository = customerRepository;
    }

    public IEnumerable<Customer> GetAllCustomers()
    {
        return _customerRepository.SelectAll();
    }
}

public class CustomerServiceLoggingDecorator : ICustomerService
{
    private readonly ICustomerService _customerService;
    private readonly ILogger _log = LogManager.GetCurrentClassLogger();

    public CustomerServiceLoggingDecorator(ICustomerService customerService)
    {
        _customerService = customerService;
    }

    public IEnumerable<Customer> GetAllCustomers()
    {
        var stopwatch = Stopwatch.StartNew();
        var result =  _customerService.GetAllCustomers();

        stopwatch.Stop();

        _log.Trace("Querying for all customers took: {0}ms", stopwatch.Elapsed.TotalMilliseconds);
        return result;
    }
}

我目前有这样的注册设置(这个 stub 方法是由 Unity.Mvc 创建的):

        public static void RegisterTypes(IUnityContainer container)
        {
            // NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.
            // container.LoadConfiguration();

            // TODO: Register your types here
            // container.RegisterType<IProductRepository, ProductRepository>();

            // Register the database context
            container.RegisterType<DbContext, CustomerDbContext>();

            // Register the repositories
            container.RegisterType<IGenericRepository<Customer>, GenericRepository<Customer>>();

            // Register the services

            // Register logging decorators
            // This way "works"*
            container.RegisterType<ICustomerService, CustomerServiceLoggingDecorator>(
            new InjectionConstructor(
                new CustomerService(
                    new GenericRepository<Customer>(
                        new CustomerDbContext()))));

            // This way seems more natural for DI but overflows the stack
            container.RegisterType<ICustomerService, CustomerServiceLoggingDecorator>();

        }

所以现在我不确定实际创建具有依赖注入(inject)的装饰器的“正确”方法。我的装饰器基于 Mark Seemann 的回答 here .在他的示例中,他正在更新几个传递到类中的对象。这就是我的它“工作”* 片段的工作方式。但是,我认为我错过了一个基本步骤。

为什么要像这样手动创建新对象?这是否否定了让容器为我解析的意义?或者我应该在这个方法中执行 contain.Resolve()(服务定位器),以获取仍然注入(inject)的所有依赖项?

我对“复合根”概念略微熟悉,您应该将这些依赖项连接到一个且仅一个位置,然后向下级联到应用程序的较低级别。那么 Unity.Mvc 生成的 RegisterTypes() 是 ASP.NET MVC 应用程序的组合根吗?如果是这样,直接在此处更新对象实际上是正确的吗?

我的印象是,通常使用 Unity 您需要自己创建组合根,但是,Unity.Mvc 是一个异常(exception),因为它创建了自己的组合根,因为它似乎能够在构造函数中将依赖项注入(inject)到具有接口(interface)(例如 ICustomerService)的 Controller 中,而无需我编写代码来实现这一点。

问题:我认为我遗漏了一条关键信息,由于循环依赖,这导致我出现StackoverflowExceptions。如何在仍然遵循依赖注入(inject)/控制反转原则和约定的同时正确实现我的装饰器类?

第二个问题:如果我决定只想在某些情况下应用日志装饰器呢?因此,如果我有 MyController1,我希望有一个 CustomerServiceLoggingDecorator 依赖项,但是 MyController2 只需要一个普通的 CustomerService,如何创建两个单独的注册?因为如果我这样做:

container.RegisterType<ICustomerService, CustomerServiceLoggingDecorator>();
container.RegisterType<ICustomerService, CustomerService>();

然后一个将被覆盖,这意味着两个 Controller 都将注入(inject)装饰器或注入(inject)普通服务。我如何允许两者兼顾?

编辑:这不是一个重复的问题,因为我在循环依赖方面遇到了问题,并且对此缺乏正确的 DI 方法的理解。我的问题适用于整个概念,而不仅仅是像链接问题那样的装饰器模式。

最佳答案

序言

每当您在使用 DI 容器(Unity 或其他)时遇到问题,问问自己:is using a DI Container worth the effort?

在大多数情况下,答案应该是。使用 Pure DI反而。用 Pure DI 回答你所有的答案都是微不足道的。

团结

如果您必须使用 Unity,也许以下内容会对您有所帮助。自 2011 年以来我就没有使用过 Unity,所以从那以后情况可能发生了变化,但在 my book 的第 14.3.3 节中查找问题。 ,这样的事情可能会成功:

container.RegisterType<ICustomerService, CustomerService>("custSvc");
container.RegisterType<ICustomerService, CustomerServiceLoggingDecorator>(
    new InjectionConstructor(
        new ResolvedParameter<ICustomerService>("custSvc")));

或者,您也可以这样做:

container.RegisterType<ICustomerService, CustomerServiceLoggingDecorator>(
    new InjectionConstructor(
        new ResolvedParameter<CustomerService>()));

此替代方案更易于维护,因为它不依赖命名服务,但具有(潜在的)缺点,即您无法通过 ICustomerService 接口(interface)解析 CustomerService .无论如何你可能不应该这样做,所以这应该不是问题,所以这可能是一个更好的选择。

关于c# - 依赖注入(inject)组合根和装饰器模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36048646/

有关c# - 依赖注入(inject)组合根和装饰器模式的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  4. ruby-on-rails - 在 ruby​​ .gemspec 文件中,如何指定依赖项的多个版本? - 2

    我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这

  5. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  6. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  7. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  8. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  9. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  10. ruby-on-rails - environment.rb 中设置的常量在开发模式中消失 - 2

    了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl

随机推荐