具有接口的构造函数的StructureMap和延迟初始化

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了具有接口的构造函数的StructureMap和延迟初始化相关的知识,希望对你有一定的参考价值。

我使用StructureMap v4.6.1.0,我有一个结构,我使用构造函数创建一个实例,我插入该类的接口,这个类通常调用我的构造函数,在其服务中使用它的参数

 private readonly IFirstService _firstService;
 private readonly ISecondService _secondService;

 private readonly ILog _log;

 public ProductController(IFirstService firstService, ISecondService secondService, ILog log)
 {
       _firstService = firstService;
       _secondService = secondService;

       _log = log;
 }

 [Route("Default")]
 public ActionResult First()
 {
       var model = _firstService.DoIt();
       return View("~/Views/First/index.cshtml", model);
 }

 [Route("Default")]
 public ActionResult Second()
 {
       var model = _secondService.DoIt();
       return View("~/Views/Second/index.cshtml", model);
 }

这个解决方案的主要问题是我调用Controller然后它创建了一个2个实例(一个用于firstService,第二个用于secondService),但是这个服务我调用了特定的控制器页面方法。

例如,在工厂,日志和存储库加载器的构造函数接口中进行服务调用,这意味着当我调用控制器构造函数时,我从两个服务加载所有存储库 -

  • 我可以使用C#.NET Lazy(T)或Func吗?
  • 我可以在所选页面的方法中使用interface作为参数吗?
  • 我可以将Cache用于存储库中的只读数据吗?
  • 另一种方法?

当我使用Lazy时,我得到了消息,然后调用过程未定义

我正在寻找最好的架构解决方案,我尝试了一些懒惰和代码优化,但我总是遇到一个问题

编辑:

StructureMap容器​​注册

Scan(
    scan =>
    {
           scan.TheCallingAssembly();
           scan.WithDefaultConventions();
           scan.With(new ControllerConvention());
    });

For<ILog>().Use(c => LogManager.GetLogger(GetType())).Singleton();

For<IFirstService>().Use<FirstService>().Singleton();
For<ISecondService>().Use<SecondService>().Singleton();

我的解决方案

  • 我可以在所选页面的方法中使用interface作为参数吗?

在类构造函数中,我使用StructureMap的接口

private readonly IContainer _container;
private readonly ILog _log;

public ProductController(IContainer container, ILog log)
{
       _container = container;
       _log = log;
}

在我使用的方法中

var model = _container.GetInstance<IFirstService>().DoIt();
  • 我可以将Cache用于存储库中的只读数据吗?

我使用.NET lib使用静态System.Web.HttpRuntime;并在存储库类的构造函数中调用的方法中使用下面的代码

if (!(Cache[_cacheName] is IEnumerable<YourObject> result)) // Cache is empty
            {
                _log.Info("-- Loading from DB --");
                lock (CacheLockObject)
                {
                    result = Cache[_cacheName] as IEnumerable<YourObject>;
                    if (result == null)
                    {
                        result = LoadAll(); // load data from DB
                        Cache.Insert(_cacheName, result, null,
                            DateTime.Now.AddMinutes(10), TimeSpan.Zero);
                    }

                    return result;
                }
            }

            _log.Info("-- Loading from Cache --");
            return result;

谢谢

答案

当前依赖注入的解决方案是使用服务定位器反模式。容器不应作为依赖项传递。这样做是服务定位器的明确指标。

您可以使用Lazy<T>Func<T>推迟初始化

例如,以下使用Func<T>

private readonly Func<IFirstService> _firstService;
private readonly Func<ISecondService> _secondService;    
private readonly ILog _log;

public ProductController(Func<IFirstService> firstService, Func<ISecondService> secondService, ILog log) {
    _firstService = firstService;
    _secondService = secondService;    
    _log = log;
 }

[Route("Default")]
public ActionResult First() {
    IFirstService service = _firstService();//invoke delegate to get service
    var model = service.DoIt();
    return View("~/Views/First/index.cshtml", model);
}

[Route("Default")]
public ActionResult Second() {
    ISecondService service = _secondService();
    var model = service.DoIt();
    return View("~/Views/Second/index.cshtml", model);
}

Func充当工厂代表,只有在需要时才会延迟依赖项的初始化/激活。

因此,在上面的示例中,如果请求First(),则仅为该请求调用_firstService()委托,而不是两个服务。

你可以用Lazy<T>做同样的事情

private readonly Lazy<IFirstService> _firstService;
private readonly Lazy<ISecondService> _secondService;    
private readonly ILog _log;

public ProductController(Lazy<IFirstService> firstService, Lazy<ISecondService> secondService, ILog log) {
    _firstService = firstService;
    _secondService = secondService;    
    _log = log;
 }

[Route("Default")]
public ActionResult First() {
    IFirstService service = _firstService.Value;//lazy load service
    var model = service.DoIt();
    return View("~/Views/First/index.cshtml", model);
}

[Route("Default")]
public ActionResult Second() {
    ISecondService service = _secondService.Value;
    var model = service.DoIt();        
    return View("~/Views/Second/index.cshtml", model);
}

StructureMap Documentation: Lazy Resolution

StructureMap具有一些内置的“懒惰”已解析依赖项功能,因此您可以使用可以用于检索的IExpensiveToBuildServiceLazy<IExpensiveToBuildService>依赖于结构映射,而不是您的应用程序服务直接依赖于Func<IExpensiveToBuildService>。只有在最初创建父对象的任何Container需要它时才需要昂贵的服务。

以上是关于具有接口的构造函数的StructureMap和延迟初始化的主要内容,如果未能解决你的问题,请参考以下文章

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

如何使用StructureMap的AutoMocker模拟没有接口的类?

抽象类是不是像接口一样与 StructureMap 一起使用?

使用 StructureMap 3.* 拦截

kotlin学习总结——类和对象继承接口和抽象类

在 Asp.Net MVC 应用程序中使用 Structuremap 将 ISession 注入我的存储库