سؤال

I am trying to implement the strategy pattern in my repository layer using SM and generics. For that I have an interface, IPocoRepository, which has a concrete implementation using Entity Framework. This I have managed to wire up in my Bootstrapper-file:

For(typeof(IPocoRepository<>)).Use(typeof(EntityFrameworkRepository<>));

The problem appears when I try to implement caching for this interface. In my cached class I want an instance of the base repository class, so that I can keep my design DRY. Let me outline how these three files look:

public interface IPocoRepository<T>
{
    IQueryable<T> GetAll();
    ...


public class EntityFrameworkRepository<T> : IPocoRepository<T> where T : class
{
    public IQueryable<T> GetAll()
    {
        ...

public class CachedRepository<T> : IPocoRepository<T> where T : class
{
    private IPocoRepository<T> _pocoRepository;

    public CachedRepository(IPocoRepository<T> pr)
    {
        _pocoRepository = pr;
    }

    public IQueryable<T> GetAll()
    {
        var all = (IQueryable<T>)CacheProvider.Get(_cacheKey);
        if (!CacheProvider.IsSet(_cacheKey))
        {
            all = _pocoRepository.GetAll();
            ...

Edit: I want StructureMap to return CachedRepository when IPocoRepository is requested, except when requested for in CachedRepository - then I want it to return EntityFrameworkRepository.

I know this is simple when dealing with non-generic classes:

For<ICountyRepository>().Use<CachedCountyRepository>()
.Ctor<ICountyRepository>().Is<CountyRepository>();

I tried searching the documentation for how to do this, but couldn't find anything. Any help would be appreciated!

هل كانت مفيدة؟

المحلول

Ok, this isn't too hard. You can use a type interceptor. Given you have the following classes:

public interface IRepository<T>{}

public class Repository<T>:IRepository<T>{}

public class RepositoryCache<T> : IRepository<T>
{
    private readonly IRepository<T> _internalRepo;

    public RepositoryCache(IRepository<T> internalRepo)
    {
        _internalRepo = internalRepo;
    }

    public IRepository<T> InternalRepo
    {
        get { return _internalRepo; }
    }
}

You will then need to create a type interceptor. You can use the configurable "MatchedTypeInterceptor" provided by StructureMap for this. The interceptor will need to look for your repositories and then figure out what the generic type parameters are. Once it has the type parameters it can declare the type of cache it needs and initialize it. As part of the initialization, it will take the original repository in it's constructor. Then the interceptor will return the completed cache to whatever requested it from the ioc context. Here is the complete sequence inside a test.

This can be moved out into your registry, I just left it all together as an minimal example.

    [Test]
    public void doTest()
    {
        MatchedTypeInterceptor interceptor = new MatchedTypeInterceptor(
             x => x.FindFirstInterfaceThatCloses(typeof (IRepository<>)) != null);

        interceptor.InterceptWith(original =>
        {
            Type closedType = original.GetType()
                 .FindFirstInterfaceThatCloses(typeof(IRepository<>));

            var genericParameters = closedType.GetGenericArguments();

            var closedCacheType = typeof(RepositoryCache<>)
                 .MakeGenericType(genericParameters);

            return Activator.CreateInstance(closedCacheType, new[] {original});
        });

        ObjectFactory.Initialize(x =>
        {
            x.For(typeof (IRepository<>)).Use(typeof (Repository<>));
            x.RegisterInterceptor(interceptor);
        });

        var test = ObjectFactory.GetInstance<IRepository<int>>();

        Assert.That(test is RepositoryCache<int>);
    }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top