【问题标题】:Create a singleton class with Lazy and Unity.Mvc5使用 Lazy 和 Unity.Mvc5 创建一个单例类
【发布时间】:2014-06-04 00:15:38
【问题描述】:

我在大多数控制器(使用 Unity.Mvc5)中使用了至少两个服务(例如,IVendorService 和 IFooService)。 我想为这些控制器创建一个通用助手。我正在考虑使用延迟加载和 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; }
    }
}

有什么建议吗?

【问题讨论】:

  • 为什么使用单例反模式本身很重要?对你来说,你的目标是从你拥有的有效代码中获得什么?
  • 好点。我的主要原因是使用有效的代码,我需要在每个控制器文件中实例化 AppHelper。所以我想看看是否有一种方法可以在 Global.asax.cs 文件中执行 AppHelper.Instance()。
  • 哦,您只使用控制器中的这些属性?
  • 我也在我的观点中使用它。

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


【解决方案1】:

如果仅在您的控制器中需要这些属性或方法,那么我会将它们作为这些控制器的公共基类的方法(它派生自 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 服务。
  3. 与此相关,虽然差异似乎主要是语义上的,但“缓存对象”为我们提供了一些问题较少的单例的好处。特别是,虽然它可以防止浪费的调用,但它避免了全局状态的许多缺点,因为它更像是优化非全局行为代码的全局实现人工制品:大多数调用代码不像依赖于全局的代码那样编写状态。
  4. 我们可以更轻松地添加更多此类列表或其他缓存信息,而不会影响不使用它们的现有代码,无论是在线性性能方面(其他代码不会触及它,也不会注意)并发性能(缓存的对象可以相互同时设置)或代码签名(无需更改我们不再使用的构造函数)。
  5. 正如我们控制 crating 更接近要求一样,我们也可以将失效放在那里,如果未来的变化给我们带来我们不能依赖列表在应用程序生命周期内不可变的情况。 (我们也没有被阻止在其他地方也有失效)。

我们也可以相对轻松地处理以下变化:

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 的一种形式,无论如何都提供了一些全局缓存。同样,实现的全局工件是隐藏的,从而降低了全局依赖关系被进一步推入代码或在未来需要时变得难以更改的风险。

【讨论】:

  • 哇,我喜欢这个!感谢您的建议并指出您实施的优势。并感谢您还建议为此创建扩展方法。非常感谢您的帮助!!!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-01
  • 1970-01-01
相关资源
最近更新 更多