在StructureMap中选择具有未配置参数的构造函数

时间:2014-06-30 10:15:06

标签: c# dependency-injection structuremap

我很欣赏这个问题标题可能有点含糊不清。我正在尝试使用我自己的功能启发类ResourceFactory<T>来抽象电子邮件提供程序。所以ResourceFactory<T>的工作方式是它的构造函数需要Func<T>这是一个工厂来创建T的实例。 ResourceFactory<T>然后公开一个名为Using<T>(Action<T>)的方法,该方法将采用Action<T>,通过调用构造函数中传递给它的T创建一个新的Func<T>对象,在预定义的调度程序(例如线程池)上调用操作,然后在函数完成时处理创建的T

因此,我的电子邮件提供商期望在其构造函数中有ResourceFactory<SmtpClient>的实例。它也可以重载以接受名为from的字符串,该字符串是电子邮件的发件人地址。代码如下:

public class DotNetEmailProvider : IEmailProvider
{
    // TODO: Don't hard-code the CC
    private readonly ResourceFactory<SmtpClient> _smtpFactory;
    private readonly MailAddress _from;

    /// <summary>
    /// Create the DotNetEmailProvider.
    /// </summary>
    /// <param name="factory"></param>
    public DotNetEmailProvider(ResourceFactory<SmtpClient> factory) : this(factory, "support@rumm.co.uk") { }

    /// <summary>
    /// Create the DotNetEmailProvider.
    /// </summary>
    /// <param name="factory"></param>
    /// <param name="from"></param>
    public DotNetEmailProvider(ResourceFactory<SmtpClient> factory, string from)
    {
        _smtpFactory = factory;
        _from = new MailAddress(from);
    }


    public void SendEmail(string to, string subject, string body)
    {
        // Build the e-mail
        var email = new MailMessage
        {
            Subject = subject,
            Body = body,
            IsBodyHtml = true,
            From = _from
        };

        email.CC.Add(_from);

        _smtpFactory.Invoke(client => client.Send(email));
    }
}

ResourceFactory<T>基本上抽象出Observable.Using,就像这样:

public class ResourceFactory<T> 
    where T : IDisposable
{
    private readonly Func<T> _factory;
    private readonly IScheduler _scheduler;

    /// <summary>
    /// Create the given resource with a factory that will create instances of the resource.
    /// </summary>
    /// <param name="factory"></param>
    public ResourceFactory(Func<T> factory, IScheduler scheduler)
    {
        _factory = factory;
        _scheduler = scheduler;
    }

    /// <summary>
    /// Invoke the given action by creating a new instance of the resource.
    /// </summary>
    /// <param name="invocation"></param>
    public IObservable<Unit> Invoke(Action<T> invocation)
    {
        return Observable.Using(_factory, (t) => Observable.Start(() => invocation(t), _scheduler));
    }
}

尝试使用StructureMap进行设置时出现问题。首先,StructureMap继续尝试使用带有DotNetEmailProvider参数的重载from构造函数。此时我并不关心这一点,所以我进行了一些调查,以了解如何在StructureMap中“选择”构造函数并最终得到此代码:

x.SelectConstructor<DotNetEmailProvider>(() => new DotNetEmailProvider(null));
x.ForConcreteType<DotNetEmailProvider>()
    .Configure
        .Ctor<ResourceFactory<SmtpClient>>().Is(new ResourceFactory<SmtpClient>(() => new SmtpClient(), TaskPoolScheduler.Default));

x.For<IEmailProvider>()
    .Use<DotNetEmailProvider>();

我现在的问题是,现在我收到了这个错误:

{"StructureMap Exception Code:  202\nNo Default Instance defined for PluginFamily System.Reactive.Concurrency.IScheduler, System.Reactive.Interfaces, Version=2.2.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"}

对我来说没有意义,因为,是的,ResourceFactory<T>构造函数确实IScheduler实例作为它的第二个参数,但是我已经通过了在我的配置中(TaskPoolScheduler.Default),我不一定希望每个IScheduler实例在整个应用程序中都是相同的(所以我不想做x.For<IScheduler>().Is(...)

做什么?

编辑:应该注意我知道我的ResourceFactory<T>.Invoke方法在订阅观察者之前不会“做”任何事情,但我的问题仍然存在

1 个答案:

答案 0 :(得分:0)

当我们要求接口IEmailProvider时会抛出异常:

var provider = ObjectFactory.GetInstance<IEmailProvider>();

因此,我们应该更准确地配置IEmailProvider

x.For<IEmailProvider>()
    .Use<DotNetEmailProvider>()
    // plus this
    .Ctor<ResourceFactory<SmtpClient>>().Is(new ResourceFactory<SmtpClient>(
          () => new SmtpClient(), TaskPoolScheduler.Default));

现在StructureMap知道足以返回实例化的 IEmailProvider