在构造函数中自动执行多个func

时间:2018-01-03 17:48:30

标签: c# asp.net-web-api autofac

我甚至不确定提出这个问题的最好方法。我希望一个解析器能够接受多个在请求时创建对象的函数。我遇到的问题是,在使用Autofac注册解析器时,我不知道如何传递多个函数。

我如何使用函数注册此解析器来创建每个活动? (ActivityAggregate只是简单的对象注册。那里没有任何帮助。)

public class ActivityResolver
{
    Func<ActivityAggregate, IActivity> _getActivityOne;
    Func<ActivityAggregate, IActivity> _getActivityTwo;
    Func<ActivityAggregate, IActivity> _getActivityThree;
    ActivityAggregate _activityAggregate;

    public ActivityResolver(Func<ActivityAggregate, IActivity> getActivityOne, Func<ActivityAggregate, IActivity> getActivityTwo, Func<ActivityAggregate, IActivity> getActivityThree,
        ActivityAggregate aggregate)
    {
        _getActivityOne = getActivityOne;
        _getActivityTwo = getActivityTwo;
        _getActivityThree = getActivityThree;
        _activityAggregate = aggregate;
    }

    public IActivity GetActivity(ActivityType activityType)
    {
        switch (activityType)
        {
            //The choice between activity one and two is config driven. Providing either one will then go to the configuration settings to make the final choice.
            //We have this scenario to decide between using a legacy activity or the newly created version that may need to be switched off via config without a code push.
            case ActivityType.One:
            case ActivityType.Two:
                if (_activityAggregate.ConfigReader.SelectBooleanValue("ConfigSettting.UseActivityOne", true))
                    return _getActivityOne(_activityAggregate);
                else
                    return _getActivityTwo(_activityAggregate);

            case ActivityType.Three:
                return _getActivityThree(_activityAggregate);
            default:
                throw new NotImplementedException(string.Format("ActivityResolver does not recognize activity type {0}", activityType));
        }
    }
}

在为autofac重新设计之前,创建活动对象的其中一个func将如下所示:

public static IActivity GetActivityOne(ActivityAggregate aggregate)
{
    return new ActivityOne(aggregate);
}
编辑:我找到了一个如何使用Unity完全符合我要求的例子。希望有人看到用Autofac做同样事情的方法。

以下是Unity示例:

private static IUnityContainer BuildUnityContainer()
{
    var container = new UnityContainer();

    container.RegisterType<IExampleService, ExampleService>("default");
    container.RegisterType<IExampleService, AnotherExampleService>("another");

    container.RegisterType<Func<string, IExampleService>>(
        new InjectionFactory(c => 
        new Func<string, IExampleService>(name => c.Resolve<IExampleService>(name))));

    container.RegisterControllers();

    return container;
}

然后是示例电话:

public class HomeController
{
  private IExampleService _service;

  public HomeController(Func<string, IExampleService> serviceFactory)
  {
    var exampleServiceImplementation = "default"; // TODO get dynamically
    _service = serviceFactory(exampleServiceImplementation);
  }

  public ActionResult Index()
  {
    return View(_service.GetSomething());
  }         
}

2 个答案:

答案 0 :(得分:2)

好而不是将多个func作为参数传递,只需将一个func作为构造函数中的依赖项传递,并且该func可以将ActivityType和ActivityAggregate作为输入参数并返回IActivity。这样,您将只向构造函数中注入一个依赖项。使用输入参数注册func的方法如下: -

services.AddScoped(factory =>
            {
                IHusbandryTaskMapper GetHusbandryTaskMapper(HusbandryTaskSubType subType)
                {
                    switch (subType)
                    {
                        case HusbandryTaskSubType.AnimalCull:
                            return new HusbandryCullAnimalTaskMapper(factory.GetService<IUserIdentityResolver>(), factory.GetService<ILanguageProvider>());
                        case HusbandryTaskSubType.CageChange:
                        case HusbandryTaskSubType.CensusCheck:
                            return new CageTaskMapper(factory.GetService<IUserIdentityResolver>(), factory.GetService<ILanguageProvider>());
                        default:
                            throw new NotImplementedException();
                    }
                }

                return (Func<HusbandryTaskSubType, IHusbandryTaskMapper>)GetHusbandryTaskMapper;
            });

