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

本文关键字:一个 单例类 创建 Lazy Unity Mvc5 使用 | 更新日期: 2023-09-27 18:06:38

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

这有效,但这并不是真正使用单例模式。

应用助手.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; }
    }
}

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

应用助手.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; }
    }
}

有什么建议吗?

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

如果对这些

属性或方法的需求仅在您的控制器中,那么我会将它们作为这些控制器的公共基类的方法(它派生自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. 需要供应商列表的方法应该是具有
  3. 创建供应商列表所需的信息的方法(如果尚未发生(,而需要foo列表的方法应具有该信息。这通常比首先必须弄清楚如何创建两者的方法更容易。特别是,它允许我们将懒惰传递下去,因此例如,一个只需要一个foo列表的方法有时只需要获得foo服务。
  4. 相关地,虽然差异似乎主要是语义上的,但"缓存对象"为我们提供了问题较少的单例的一些好处。特别是,虽然它可以防止浪费调用,但它避免了全局状态的许多缺点,因为它更像是优化非全局行为代码的全局实现工件:大多数调用代码不像依赖于全局状态的代码那样编写。
  5. 我们可以更轻松地添加更多此类列表或其他缓存信息,而不会影响不使用它们的现有代码,无论是线性性能(其他代码不会触及它,也不会关心(、并发性能(缓存的对象可以并发设置(或代码签名(无需更改我们不再使用的构造函数(。
  6. 正如我们控制 crate 更接近需求一样,我们也可以将失效放在那里,如果未来的更改给我们的情况,我们不能依赖列表在应用程序的生命周期内不可变。(我们也不能阻止在其他地方也失效(。

我们还可以相对容易地处理以下更改:

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形式。同样,实现的全局工件是隐藏的,从而降低了全局依赖项在代码中进一步推送的风险,或者在未来需要时变得难以更改的风险。