是否有任何方法可以将Action注册到DI容器(Autofac)

本文关键字:DI 容器 Autofac 注册 Action 任何 方法 是否 | 更新日期: 2023-09-27 18:13:29

我有一个情况,我需要注入一个动作到一个类的构造函数。由于这种需要,我目前被困在整个应用程序中使用服务定位器模式,而不是简单地使用DI容器来达到预期的目的。

下面是示例

RootPage.cs (master detail page)

public RootPage()
{
    this.Master = new NavigationPage();
    this.Detail = new DetailPage(OnToggleRequest);
}
private Action OnToggleRequest()
{
    IsPresented = !IsPresented;
}

现在我要做的是将NavigationPageDetailPage注册到容器中,然后使用一些自定义逻辑来解析泛型。

PseudoRootPage.cs

public RootPage()
{
    this.Master = MyApp.PageBuilder < NavigationPage > ();
    this.Detail = MyApp.PageBuilder < DetailPage > ();
}

问题在于我需要在DetailPage中使用Action来切换导航菜单。

是否有办法将Action注册到DI容器中?

另一个选择是使OnToggleRequest和internal static

是否有任何方法可以将Action注册到DI容器(Autofac)

不确定我100%理解这与Master Detail Pages相关的所有复杂性,但是如何使用AutoFac 动态实例化 ?

public class RootPage
{
   public RootPage(INavigationPage navigationPage,
      Func<Action, IDetailPage> detailPageFactory)
   {
      var detailPage = detailPageFactory(myAction);
   }
}

当autofacc看到Func<Action, IDetailPage>构造函数参数时,它传递一个委托,作为IDetailPage的工厂方法。

。它将把您发送给工厂的Action参数通过IDetailPage类型的构造函数传递给工厂。

更多详细信息请访问:http://nblumhardt.com/2010/01/the-relationship-zoo/

听起来你需要的是一个组件工厂。

DetailPage中,您创建了一个委托属性,它将充当页面的工厂。我还没有这样做,但你可以把它设为静态(就像工厂方法/委托应该的那样)。我基本上是在提供Autofac给出的例子,为您的类型量身定制。

public delegate Shareholding Factory(Action toggleRequest);

然后将Detail Page注册到IoC容器中。

var builder = new ContainerBuilder();
builder.RegisterType<DetailPage>();
var container = builder.Build();

和最终解决您的DetailPage在您的RootPage .

var DetailPageFactory = container.Resolve<DetailPage.Factory>();
this.Detail = detailPageFactory.Invoke(OnToggleRequest);

希望这就是你要找的

如果您想要注入的Action的实现是静态的,那真的很容易。通常我们不会把静态方法和注入放在一起,但是你依赖的是一个可以被替换的抽象,所以这一点都不重要。

我建议声明一个与你的操作相对应的委托。否则,如果你必须要注入两个动作,并且它们都具有相同的签名,那么它们是无法区分的。这也使你注册的内容更清晰。

builder.Register<DelegateForSomeAction>(context => MyStaticClass.MyStaticMethod);

如果我们想注入的动作属于一个必须解析的类,我们也可以这样做:

首先声明要使用的委托。这类似于声明一个接口,只不过它只是用于操作:

public delegate int DoMath(Single value1, Single value2);

为了便于说明,这里有一个类,其方法实现了委托:

public class AddsNumbers 
{
    public Single Add(Single value1, Single value2)
    {
        return value1 + value2;
    }
}

然后注册包含要注入的方法的类型:

builder.RegisterType<AddsNumbers>();

然后注册委托的实现:

builder.RegisterType<AddsNumbers>();
builder.Register<DoMath>(c =>
{
    var componentContext = c.Resolve<IComponentContext>();
    var addsNumbers = componentContext.Resolve<AddsNumbers>();
    return addsNumbers.Add;
});

这告诉容器,当您需要DoMath委托的实例时,它应该解析AddsNumbers的实例并返回其Add方法。

这有点乱。这是一个扩展Autofac,让你从编写相同的代码:

{ 
    public static IRegistrationBuilder<TDelegate, SimpleActivatorData, SingleRegistrationStyle> RegisterDelegate<TDelegate, TSource>( 
        this ContainerBuilder builder,  
        Func<TSource, TDelegate> extractDelegate,  
        string sourceComponentName = null,  
        string registeredComponentName = null)  
        where TDelegate : class
    {
        var registrationFunction = new Func<IComponentContext, TDelegate>(context => 
        { 
            var c = context.Resolve<IComponentContext>(); 
            var source = sourceComponentName == null 
                ? c.Resolve<TSource>() 
                : c.ResolveNamed<TSource>(sourceComponentName); 
            return extractDelegate(source); 
        }); 
        return registeredComponentName == null ? 
            builder.Register(registrationFunction) : 
            builder.Register(registrationFunction) 
                .Named<TDelegate>(registeredComponentName); 
    } 
}
现在实际的注册代码要简单得多:
builder.RegisterDelegate<DoMath, AddsNumbers>(addsNumbers => addsNumbers.Add);

扩展方法也有一些额外的参数,以防您需要

  • 解析委托驻留的组件的命名实例
  • 指定要注册的委托组件

我更喜欢这种方法,因为如果你的类依赖于一个动作(委托),它应该只依赖于它,而不是依赖于autofacc创建工厂所需的一些奇怪的接口。所有这些神奇的东西都应该移到composition根目录,这样类就能得到它想要的东西。

更多细节和示例