然后在构造函数中注入如下

public ActivityResolver(Func<HusbandryTaskSubType, IHusbandryTaskMapper>)taskMapper,
        ActivityAggregate aggregate)
    {
       _taskMapper = taskMapper;
       _activityAggregate = aggregate;
    }

使用如下

public IActivity GetActivity(ActivityType activityType)
    {
        return _taskMapper(activity, _activityAggregate );
    }

答案 1 :(得分:2)

问题的初始版本表明您希望ActivityResolver具有三个相同类型的不同参数:

public ActivityResolver(
    Func<ActivityAggregate, IActivity> getActivityOne,
    Func<ActivityAggregate, IActivity> getActivityTwo,
    Func<ActivityAggregate, IActivity> getActivityThree,
    ActivityAggregate aggregate)

根据原始要求,存在挑战。从类型参数的角度来看,除非您使用lambda注册ActivityResolver或以其他方式非常严格地控制参数(例如使用Autofac ResolvedParameter),否则您将无法获得所需内容。所有参数类型都相同,因此基本反射注册将始终只为您提供相同值的三个副本。也就是说,getActivityOnegetActivityTwogetActivityThree都将是相同内容的相同实例,因为它们都是相同的类型,没有其他区别。

This FAQ touches on what it appears you're trying to do there并且可能会给你一些额外的想法。

您问题的更新版本会改变游戏。这绝对表明如何以不同的方式思考某些事情可以打开答案......并且可能会改变您将来提问的方式。

更新后的版本显示了一个控制器,它根据某些输入解析了一个不同的对象 - 它不是三个不同的函数,它只是一个只能获得正确解析对象的函数。

public HomeController(Func<string, IExampleService> serviceFactory)
“关键服务”部分中的

That is totally covered in that FAQ I linked 也指向section of the docs on implicit relationship types。从技术上讲,您也可以使用元数据。

这两个例子都是通过Autofac文档,但是the easiest way to go is probably metadata

首先,使用已知的元数据注册所有不同的内容。

builder.RegisterType<ActivityOne>()
       .As<IActivity>()
       .WithMetadata("activity", ActivityType.One);
builder.RegisterType<ActivityTwo>()
       .As<IActivity>()
       .WithMetadata("activity", ActivityType.Two);
builder.RegisterType<ActivityThree>()
       .As<IActivity>()
       .WithMetadata("activity", ActivityType.Three);

在构造函数中,您可以使用IEnumerable<Meta<Lazy<IActivity>>>参数。乍一看这有点令人生畏,但它只是Autofac built-in relationship support composing a bunch of things together for you。在这种情况下,您需要:

  • 您注册的所有IActivity类型(IEnumerable<T>
  • 附加元数据,以便查询(Meta<T>
  • 但实际上并没有解决它们因为你不知道你想要哪一个(Lazy<T>

public ActivityResolver(IEnumerable<Meta<Lazy<IActivity>>> activities)

然后在你的解析器方法中,你可以使用LINQ。

public IActivity GetActivity(ActivityType activityType)
{
    return this._activities
      .First(m => m.Metadata["activity"].Equals(activityType))
      .Value // The value from the Meta<T> object is a Lazy<T>
      .Value; // This resolves the T from Lazy<T>
}

显然可以根据需要处理错误,但重点是你可以通过改变你对问题的思考方式来回避如何将三个相同的东西注入构造函数的问题。密钥服务,元数据等等。

再次, it is well worth cruising the Autofac docs 。我知道有很多。我写了大部分内容。有很多因为Autofac支持许多功能。几乎在每种情况下,我们都会尽力在文档中展示示例,但您也应始终consider the unit tests a good source of examples。哦,我们有a whole repo that's dedicated to just showing examples.

熟悉这些功能将帮助您解决一些听起来更复杂的问题,这些问题听起来似乎正在进行中,并且可能有助于更改问题而不是强迫方形挂钩圆孔。