I appreciate the question title might be a bit ambiguous. I'm working on trying to abstract an email provider using my own functionally-inspired class ResourceFactory<T>
. So the way ResourceFactory<T>
works is that it's constructor takes a Func<T>
which is a factory to create an instance of T
. ResourceFactory<T>
then exposes a method called Using<T>(Action<T>)
, which will take an Action<T>
, create a new T
object by invoking the Func<T>
passed to it in the constructor, invoke the action on a pre-defined scheduler (such as the thread pool) and then dispose of the created T
when the function has finished.
So, my email provider is expecting an instance of ResourceFactory<SmtpClient>
in it's constructor. It can also be overloaded to accept a string called from
, which is the from address of the e-mail. Code is below:
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>
just basically abstracts away Observable.Using
, like so:
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));
}
}
My issue comes when trying to set this up using StructureMap. First of all, StructureMap kept trying to use the overloaded DotNetEmailProvider
constructor with the from
argument. At this point I didn't care about that, so I investigated a little bit to find out how to 'select' a constructor in StructureMap and ended up with this code:
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>();
My problem now is that now I'm getting this error thrown:
{"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"}
Which to me doesn't make sense because, yes, the ResourceFactory<T>
constructor does take an IScheduler
instance as it's second argument, however I'm already passing that in in my configuration ( TaskPoolScheduler.Default
), and I don't necessarily want every IScheduler instance to be the same throughout the application (so I dont' want to do x.For<IScheduler>().Is(...)
What do?
EDIT: It should be noted I am aware that my ResourceFactory<T>.Invoke
method won't "do" anything until the observable is subscribed to, however my issue still remains
The exception is thrown when we do ask for interface IEmailProvider
like:
var provider = ObjectFactory.GetInstance<IEmailProvider>();
So, we should configure the IEmailProvider
a bit more precise way
x.For<IEmailProvider>()
.Use<DotNetEmailProvider>()
// plus this
.Ctor<ResourceFactory<SmtpClient>>().Is(new ResourceFactory<SmtpClient>(
() => new SmtpClient(), TaskPoolScheduler.Default));
Now StructureMap
knows enough to return instantiated IEmailProvider
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.