使用Autofac依赖注入的工厂

3

我希望编写一个工厂,用于创建各种类型的“xxNotification”类。我的具体“xxNotification”类已经在AutoFac中注册了依赖项。我想使用Autofac获取/解析“xxNotification”的实例。如何做到这一点?

public interface INotification
{
    void Notify(string Action, int OrderID);
}

public class MagentoOrderStateNotification : INotification
{
    private readonly GenericRepository<Order> _orderRepository;
    private readonly OMRestIntegrationService _oMRestIntegrationService;

    public MagentoOrderStateNotification(GenericRepository<Order> orderRepository, OMRestIntegrationService oMRestIntegrationService)
    {
        _orderRepository = orderRepository;
        _oMRestIntegrationService = oMRestIntegrationService;
    }

    public void Notify(string Action, int OrderID)
    {
        //implementation...
    }
}

public class NotificationFactory
{
    public INotification GetNotification(string NotificationName)
    {
        switch (NotificationName)
        {
            case "MagentoOrderStateNotification":
                //resolve instance of MagentoOrderStateNotification
        }
    }
}

1
通常情况下,您不会从 DI 容器中“获取”对象。这是一种已知的反模式,称为服务定位器。相反,在启动时组合应用程序,包括工厂及其依赖项。这被称为好莱坞原则:别打电话给我们,我们会打电话给你 - NightOwl888
好的,那么如果我需要根据某些配置在运行时实例化一个服务,比如“对于这个订单状态,调用那个 INotification”怎么办? - vpetrovic
您可以通过方法参数将运行时订单状态传递到您的服务中。在您的服务中,您可以使用创建对象模式策略模式或者仅使用泛型来选择要使用的服务或服务。根据情况,您还可以组合模式 - NightOwl888
1个回答

3
我可以帮忙翻译。这段文字是关于编程的,建议使用策略模式

接口

public interface INotification
{
    void Notify(string Action, int OrderID);
    bool AppliesTo(string NotificationName);
}

public interface INotificationStrategy
{
    void Notify(string NotificationName, string Action, int OrderID);
}

INotification实现

public class MagentoOrderStateNotification : INotification
{
    private readonly GenericRepository<Order> _orderRepository;
    private readonly OMRestIntegrationService _oMRestIntegrationService;

    public MagentoOrderStateNotification(GenericRepository<Order> orderRepository, OMRestIntegrationService oMRestIntegrationService)
    {
        _orderRepository = orderRepository;
        _oMRestIntegrationService = oMRestIntegrationService;
    }

    public void Notify(string Action, int OrderID)
    {
        //implementation...
    }

    public bool AppliesTo(string NotificationName)
    {
        // Note that you can make the criteria to use this
        // service as advanced as you need to. You could even
        // design your strategy to use multiple services in specific
        // scenarios. But putting that logic here has many advantages
        // over a switch case statement.
        return NotificationName == "a";
    }
}

public class FooOrderStateNotification : INotification
{
    public void Notify(string Action, int OrderID)
    {
        //implementation...
    }

    public bool AppliesTo(string NotificationName)
    {
        return NotificationName == "b";
    }
}

策略

public class NotificationStrategy : INotificationStrategy
{
    private readonly IEnumerable<INotification> oNotifications;

    public MessageStrategy(IEnumerable<INotification> oNotifications)
    {
        if (oNotifications == null)
            throw new ArgumentNullException("oNotifications");
        this.oNotifications = oNotifications;
    }

    public void Notify(string NotificationName, string Action, int OrderID)
    {
        var notification = this.oNotifications
            .FirstOrDefault(x => x.AppliesTo(NotificationName));

        // Possible alternative: get multiple implementations and
        // then loop through them, executing each one.
        // var notifications = this.oNotifications
        //     .Where(x => x.AppliesTo(NotificationName)).ToArray();

        if (notification == null)
        {
            throw new Exception("No notification type registered for " + NotificationName);
        }

        notification.Notify(Action, OrderID);
    }
}

使用方法

public class SomeService : ISomeService
{
    private readonly INotificationStrategy oNotificationStrategy;

    public SomeService(INotificationStrategy oNotificationStrategy)
    {
        if (oNotificationStrategy == null)
            throw new ArgumentNullException("oNotificationStrategy");
        this.oNotificationStrategy = oNotificationStrategy;
    }

    public void DoSomething()
    {
        this.oNotificationStrategy.Notify("a", "Hello", 1234);
    }
}

请注意,另一种方法是使用基于OrderID的“OrderType”状态查找要使用的服务(不确定将OrderID传递到Notify()方法中是否实际有意义)。但无论如何,这是一个应用程序设计问题,而不是DI问题。

Autofac注册

您可以使用几种方法配置Autofac以注册同一接口的多个实现,以便在NotificationStrategy构造函数中使用。以下是几个示例:

扫描

// Note you may need more than one registration if you are spanning
// multiple assemblies.
builder.RegisterAssemblyTypes(typeof(INotification).Assembly)
   .Where(x => typeof(INotification).IsAssignableFrom(x))
   .AsImplementedInterfaces();

当以这种方式注册实现时,Autofac将隐式地将所有实现注入到任何IEnumerable<INotification>构造函数参数中。

挑选

builder.Register<MagentoOrderStateNotification>()
    .Named<INotification>("magentoOrderStateNotification");
builder.Register<FooOrderStateNotification>()
    .Named<INotification>("fooOrderStateNotification");

builder.RegisterType<NotificationStrategy>()
            .As<INotificationStrategy>()
            .WithParameter(
                (p, c) => p.Name == "oNotifications",
                (p, c) => new[]
                    {
                        c.ResolveNamed<INotification>("magentoOrderStateNotification"),
                        c.ResolveNamed<INotification>("fooOrderStateNotification")
                    });

如果我只想基于某些运行时条件(例如,由 CliFx 指定的子命令)注册一个策略实现,该怎么办?从组合根中很难知道应该创建哪个具体实现。 - void.pointer
@void.pointer - 这就是AppliesTo方法的作用。在启动时注册所有策略实例,然后通过将过滤逻辑放入AppliesTo方法来在运行时选择一个或多个实例进行使用。由于所有实例始终被加载,因此通常最好将对象数量限制在几百个以内,超过这个数量,您可能最好使用抽象工厂。 - NightOwl888
所以基本上在我的情况下,您认为这样做不会有滥用的行为:builder.Register(c => c.Resolve<IEnumerable<ICommand>>().First(cmd => cmd.IsActive)).As<ICommand>()?然后在构造函数中,我可以这样做:public MyObject(ICommand cmd),并获取用户在命令行中选择的命令?到目前为止,我一直在使用IActiveCommandProvider,但是您的想法更加简洁。 - void.pointer
一般来说,我对于带有 DI 的控制台应用程序的建议是使用容器来解析代表整个应用程序的 1 个对象,并且该对象具有 Run(object[] args) 参数。一旦解析了该对象,应用程序就不知道存在容器。 - NightOwl888
话虽如此,我有需要构建一个命令行应用程序而不必重新发明轮子,最终我选择了 Microsoft.Extensions.CommandLineUtils 的定制版本。它没有遵循良好的 DI 实践,但处理了所有关于命令、选项和帮助的细节。 - NightOwl888
显示剩余2条评论

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