如何使用Simple Injector注入CacheItemPolicy

Dav*_*rke 6 c# dependency-injection simple-injector asp.net-mvc-4

我正在遵循史蒂文记录并使用Simple Injector 记录的一些实践.我有一个查询从WCF服务检索数据,我想使用的实例缓存结果.ObjectCache

我已经定义了一个装饰器CachingQueryHandlerDecorator<TQuery, TResult>:

public sealed class CachingQueryHandlerDecorator<TQuery, TResult>
    : IQueryHandler<TQuery, TResult>
    where TQuery : IQuery<TResult>
{
    private readonly IQueryHandler<TQuery, TResult> _handler;
    private readonly ObjectCache _cache;
    private readonly CacheItemPolicy  _policy;
    private readonly ILog _log;

    public CachingQueryHandlerDecorator(IQueryHandler<TQuery, TResult> handler,
                                        ObjectCache cache,
                                        CacheItemPolicy policy,
                                        ILog log)
    {
        _handler = handler;
        _cache = cache;
        _policy = policy;
        _log = log;
    }

    public TResult Handle(TQuery query)
    {
        var key = query.GetType().ToString();
        var result = (TResult) _cache[key];
        if (result == null)
        {
            _log.Debug(m => m("No cache entry for {0}", key));
            result = (TResult)_handler.Handle(query);
            if (!_cache.Contains(key))
                _cache.Add(key, result, _policy);
        }
        return result;
    }
}
Run Code Online (Sandbox Code Playgroud)

SimpleInjectorInitializer.cs我定义缓存和策略,并为特定查询添加装饰器:

container.RegisterSingle<ILog>(LogManager.GetCurrentClassLogger());
container.RegisterSingle<ObjectCache>(() => new MemoryCache("MyCache"));
container.RegisterSingle<CacheItemPolicy>(() => new CacheItemPolicy { AbsoluteExpiration = DateTime.Now.AddMinutes(1) } );
.
.
.
container.RegisterDecorator(typeof(IQueryHandler<,>),
    typeof(CachingQueryHandlerDecorator<,>),
    ctx => ctx.ServiceType.GetGenericArguments()[0] == typeof(MyQuery));
Run Code Online (Sandbox Code Playgroud)

我面临的问题是我希望能够CacheItemPolicy为不同的查询指定不同的.我可以创建一个新的ICachePolicy<TQuery>接口,然后为每个不同的查询类型定义具体的类,但我希望可能有一种方法可以避免这种情况,并直接在初始化文件中定义每个查询的策略.

Ste*_*ven 14

我可以创建一个新的ICachePolicy接口,然后为每个不同的查询类型定义具体的类

我认为这实际上是一个非常巧妙的想法.您可以注册一个默认的通用实现,该实现注入到没有注册特定实现的每个装饰器中:

container.RegisterOpenGeneric(typeof(ICachePolicy<>), typeof(DefaultCachePolicy<>),
    Lifestyle.Singleton);
Run Code Online (Sandbox Code Playgroud)

对于具有备用缓存策略的查询,您可以注册特定的实现:

container.RegisterSingle<ICachePolicy<MyQuery>>(new CachePolicy<MyQuery> 
{
    AbsoluteExpiration = DateTime.Now.AddHour(2)
});
Run Code Online (Sandbox Code Playgroud)

另一种选择是使用描述缓存策略的属性(这是我通常采用的路径)标记查询或其查询处理程序:

[CachePolicy(AbsoluteExpirationInSeconds = 1 * 60 * 60)]
public class MyQuery : IQuery<string[]> { }
Run Code Online (Sandbox Code Playgroud)

现在您不必注入ICachePolicy<T>,但可以使用反射直接读取此元数据:

