如何在Ninject中使用策略模式

时间:2013-04-22 17:58:34

标签: c# ninject strategy-pattern

我有两个存储库AlbumRepository,接口IAlbumRepositoryCachedAlbumRepository接口IAlbumRepository,其构造函数接口IAlbumRepository。我需要使用带有ICachedAlbumRepository的Ninject CachedAlbumRepository和带有AlbumRepository的构造函数注入。

如何使用Ninject实现它?

与结构图相同的方法

x.For<IAlbumRepository>().Use<CachedAlbumRepository>()

.Ctor<IAlbumRepository>().Is<EfAlbumRepository>();

    public class CachedAlbumRepository : IAlbumRepository
    {
        private readonly IAlbumRepository _albumRepository;

        public CachedAlbumRepository(IAlbumRepository albumRepository)
        {
            _albumRepository = albumRepository;
        }

        private static readonly object CacheLockObject = new object();

        public IEnumerable<Album> GetTopSellingAlbums(int count)
        {
            string cacheKey = "TopSellingAlbums-" + count;

            var result = HttpRuntime.Cache[cacheKey] as List<Album>;

            if (result == null)
            {
                lock (CacheLockObject)
                {
                    result = HttpRuntime.Cache[cacheKey] as List<Album>;

                    if (result == null)
                    {
                        result = _albumRepository.GetTopSellingAlbums(count).ToList();

                        HttpRuntime.Cache.Insert(cacheKey, result, null, 

                            DateTime.Now.AddSeconds(60), TimeSpan.Zero);
                    }
                }
            }

            return result;
        }
    }

2 个答案:

答案 0 :(得分:3)

您需要创建2个绑定 - 一个表示将CachedAlbumRepository注入需要IAlbumRepository的任何内容,另一个表示将正常AlbumRepository注入CachedAlbumRepository。这些绑定应该这样做:

Bind<IAlbumRepository>()
    .To<CachedAlbumRepository>();

Bind<IAlbumRepository>()
    .To<AlbumRepository>()
    .WhenInjectedInto<CachedAlbumRepository>();

答案 1 :(得分:2)

我无法回答你的问题,但我有一些反馈意见。

您的应用程序设计错过了一个很好的机会。它错过了成为并保持可维护的机会。由于您定义了一个装饰器(CachedAlbumRepositorydecorator),您可能也会开始为其他存储库编写装饰器。我想你有IArtistRepositoryIRecordLabelRepository等装扮师。

必须实施这些重复的存储库违反了DRY原则。但是,违反DRY实际上是由违反其他一些原则引起的。您的设计违反了一些SOLID原则,即:

  1. 您的设计违反了Single Responsibility Principle,因为您将放置在存储库中的查询方法(例如GetTopSellingAlbums方法)不是很有凝聚力。换句话说,你的存储库类会变得很大,会做得太多,开始难以阅读,难以测试,难以改变,难以维护。
  2. 您的设计违反了Open/closed principle,因为每次向系统添加新查询时都必须更改存储库。这意味着更改界面,更改装饰器,更改实际实现,以及更改系统中存在的每个虚假实现。
  3. 您的设计违反了Interface Segregation Principle,因为您的存储库接口将变宽(将有许多方法),并且这些接口的使用者被迫依赖于他们不使用的方法。这使得实现装饰器和编写假对象变得越来越难。
  4. 此问题的解决方案是将所有存储库隐藏在一个通用抽象背后:

    public interface IRepository<TEntity>
    {
        void Save(TEntity entity);
        TEntity Get(Guid id);
    }
    

    由于这是一个通用接口,因此它不会给你任何添加任何特定于实体的查询方法的空间,这很好。这很好,因为IRepository<T>将是狭窄和稳定的。这使得向它添加装饰器变得非常容易(如果你还需要在这里添加装饰器)。

    诀窍是阻止向此接口添加查询方法(并且不从此接口继承新接口),而是为系统中的每个查询提供自己的类。或者实际上是两个班级。一个具有数据定义的类,以及一个知道如何执行该查询的类。最后但并非最不重要的是,您可以隐藏查询的相同通用抽象背后的每个类(就像我们对存储库有一个通用抽象)。当你这样做时,你只需要定义一个可以应用于系统中任何查询子集的缓存装饰器。

    您可以阅读detail about this design here。一开始这看起来有点抽象,但我向你保证,当你掌握这一点时,你无法回到原来的设计。