使用Lazy和Unity.Mvc5创建单例类

时间:2014-06-04 00:15:38

标签: c# asp.net-mvc singleton unity-container

我在大多数控制器中使用至少两个服务(例如,IVendorService和IFooService)(使用Unity.Mvc5)。 我想为这些控制器创建一个通用帮助器。我正在考虑使用Lazy加载和Unity创建单例类。 我在其中一个博客中读到,统一要求构造函数是公开的。

这样可行,但这并不是真的使用单例模式。

AppHelper.cs

public sealed class AppHelper
{
    private static IList<Vendor> _vendorList;
    private static IList<Foo> _fooList;

    public AppHelper(IFooService fooService, IVendorService vendorService)
    {
        if (fooService != null) _vendorList = vendorService.GetVendors();
        if (vendorService != null) _fooList = fooService.GetFoo();
    }

    public static IList<Vendor> VendorList
    {
        get { return _vendorList; }
    }

    public static IList<Foo> FooList
    {
        get { return _fooList; }
    }
}

我已经尝试过这种方式,它给了我一个编译错误。

AppHelper.cs

public sealed class AppHelper
{
    private static readonly Lazy<AppHelper> instance = new Lazy<AppHelper>(() => new AppHelper(fooService, vendorService));
    private static IList<Vendor> _vendorList;
    private static IList<Foo> _fooList;

    private AppHelper(IFooService fooService, IVendorService vendorService)
    {
        _vendorList = vendorService.GetVendors();
        _fooList = fooService.GetFoo();
    }

    public static AppHelper Instance()
    {
        return instance.Value;
    }

    public static IList<Vendor> VendorList
    {
        get { return _vendorList; }
    }

    public static IList<Foo> FooList
    {
        get { return _fooList; }
    }
}

有什么建议吗?

1 个答案:

答案 0 :(得分:2)

如果对这些属性或方法的需求仅在您的控制器中,那么我将它们作为这些控制器的公共基类的方法(它来自System.Web.Mvc.Controller,并且您的控制器来自它而不是直接来自Controller)。

如果需要仅仅是在意见中,我也同样有这些意见。

但你有两个。

一种方法是要么考虑一个可接受的重复(即将它放在两者的基础上),要么让一个类与你的第一个类很相似,这是由那些基类调用的,所以唯一的重复是非常薄的包装方法。

无论你是这样做的,还是只是通过实际需要它们的方法调用了帮助器,你可能比你更懒惰:

private static class CachedElements
{
    private static IList<Vendor> _vendorList;
    private static IList<Foo> _fooList;
    public static IList<Vendor> VendorList(IVendorService vendorService)
    {
        return _vendorList = _vendorList ?? vendorService.GetVendors();
    }
    public static IList<Foo> FooList(IFooService fooService)
    {
        return _fooList = _fooList ?? fooService.GetFoos();
    }
}

(我确定你已经知道了,但值得指出的是,这与你问题中的原件分享了一个短暂的时期,其中不止一个可以创建和使用_vendorlist,每个都替换另一个,因为发现_vendorList为空,并设置它之间的竞争。由于实际设置是原子的,从长远来看这可能比阻止这种情况发生的努力,但重要的是要知道有几次真的只有一个这样的列表被创造出来。)

这里的优点是:

  1. 我们确保只创建我们需要的东西(例如,如果要求供应商列表的第一个请求不需要foo列表等)。
  2. 需要供应商列表的方法应该是具有创建一个所需信息的方法,如果还没有发生,那么需要foo列表的那些(那些)预计会有这方面的信息。这通常比首先必须解决如何创建两者的方法更容易。特别是,它让我们将懒惰传递到线下,例如,一个只需要有时需要foo列表的方法,有时只需要获取foo-service。
  3. 相关地,虽然差异似乎主要是语义上的,但是&#34;缓存对象&#34;给我们一些问题较少的单身人士带来的好处。特别是,虽然它可以防止浪费掉调用,但它避免了全局状态的大部分缺点,因为它更像是优化非全局行为代码的全局实现假象:大多数调用代码都不是写得像依赖于全球状态的代码。
  4. 我们可以更轻松地添加更多此类列表或其他缓存信息,而不会影响现有代码,无论是在线性性能方面还是没有使用它们(其他代码都不会触及它,并且不关心)并发性能(缓存的对象可以彼此同时设置),或代码签名(不需要更改我们不再使用的构造函数)。
  5. 正如我们正在控制更接近要求的条件一样,我们也可以在那里放置无效,如果未来的变化给我们的情况我们不能依赖于列表在生命周期中是不可变的应用。 (我们也没有阻止在其他地方失效)。
  6. 我们还可以相对轻松地处理以下更改:

    public static IList<Foo> FooList(IFooService fooService)
    {
      //some change to the application has meant this is no longer safe to cache, but
      //we only needed to change one piece of code:
      return fooService.GetFoos();
    }
    

    或者复杂化如下:

    private static ConcurrentDictionary<Territory, IList<Vendor>> _vendors = new ConcurrentDictionary<Territory, IList<Vendor>>();
    public static IList<Vendor> VendorList(IVendorService vendorSerice, Territory territory)
    {
      // We now have different lists of vendors for different countries, states, etc.
      // We were able to make all of our changes through this place, and keep a similar
      // type of caching happening.
      return _vendors.GetOrAdd(territory, () => vendorSerice.GetVendors(territory));
    }
    

    另外,请注意,根本没有理由在调用代码中明确地处理这个类。 VendorListFooList都可以创建为扩展方法。例如。用:

    public static IList<Vendor> VendorList(this IVendorService vendorService)
    {
        return _vendorList = _vendorList ?? vendorService.GetVendors();
    }
    

    现在我们可以调用它,就像它是实现IVendorService的任何实例方法一样,这是理想的,因为它毕竟只是一种GetVendors形式提供了一些全局缓存无论如何。同样,实现的全局伪像被隐藏起来,降低了全局依赖性在代码中被进一步推动的风险,或者在未来需求需要时难以改变。