使用 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; }
}
}
有什么建议吗?
属性或方法的需求仅在您的控制器中,那么我会将它们作为这些控制器的公共基类的方法(它派生自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
为空和设置它之间的竞赛。由于实际设置是原子的,从长远来看,这可能比阻止这种情况发生的努力要好,但重要的是要知道实际上只有一个这样的列表的几次(。
这里的优点是:
- 我们确保我们只创建我们需要的东西(例如,如果第一个要求供应商列表的请求不需要foo列表等(。 需要供应商列表的方法应该是具有
- 创建供应商列表所需的信息的方法(如果尚未发生(,而需要foo列表的方法应具有该信息。这通常比首先必须弄清楚如何创建两者的方法更容易。特别是,它允许我们将懒惰传递下去,因此例如,一个只需要一个foo列表的方法有时只需要获得foo服务。
- 相关地,虽然差异似乎主要是语义上的,但"缓存对象"为我们提供了问题较少的单例的一些好处。特别是,虽然它可以防止浪费调用,但它避免了全局状态的许多缺点,因为它更像是优化非全局行为代码的全局实现工件:大多数调用代码不像依赖于全局状态的代码那样编写。
- 我们可以更轻松地添加更多此类列表或其他缓存信息,而不会影响不使用它们的现有代码,无论是线性性能(其他代码不会触及它,也不会关心(、并发性能(缓存的对象可以并发设置(或代码签名(无需更改我们不再使用的构造函数(。
- 正如我们控制 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));
}
另外,请注意,根本没有理由在调用代码中显式处理此类。VendorList
和FooList
都可以创建为扩展方法。例如:
public static IList<Vendor> VendorList(this IVendorService vendorService)
{
return _vendorList = _vendorList ?? vendorService.GetVendors();
}
现在我们可以调用代码可以调用它,就好像它是实现IVendorService
的任何实例方法一样,这是理想的,因为它毕竟只是一种提供一些全局缓存的GetVendors
形式。同样,实现的全局工件是隐藏的,从而降低了全局依赖项在代码中进一步推送的风险,或者在未来需要时变得难以更改的风险。