Предоставляет ли структурная карта возможность делать инъекцию конструктора ленивым способом? Значение не создает объект, который вводится до его использования?
Поддерживает ли Structuremap Lazy из коробки?
Ответ 1
UPDATE: StructureMap v3 реализует это из коробки, поэтому этот трюк больше не нужен.
StructureMap версии 2 нет, но с несколькими трюками вы можете заставить его делать то, что, как я считаю, вы ищете. Прежде всего, вы уже можете подключить экземпляры Lazy<T>
вручную следующим образом:
container = new Container(x =>
{
x.Scan(y =>
{
y.TheCallingAssembly();
y.WithDefaultConventions();
});
x.For<Lazy<IFoo>>().Use(y => new Lazy<IFoo>(y.GetInstance<Foo>));
x.For<Lazy<IBar>>().Use(y => new Lazy<IBar>(y.GetInstance<Bar>));
x.For<Lazy<IBaz>>().Use(y => new Lazy<IBaz>(y.GetInstance<Baz>));
});
Это работает отлично, но вы должны регистрировать каждый тип отдельно. Было бы лучше, если бы вы могли использовать более основанный на конвенциях подход. В идеале, следующий синтаксис будет приятным.
x.For(typeof(Lazy<>)).Use(typeof(Lazy<>));
Этот синтаксис фактически работает... несколько. К сожалению, во время выполнения StructureMap попытается найти "самый жадный" конструктор для Lazy<T>
и опуститься на public Lazy(Func<T> valueFactory, bool isThreadSafe)
. Поскольку мы не говорили, что делать с логическим параметром isThreadSafe, он будет генерировать исключение, когда он пытается разрешить "Lazy".
В документации для Lazy указано, что "режим безопасности потока" конструктора по умолчанию Lazy(Func<T> valueFactory)
равен LazyThreadSafetyMode.ExecutionAndPublication
, который, как раз так происходит, является тем, что вы получаете, передавая true в параметр isThreadSafe конструктора выше. Итак, если бы мы могли просто передать StructureMap для передачи true
для isThreadSafe
, мы получим такое же поведение, как если бы мы назвали конструктор, который мы действительно хотели использовать в первую очередь (например, Lazy(Func<T> valueFactory)
).
Простое регистрацию x.For(typeof(bool)).Use(y => true)
было бы очень безрассудным и опасным, так как мы будем говорить StructureMap, чтобы продолжить и использовать значение true
для любого булева в любом месте. Вместо этого нам нужно указать StructureMap, какое значение использовать только для этого одного логического параметра, который мы можем сделать следующим образом.
x.For(typeof(Lazy<>)).Use(typeof(Lazy<>))
.CtorDependency<bool>("isThreadSafe").Is(true);
StructureMap теперь знает, использовать значение "true" для параметра isThreadSafe при разрешении Lazy<T>
. Теперь мы можем использовать Lazy<T>
в параметрах конструктора и получить поведение, которое, как я полагаю, вы искали.
Вы можете прочитать о классе Lazy более подробно здесь.
Ответ 2
Да, да. Последняя версия StructureMap (2.6.x) скомпилирована против .NET Framework 3.5 и поэтому не имеет доступа к типу Lazy<T>
, представленному в .NET 4. Однако он поддерживает ту же функциональность - "не создавая объект который вводится до тех пор, пока он не будет использован". Вместо зависимости от a Lazy<T>
вы зависите от a Func<T>
. Никакой специальной регистрации контейнера не требуется.
Я включил пример программы, которая создает следующий вывод:
Created Consumer
Consuming
Created Helper
Helping
Sample.cs:
class Program
{
static void Main(string[] args)
{
var container = new Container(x =>
{
x.For<IConsumer>().Use<Consumer>();
x.For<IHelper>().Use<Helper>();
});
var consumer = container.GetInstance<IConsumer>();
consumer.Consume();
}
}
public class Consumer : IConsumer
{
private readonly Func<IHelper> _helper;
public Consumer(Func<IHelper> helper)
{
_helper = helper;
Console.WriteLine("Created Consumer");
}
public void Consume()
{
Console.WriteLine("Consuming");
_helper().Help();
}
}
public interface IConsumer
{
void Consume();
}
public interface IHelper
{
void Help();
}
public class Helper : IHelper
{
public Helper()
{
Console.WriteLine("Created Helper");
}
public void Help()
{
Console.WriteLine("Helping");
}
}