У меня есть похожий случай, связанный с "дорогим созданием, и может * использоваться ", где в моей собственной реализации IoC я добавляю автоматическую поддержку сервисов фабрики.
В основном вместо этого:
public SomeService(ICDBurner burner)
{
}
вы бы сделали это:
public SomeService(IServiceFactory<ICDBurner> burnerFactory)
{
}
ICDBurner burner = burnerFactory.Create();
Это имеет два преимущества:
- За кулисами служебный контейнер, который разрешил вашу службу, также используется для разрешения записывающего устройства, если и когда он запрашивается
- Это снимает проблемы, с которыми я сталкивался ранее в подобном случае, когда типичным способом было бы внедрить сам контейнер служб в качестве параметра для вашей службы, в основном говоря: «Эта служба требует других служб, но я не легко скажу, какие из них "
Заводской объект довольно прост в изготовлении и решает множество проблем.
Вот мой заводской класс:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LVK.IoC.Interfaces;
using System.Diagnostics;
namespace LVK.IoC
{
/// <summary>
/// This class is used to implement <see cref="IServiceFactory{T}"/> for all
/// services automatically.
/// </summary>
[DebuggerDisplay("AutoServiceFactory (Type={typeof(T)}, Policy={Policy})")]
internal class AutoServiceFactory<T> : ServiceBase, IServiceFactory<T>
{
#region Private Fields
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly String _Policy;
#endregion
#region Construction & Destruction
/// <summary>
/// Initializes a new instance of the <see cref="AutoServiceFactory<T>"/> class.
/// </summary>
/// <param name="serviceContainer">The service container involved.</param>
/// <param name="policy">The policy to use when resolving the service.</param>
/// <exception cref="ArgumentNullException"><paramref name="serviceContainer"/> is <c>null</c>.</exception>
public AutoServiceFactory(IServiceContainer serviceContainer, String policy)
: base(serviceContainer)
{
_Policy = policy;
}
/// <summary>
/// Initializes a new instance of the <see cref="AutoServiceFactory<T>"/> class.
/// </summary>
/// <param name="serviceContainer">The service container involved.</param>
/// <exception cref="ArgumentNullException"><paramref name="serviceContainer"/> is <c>null</c>.</exception>
public AutoServiceFactory(IServiceContainer serviceContainer)
: this(serviceContainer, null)
{
// Do nothing here
}
#endregion
#region Public Properties
/// <summary>
/// Gets the policy that will be used when the service is resolved.
/// </summary>
public String Policy
{
get
{
return _Policy;
}
}
#endregion
#region IServiceFactory<T> Members
/// <summary>
/// Constructs a new service of the correct type and returns it.
/// </summary>
/// <returns>The created service.</returns>
public IService<T> Create()
{
return MyServiceContainer.Resolve<T>(_Policy);
}
#endregion
}
}
По сути, когда я создаю контейнер служб из своего класса конструктора контейнеров служб, все регистрации служб автоматически получают другую сопутствующую услугу, реализующую IServiceFactory для этой службы, если только программист явно не зарегистрировался в себе для этой службы , Затем используется вышеупомянутая служба с одним параметром, определяющим политику (которая может быть нулевой, если политики не используются).
Это позволяет мне сделать это:
var builder = new ServiceContainerBuilder();
builder.Register<ISomeService>()
.From.ConcreteType<SomeService>();
using (var container = builder.Build())
{
using (var factory = container.Resolve<IServiceFactory<ISomeService>>())
{
using (var service = factory.Instance.Create())
{
service.Instance.DoSomethingAwesomeHere();
}
}
}
Конечно, более типичное использование будет с вашим объектом CD Burner. В приведенном выше коде я бы разрешил службу вместо, конечно, но это иллюстрация того, что происходит.
Так что с вашим сервисом записи компакт-дисков:
var builder = new ServiceContainerBuilder();
builder.Register<ICDBurner>()
.From.ConcreteType<CDBurner>();
builder.Register<ISomeService>()
.From.ConcreteType<SomeService>(); // constructor used in the top of answer
using (var container = builder.Build())
{
using (var service = container.Resolve<ISomeService>())
{
service.Instance.DoSomethingHere();
}
}
внутри службы теперь вы можете иметь службу, заводскую службу, которая знает, как разрешить службу записи компакт-дисков по запросу. Это полезно по следующим причинам:
- Возможно, вы захотите разрешить несколько служб одновременно (записать два диска одновременно?)
- Возможно, вам это не нужно, и его создание может быть дорогостоящим, поэтому вы можете разрешить его только , если необходимо
- Возможно, вам придется разрешать, удалять, разрешать, удалять несколько раз, вместо того, чтобы надеяться / пытаться очистить существующий экземпляр службы
- Вы также отмечаете в своем конструкторе, какие услуги вам нужны , а какие вам могут понадобиться
Вот два одновременно:
using (var service1 = container.Resolve<ISomeService>())
using (var service2 = container.Resolve<ISomeService>())
{
service1.Instance.DoSomethingHere();
service2.Instance.DoSomethingHere();
}
Вот два после друг друга, без повторного использования одного и того же сервиса:
using (var service = container.Resolve<ISomeService>())
{
service.Instance.DoSomethingHere();
}
using (var service = container.Resolve<ISomeService>())
{
service.Instance.DoSomethingElseHere();
}