Pregunta

I have two repositories AlbumRepository with interface IAlbumRepository and CachedAlbumRepository with interface IAlbumRepository which has constructor interface IAlbumRepository. I need inject with Ninject ICachedAlbumRepository with CachedAlbumRepository and constructor with AlbumRepository.

How to achieve it with Ninject?

Same approach with structure map

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;
        }
    }
¿Fue útil?

Solución

You need to create 2 bindings - one that says inject CachedAlbumRepository into anything that needs an IAlbumRepository and another that says inject a normal AlbumRepository into CachedAlbumRepository. These bindings should do that:

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

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

Otros consejos

I can't answer the question for you, but I have some feedback for you.

Your application design misses a great opportunity. It misses the opportunity to be and stay maintainable. Since you defined a decorator (the CachedAlbumRepository is a decorator), you will probably start writing decorators for other repositories as well. I imagine you having a decorator for your IArtistRepository, IRecordLabelRepository, etc.

Having to implement these duplicate repositories is a violation of the DRY principle. But the violation of DRY is actually caused by a violation some other principles. Your design is violating some of the SOLID principles, namely:

  1. Your design violates the Single Responsibility Principle, because the query methods that you'll place inside your repository (such as the GetTopSellingAlbums method) are not very cohesive. In other words, your repository classes will get big and will do too much and start to get hard to read, hard to test, hard to change, and hard to maintain.
  2. Your design violates the Open/closed principle, since you will have to alter a repository every time you add a new query to the system. This means changing the interface, changing the decorator, changing the real implementation, and changing every fake implementation there exists in the system.
  3. Your design violates the Interface Segregation Principle, because your repository interfaces will get wide (will have many methods) and consumers of those interfaces are forced to depend on methods that they don’t use. This makes it harder and harder to implement decorators and write fake objects.

The solution to this problem is to hide all repositories behind one single generic abstraction:

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

Since this is a generic interface, it doesn't give you any room to add any entity-specific query methods, and this is good. It is good, because the IRepository<T> will be narrow and stable. This makes it really easy to add decorators to it (if you still need to add decorators here).

The trick is to prevent adding query methods to this interface (and don't inherit new interfaces from this interface), but to give each query in the system its own class. Or in fact, two classes. One class with the definition of the data, and one class that knows how to execute that query. And last but not least, you can hide each class behind the same generic abstraction for queries (just as we have one generic abstraction over repositories). And when you do this, you just have to define one single caching decorator that you can apply to any subset of queries in the system.

You can read in detail about this design here. This might seem a bit abstraction at first, but I promise you, when you get the hang of this, there's no way you're ever getting back to your old design.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top