public sealed class CachingQueryHandlerDecorator<TQuery, TResult>
    : IQueryHandler<TQuery, TResult>
    where TQuery : IQuery<TResult>
{
    private static readonly bool shouldCache;
    private static readonly CachingPolicySettings policy;

    private readonly IQueryHandler<TQuery, TResult> _handler;
    private readonly ObjectCache _cache;
    private readonly ILog _log;

    static CachingQueryHandlerDecorator()
    {
        var attribute = typeof(TQuery).GetCustomAttribute<CachePolicyAttribute>();

        if (attribute != null)
        {
            shouldCache = true;
            policy = attribute.Policy;
        }
    }

    public CachingQueryHandlerDecorator(
        IQueryHandler<TQuery, TResult> handler,
        ObjectCache cache,
        ILog log)
    {
        _handler = handler;
        _cache = cache;
        _log = log;
    }

    public TResult Handle(TQuery query)
    {
        if (!shouldCache)
        {
            return this._handler.handle(query);
        }

        // do your caching stuff here.
    }
Run Code Online (Sandbox Code Playgroud)

  • 打了6秒! (3认同)

quj*_*jck 7

您可以使用开放式通用实现获得所需的结果,并根据需要覆盖特定的默认值.即,您定义了一个开放的通用实现CachePolicy<TQuery>,ICachePolicy<TQuery>并使用该RegisterInitializer方法来覆盖默认实现的部分.

鉴于这些定义:

public interface ICachePolicy<TQuery>
{
    DateTime AbsoluteExpiration { get; }
}

public class CachePolicy<TQuery> : ICachePolicy<TQuery>
{
    public CachePolicy()
    {
        AbsoluteExpiration = Cache.NoAbsoluteExpiration;
    }

    public DateTime AbsoluteExpiration { get; set; }
}

public interface IQueryHandler<TQuery, TResult> { }
public class QueryHandlerA : IQueryHandler<A, AResult> { }
public class QueryHandlerB : IQueryHandler<B, BResult> { }

public sealed class CachingQueryHandlerDecorator<TQuery, TResult>
    : IQueryHandler<TQuery, TResult>
{
    private readonly IQueryHandler<TQuery, TResult> decorated;
    public readonly ICachePolicy<TQuery> Policy;

    public CachingQueryHandlerDecorator(
        IQueryHandler<TQuery, TResult> decorated,
        ICachePolicy<TQuery> cachePolicy)
    {
        this.decorated = decorated;
        this.Policy = cachePolicy;
    }
}
Run Code Online (Sandbox Code Playgroud)

使用该RegisterOpenGeneric方法设置容器并使用以下命令配置非默认值RegisterInitializer:

public Container ConfigureContainer()
{
    Container container = new Container();

    container.RegisterOpenGeneric(
        typeof(ICachePolicy<>), 
        typeof(CachePolicy<>), 
        Lifestyle.Singleton);

    container.RegisterInitializer<CachePolicy<A>>(a =>
        a.AbsoluteExpiration = DateTime.Now.AddMinutes(1));

    container.RegisterManyForOpenGeneric(
        typeof(IQueryHandler<,>),
        typeof(IQueryHandler<,>).Assembly);

    container.RegisterDecorator(
        typeof(IQueryHandler<,>), 
        typeof(CachingQueryHandlerDecorator<,>));

    container.Verify();

    return container;
}
Run Code Online (Sandbox Code Playgroud)

这些测试表明结果符合预期:

[Test]
public void GetInstance_A_HasCustomAbsoluteExpiration()
{
    Container container = ConfigureContainer();

    var a = container.GetInstance<IQueryHandler<A, AResult>>();

    Assert.AreNotEqual(
        (a as CachingQueryHandlerDecorator<A, AResult>).Policy.AbsoluteExpiration,
        Cache.NoAbsoluteExpiration);
}

[Test]
public void GetInstance_B_HasDefaultAbsoluteExpiration()
{
    Container container = ConfigureContainer();

    var b = container.GetInstance<IQueryHandler<B, BResult>>();

    Assert.AreEqual(
        (b as CachingQueryHandlerDecorator<B, BResult>).Policy.AbsoluteExpiration,
        Cache.NoAbsoluteExpiration);
}
Run Code Online (Sandbox Code Playgroud)