Structuremap 是否支持开箱即用的 Lazy?

发布于 2024-11-26 05:51:00 字数 48 浏览 1 评论 0原文

结构图是否允许您以惰性方式进行构造函数注入? 意思是在使用之前不创建注入的对象?

Does structuremap allow you to do constructor injection in a lazy fashion?
Meaning not creating the object which is injected until it is used?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

撩心不撩汉 2024-12-03 05:51:00

更新: StructureMap v3 开箱即用地实现了此功能,因此不再需要此技巧。


StructureMap 版本 2 没有,但通过一些技巧,您可以让它完成我相信您正在寻找的事情。首先,您已经可以像这样手动连接 Lazy实例:

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 找到“最贪婪”的构造函数,并选择 public Lazy(FuncvalueFactory, bool isThreadSafe)。由于我们没有告诉它如何处理布尔 isThreadSafe 参数,因此当它尝试解析“Lazy”时会抛出异常。

Lazy 的文档指出,默认的 Lazy(FuncvalueFactory) 构造函数的“线程安全模式”是 LazyThreadSafetyMode.ExecutionAndPublication,这恰好是您可以通过将 true 传递给上面构造函数的 isThreadSafe 参数来获得。因此,如果我们可以告诉 StructureMap 为 isThreadSafe 传递 true,我们将获得与调用我们实际上想要首先使用的构造函数相同的行为(例如Lazy(FuncvalueFactory))。

简单地注册 x.For(typeof(bool)).Use(y => true) 会非常鲁莽和危险,因为我们会告诉 StructureMap 继续并使用值 true 任何地方的 any 布尔值。相反,我们需要告诉 StructureMap 对于这个一个布尔参数使用什么值,我们可以这样做。

x.For(typeof(Lazy<>)).Use(typeof(Lazy<>))
 .CtorDependency<bool>("isThreadSafe").Is(true);

StructureMap 现在知道在解析 Lazy 时对 isThreadSafe 参数使用“true”值。我们现在可以在构造函数参数中使用 Lazy,并获得我相信您正在寻找的行为。

您可以在此处详细了解 Lazy 类。

UPDATE: StructureMap v3 implements this out of the box, so this trick is no longer necessary.


StructureMap version 2 doesn't, but with a few tricks you can get it to do what I believe you are looking for. First of all, you can already wire up Lazy<T> instances manually like this:

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>));
});

This works just fine, but you have to register each and every type individually. It would be nicer if you could take advantage of a more convention-based approach. Ideally, the following syntax would be nice.

x.For(typeof(Lazy<>)).Use(typeof(Lazy<>));

This syntax actually works... somewhat. Unfortunately, at runtime, StructureMap will attempt to find the "greediest" constructor for Lazy<T> and settle on public Lazy(Func<T> valueFactory, bool isThreadSafe). Since we didn't tell it what to do with the boolean isThreadSafe parameter, it will throw an exception when it tries to resolve `Lazy'.

The documentation for Lazy states that the "thread safety mode" of the default Lazy(Func<T> valueFactory) constructor is LazyThreadSafetyMode.ExecutionAndPublication, which just so happens to be what you get by passing true into the isThreadSafe parameter of the constructor above. So, if we could just tell StructureMap to pass true for isThreadSafe, we would get the same behavior as if we called the constructor we actually wanted to use in the first place (e.g. Lazy(Func<T> valueFactory)).

Simply registering x.For(typeof(bool)).Use(y => true) would be very reckless and dangerous since we would be telling StructureMap to go ahead and use the value true for any boolean anywhere. Instead, we need to tell StructureMap what value to use for just this one boolean parameter, which we can do like this.

x.For(typeof(Lazy<>)).Use(typeof(Lazy<>))
 .CtorDependency<bool>("isThreadSafe").Is(true);

StructureMap now knows to use the value of "true" for the isThreadSafe parameter when resolving Lazy<T>. We can now use Lazy<T> in constructor parameters, and get the behavior I believe you were looking for.

You can read about the Lazy class in more detail here.

尝蛊 2024-12-03 05:51:00

是的,确实如此。最新版本的 StructureMap (2.6.x) 是针对 .NET Framework 3.5 编译的,因此无法访问 .NET 4 中引入的 Lazy 类型。但是,它确实支持相同的功能 - “在使用之前不创建注入的对象”。您不再依赖 Lazy,而是依赖 Func。无需特殊集装箱注册。

我提供了一个示例程序,该程序创建以下输出:

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");
    }
}

Yes, it does. The latest version of StructureMap (2.6.x) is compiled against .NET Framework 3.5, and so does not have access to the Lazy<T> type introduced in .NET 4. However, it does support the same functionality - "not creating the object which is injected until it is used". Instead of depending on a Lazy<T>, you depend on a Func<T>. No special container registration is required.

I've included a sample program that creates the following output:

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");
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文