I have a factory that creates validator instances. I pass in an object to validate, and it gives me the validator that I can use to validate it.
public class ValidatorFactory : IValidatorFactory
{
public ValidatorFactory(IComponentContext container) { _container = container; }
private readonly IComponentContext _container;
public IValidator create(object objectToValidate)
{
var validatorType = typeof(IValidator<>).MakeGenericType(new Type[] { objectToValidate.GetType() });
object validator;
_container.TryResolve(validatorType, out validator);
return validator as EntityValidatorI;
}
}
It works, but I need to pass in the container IComponentContext.
Is there a better way where I don't need to do that?
Autofac has "Implicit Relationship Types" but I'm unsure how to use them here, as the type would only be known at runtime.
you can do something like below,
Instead of injecting IComponentContext into your main classes, inject a generic
Func method.
The code below might not compile as I quickly just wrote it here but I hope you get the idea.
public class ValidatorFactory : IValidatorFactory
{
public ValidatorFactory(Func<Type, IValidator> factory) { _factory = factory; }
private readonly Func<Type, IValidator> _factory;
public IValidator create(object objectToValidate)
{
var validatorType = typeof(IValidator<>).MakeGenericType(new Type[] { objectToValidate.GetType() });
return _factory(validatorType);
}
}
public static class YourBootstrapperClass{
public static void Register(ContainerBuilder containerBuilder){
containerBuilder.Register(ctx => new ValidatorFactory(type => {
object validator;
containerBuilder.TryResolve(validatorType, out validator);
return validator;
})).As<IValidatorFactory>();
}
}
Related
Using this PipelineX class below, is there any way to resolve the filters applied to the pipline without injecting the autofac container and calling _container.Resolve();
public class PipelineX<T> : FilterBase<T>, IPipelineX<T>
{
private readonly IContainer _container;
public PipelineX(IContainer container)
{
_container = container;
}
protected override T Process(T input)
{
return input;
}
public PipelineX<T> FilterBy<X>()
{
var filter = _container.Resolve(typeof(X)) as IFilter<T>;
Register(filter);
return this;
}
}
To avoid the usage of Autofac as a service locator you can register your own factory method into it, in this case:
builder.Register<Func<Type, object>>((c, p) =>
{
var context = c.Resolve<IComponentContext>();
return type => context.Resolve(type);
});
and use that in your PipelineX class like this:
private readonly Func<Type, object> filterFactory;
public PipelineX(Func<Type, object> filterFactory)
{
this.filterFactory = filterFactory;
}
protected override T Process(T input)
{
return input;
}
public PipelineX<T> FilterBy<X>()
{
var filter = this.filterFactory(typeof(X)) as IFilter<T>;
Register(filter);
return this;
}
Consider: This only removes the hard reference to the Autofac container, it's still using an abstract object factory which is not self explanatory enough and should be replaced by a custom filter factory or selector implementation.
This is similar to Péter's answer but uses a custom factory:
public class FilterFactory
{
private readonly Func<Type, object> _factoryFunc;
public FilterFactory(Func<Type, object> factoryFunc)
{
_factoryFunc = factoryFunc ?? throw new ArgumentNullException(nameof(factoryFunc));
}
public IFilter<T> Create<X, T>()
{
IFilter<T> filter = Create<T>(typeof(X));
return filter;
}
public IFilter<T> Create<T>(Type type)
{
var filter = _factoryFunc(type) as IFilter<T>;
if (filter == null)
{
throw new ArgumentException($"Could not find filter for type '{type.FullName}'");
}
return filter;
}
}
PipelineX implementation would be:
public class PipelineX<T> : FilterBase<T>, IPipelineX<T>
{
private readonly FilterFactory _factory;
public PipelineX(FilterFactory factory)
{
_factory = factory;
}
protected override T Process(T input)
{
return input;
}
public PipelineX<T> FilterBy<X>()
{
var filter = _factory.Create<X,T>() as IFilter<T>;
Register(filter);
return this;
}
}
Registering the factory using Autofac:
builder.Register<FilterFactory>(c =>
{
var context = c.Resolve<IComponentContext>();
return new FilterFactory(context.Resolve);
});
I try to test the result of some function where a call to an extension method is used. This extension method is defined on an interface. The test setup creates a mock of said interface. For this mock two setups are configured. When calling these setup function on the mocked interface implementation, everything works as intended. (see TestMockSetupSourceClassA and TestMockSetupSourceClassB) But when these calls are made in the extension method the result is null. (see TestDoClassStuff)
I've set up a test project: https://github.com/sschauss/MoqExtensionMethodTest
Extension
public static class ExtensionClass
{
public static TResult DoExtensionStuff<TResult>(this ISomeInterface someInterface, object initialObject,
params object[] objects)
{
var result = someInterface.DoInterfaceStuff<TResult>(initialObject);
return objects.Aggregate(result, (agg, cur) => someInterface.DoInterfaceStuff(cur, agg));
}
}
Implementation
public class SomeClass
{
private readonly ISomeInterface _someInterface;
public SomeClass(ISomeInterface someInterface)
{
_someInterface = someInterface;
}
public TargetClass DoClassStuff(SourceClassA sourceClassA, SourceClassB sourceClassB)
{
return _someInterface.DoExtensionStuff<TargetClass>(sourceClassA, sourceClassB);
}
}
Test
public class UnitTest
{
private readonly SomeClass _sut;
private readonly SourceClassA _sourceA;
private readonly SourceClassB _sourceB;
private readonly TargetClass _target;
private readonly Mock<ISomeInterface> _someInterfaceMock;
public UnitTest()
{
_sourceA = new SourceClassA
{
Integer = 1
};
_sourceB = new SourceClassB
{
String = "stringB"
};
_target = new TargetClass
{
Integer = 2,
String = "stringT"
};
_someInterfaceMock = new Mock<ISomeInterface>();
_someInterfaceMock.Setup(m => m.DoInterfaceStuff<TargetClass>(_sourceA)).Returns(_target);
_someInterfaceMock.Setup(m => m.DoInterfaceStuff(_sourceB, _target)).Returns(_target);
_sut = new SomeClass(_someInterfaceMock.Object);
}
[Fact]
public void TestDoClassStuff()
{
var result = _sut.DoClassStuff(_sourceA, _sourceB);
result.Should().BeEquivalentTo(_target);
}
[Fact]
public void TestMockSetupSourceClassA()
{
var result = _someInterfaceMock.Object.DoInterfaceStuff<TargetClass>(_sourceA);
result.Should().BeEquivalentTo(_target);
}
[Fact]
public void TestMockSetupSourceClassB()
{
var result = _someInterfaceMock.Object.DoInterfaceStuff(_sourceB, _target);
result.Should().BeEquivalentTo(_target);
}
}
The problem has to do with the Aggregate extension, its generic argument parameters and what you have Setup the mock to expect.
The params of the extension method DoExtensionStuff is an object array so when calling the `
T2 DoInterfaceStuff<T1, T2>(T1 parameter1, T2 parameter2)
within the Aggregate delegate you are actually passing
(TResult agg, object cur) => someInterface.DoInterfaceStuff<object,TResult>(cur, agg)
which the mock was not configured to handle.
After changing the _someInterfaceMock.Setup, in this particular case, explicitly to
_someInterfaceMock
.Setup(m => m.DoInterfaceStuff<object, TargetClass>(_sourceB, _target))
.Returns(_target);
All the tests in this scenario were able to be exercised to completion successfully.
The thing with Moq is that when a mock is not told explicitly what to expect it will return null by default for reference types.
I design some application with using Repository pattern. I have a generic repository:
public interface IGenericRepository<T> where T : class { // ... }
public class GenericRepository <T> : IGenericRepository<T> where T : class {// ... }
For example, I have two entities: Order and OrderLine.
For entity "Order" I need only generic repository methods, so it's OK.
But for entity "OrderLine" I need some generic repository methods and some addtitional. So, I create a custom repository, which extends generic:
public interface IOrderLinesRepository : IGenericRepository<OrderLine> {...}
public class OrderLinesRepository : GenericRepository<OrderLine>, IOrderLinesRepository
{
//... this is my additional methods here
}
But now I want to create method, which will return a repository by entity type.
If entity has a custom repository - it should be retuned. If not, method must return GenericRepository. Here is my attempt to create this.
public sealed class RepositoryFactory
{
// Here is custom repository types
private IDictionary<Type, Func<DbContext, object>> GetCustomFactories()
{
return new Dictionary<Type, Func<DbContext, object>>
{
{ typeof(OrderLine), dbContext =>
new OrderLinesRepository(dbContext) },
};
}
// Custom repository types
private IDictionary<Type, Type> GetRepositoryTypes()
{
return new Dictionary<Type, Type>
{
{ typeof(OrderLine), typeof(IOrderLinesRepository) }
};
}
private Func<GoodWillDbContext, object> GetDefaultFactory<T>()
where T : class
{
return dbContext => new GenericRepository<T>(dbContext);
}
private Func<DbContext, object> GetFactory<T>() where T : class
{
Func<DbContext, object> factory;
var factories = GetCustomFactories();
factories.TryGetValue(typeof(T), out factory);
return factory ?? (factory = GetDefaultFactory<T>());
}
public Type GetRepoType(Type type)
{
var types = GetRepositoryTypes();
Type repoType;
types.TryGetValue(type, out repoType);
return repoType;
}
public object MakeRepository<U>(DbContext dbContext) where U : class
{
// Get repository type
// If custom type not found, it should be standart type
// IGenericRepository<U>
var type = _repositoryFactory.GetRepoType(typeof(U)) ??
typeof(IGenericRepository<U>);
var f = _repositoryFactory.GetFactory<U>();
var repo = f(dbContext);
return repo;
}
}
But it's not working for some reason. I have some questions:
1. What type of return value should be in MakeRepository?
2. How should I cast var repo = f(dbContext); to repository type?
Or maybe there is another way to do what I need?
Your code seems really complicated for something that should be simple... The below example is just dummy code, feel free to adapt it to your needs.
public static class RepositoryFactory
{
public static IGenericRepository<T> MakeRepository<T>(DbContext context)
where T : class
{
var type = typeof(T);
if (type == typeof(Car))
{
return CarRepositoryFactory.CreateCarRpository(context);
}
else if (type == typeof(Boat))
{
return BoatRepositoryFactory.CreateBoatRepository(context);
}
}
}
How to use it:
var myRepo = (CarRepository)RepositoryFactory.MakeRepository(new MyContext());
These kind of codes are not meant to be modified frequently (you don't add a repository at each commit, do you? ;)), so do yourself a favor and keep it simple. :)
Edit : if you really want to keep your code, please add a comment to my response and I see want I'll can do. :)
So if I understand it correctly, you want to be able to get the repository type by the entity type. I modified your implementation a little and I got it to work as I believe it should.
public sealed class RepositoryFactory
{
public static RepositoryFactory Instance = new RepositoryFactory();
// Here is custom repository types
private IDictionary<Type, Func<DbContext, object>> GetCustomFactories()
{
return new Dictionary<Type, Func<DbContext, object>>
{
// { typeof(IOrderLinesRepository), dbContext => new OrderLinesRepository(dbContext) },
};
}
// Custom repository types
private IDictionary<Type, Type> GetRepositoryTypes()
{
return new Dictionary<Type, Type>
{
{ typeof(OrderLine), typeof(IOrderLinesRepository) }
};
}
private Func<DbContext, object> GetDefaultFactory<T>()
where T : class
{
return dbContext => new GenericRepository<T>(dbContext);
}
private Func<DbContext, object> GetFactory(Type factoryType)
{
if (factoryType == null) return null;
Func<DbContext, object> factory;
var factories = GetCustomFactories();
factories.TryGetValue(factoryType, out factory);
return factory;
}
public Type GetRepoType(Type type)
{
var types = GetRepositoryTypes();
Type repoType;
types.TryGetValue(type, out repoType);
return repoType;
}
public IGenericRepository<TEntity> MakeRepositoryByEntity<TEntity>(DbContext dbContext) where TEntity : class
{
var type = this.GetRepoType(typeof(TEntity));
var f = this.GetFactory(type) ?? GetDefaultFactory<TEntity>();
var repo = f(dbContext);
return (IGenericRepository<TEntity>)repo;
}
public TRepo MakeRepository<TRepo, TEntity>(DbContext dbContext)
where TRepo : class, IGenericRepository<TEntity>
where TEntity : class
{
var repo = this.MakeRepositoryByEntity<TEntity>(dbContext);
try
{
return (TRepo)repo;
}
catch (InvalidCastException)
{
throw new Exception($"Registered repository for entity ${typeof(TEntity).FullName} does not implement {typeof(TRepo).FullName}");
}
}
}
You can invoke it in one of two way:
var d = RepositoryFactory.Instance.MakeRepositoryByEntity<OrderLine>(dbContext);
var d2 = RepositoryFactory.Instance.MakeRepository<IOrderLinesRepository, OrderLine>(dbContext);
I am using a DataContractJsonSerializer to serialize an object graph. When I construct the objects, each receives a reference to an instance of a utility object (it's a factory, for creating instances of subclasses of an abstract contract class) - which works great until the graph is serialized and then deserialized again, whereupon the objects no longer have a reference to the utility object any more. I need this reference. How would you recommend I implement this (singletons don't work because separate graphs need their own instance of the object)?
Another way to accomplish is to introduce a thread static or thread local factory object, then populate your classes with it using an [OnDeserializing] callback.
Thus, if you define your types as follows:
public interface IFactory
{
}
public class Factory : IFactory
{
}
public interface IHasFactory
{
IFactory Factory { get; }
}
[DataContract]
public abstract class HasFactoryBase : IHasFactory
{
[ThreadStatic]
static IFactory deserializedFactory;
static IFactory DeserializedFactory
{
get
{
return deserializedFactory;
}
set
{
deserializedFactory = value;
}
}
public static IDisposable SetDeserializedFactory(IFactory factory)
{
return new PushValue<IFactory>(factory, () => DeserializedFactory, val => DeserializedFactory = val);
}
IFactory factory;
public IFactory Factory { get { return factory; } }
public HasFactoryBase(IFactory factory)
{
this.factory = factory;
}
[OnDeserializing]
void OnDeserializing(StreamingContext context)
{
this.factory = DeserializedFactory;
}
}
public struct PushValue<T> : IDisposable
{
Action<T> setValue;
T oldValue;
public PushValue(T value, Func<T> getValue, Action<T> setValue)
{
if (getValue == null || setValue == null)
throw new ArgumentNullException();
this.setValue = setValue;
this.oldValue = getValue();
setValue(value);
}
#region IDisposable Members
// By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class.
public void Dispose()
{
if (setValue != null)
setValue(oldValue);
}
#endregion
}
[DataContract]
public class Foo : HasFactoryBase
{
public Foo(IFactory factory)
: base(factory)
{
this.Bars = new List<Bar>();
}
[DataMember]
public List<Bar> Bars { get; set; }
}
[DataContract]
public class Bar : HasFactoryBase
{
public Bar(IFactory factory) : base(factory) { }
}
You can serialize and deserialize as follows:
var factory = new Factory();
var test = new Foo(factory)
{
Bars = { new Bar(factory) },
};
var serializer = new DataContractJsonSerializer(test.GetType());
byte [] json;
using (var stream = new MemoryStream())
{
serializer.WriteObject(stream, test);
json = stream.ToArray();
}
Foo test2;
using (HasFactoryBase.SetDeserializedFactory(factory))
using (var stream = new MemoryStream(json))
{
test2 = (Foo)serializer.ReadObject(stream);
}
if (test2.Factory != test.Factory)
throw new InvalidOperationException();
And the JSON will look like:
{
"Bars": [
{}
]
}
Some notes:
The factory object does not appear at all in the JSON.
The factory objects no longer need to inherit from some abstract base class, they can simply implement a common IFactory interface.
One way to accomplish this is with a data contract surrogate. Using surrogates, you can replace your "real" factory with a dummy stub during serialization. Then, during deserialization, replace the dummy with the desired factory.
Thus, if your classes look something like:
public abstract class FactoryBase
{
}
public class Factory : FactoryBase
{
}
public interface IHasFactory
{
FactoryBase Factory { get; }
}
[DataContract]
public abstract class HasFactoryBase : IHasFactory
{
[DataMember(IsRequired = true)]
FactoryBase factory;
public FactoryBase Factory { get { return factory; } }
public HasFactoryBase(FactoryBase factory)
{
this.factory = factory;
}
}
[DataContract]
public class Foo : HasFactoryBase
{
public Foo(FactoryBase factory)
: base(factory)
{
this.Bars = new List<Bar>();
}
[DataMember]
public List<Bar> Bars { get; set; }
}
[DataContract]
public class Bar : HasFactoryBase
{
public Bar(FactoryBase factory) : base(factory) { }
}
You define an IDataContractSurrogate to replace all occurrences of FactoryBase with a surrogate as follows:
public class FactorySurrogateSelector : IDataContractSurrogate
{
[DataContract]
class FactorySurrogate
{
}
readonly FactoryBase factory;
public FactorySurrogateSelector(FactoryBase factory)
{
this.factory = factory;
}
#region IDataContractSurrogate Members
public object GetCustomDataToExport(Type clrType, Type dataContractType)
{
throw new NotImplementedException();
}
public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
{
throw new NotImplementedException();
}
public Type GetDataContractType(Type type)
{
if (typeof(FactoryBase).IsAssignableFrom(type))
return typeof(FactorySurrogate);
return type;
}
public object GetDeserializedObject(object obj, Type targetType)
{
if (obj is FactorySurrogate)
return factory;
return obj;
}
public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
{
throw new NotImplementedException();
}
public object GetObjectToSerialize(object obj, Type targetType)
{
if (obj is FactoryBase)
{
return new FactorySurrogate();
}
return obj;
}
public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
{
throw new NotImplementedException();
}
public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
{
throw new NotImplementedException();
}
#endregion
}
Then, serialize and deserialize as follows:
var factory = new Factory();
var test = new Foo(factory)
{
Bars = { new Bar(factory) },
};
var surrogate = new FactorySurrogateSelector(factory);
var serializer = new DataContractJsonSerializer(test.GetType(), Enumerable.Empty<Type>(), int.MaxValue, false, surrogate, false);
byte[] json;
using (var stream = new MemoryStream())
{
serializer.WriteObject(stream, test);
json = stream.ToArray();
}
Foo test2;
using (var stream = new MemoryStream(json))
{
test2 = (Foo)serializer.ReadObject(stream);
}
if (test2.Factory != test.Factory)
throw new InvalidOperationException();
Notice that the desired factory was passed directly into the constructor of the FactorySurrogateSelector, then eventually set inside each type that contains instances of the factory type.
The resulting JSON will look like:
{
"factory": {},
"Bars": [
{
"factory": {}
}
]
}
Some qualifications:
Your factory must inherit from some common base class, here FactoryBase. The data contract serializers will never serialize an interface member, e.g. IFactory factory where IFactory is your factory interface, even when there is an applicable surrogate.
The empty "factory": {} objects must appear in the JSON in order for the surrogate to inject the correct "real" factory value during deserialization. Hence the [DataMember(IsRequired = true)].
I am following some practices documented by steven and using Simple Injector. I have a query that retrieves data from a WCF service and I want to cache the result using an instance of ObjectCache.
I've defined a decorator 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;
}
}
Within SimpleInjectorInitializer.cs I define the cache and policy, and add the decorator for a specific query:
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));
The problem I'm facing is that I want to be able to specify different CacheItemPolicy's for different queries. I could create a new ICachePolicy<TQuery> interface and then define concrete classes for each different query type but I'm hoping there might be a way to avoid that and define the policy per query directly in the initialization file.
I could create a new ICachePolicy interface and then define
concrete classes for each different query type
I think that's a pretty neat idea actually. You can register a default generic implementation that injected into every decorator that has no specific implementation registered:
container.RegisterOpenGeneric(typeof(ICachePolicy<>), typeof(DefaultCachePolicy<>),
Lifestyle.Singleton);
And for queries that have an alternative cache policy, you can register a specific implementation:
container.RegisterSingle<ICachePolicy<MyQuery>>(new CachePolicy<MyQuery>
{
AbsoluteExpiration = DateTime.Now.AddHour(2)
});
Another option is to mark queries or their query handlers with an attribute that describes the caching policy (this is the route I usually take):
[CachePolicy(AbsoluteExpirationInSeconds = 1 * 60 * 60)]
public class MyQuery : IQuery<string[]> { }
Now you don't have to inject an ICachePolicy<T>, but can read this metadata directly using reflection:
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.
}
You can achieve the result you require with an open generic implementation and override specific default values as required. I.e. you define an open generic implementation CachePolicy<TQuery> of ICachePolicy<TQuery> and use the RegisterInitializer method to override parts of the default implementation.
Given these definitions:
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;
}
}
Set up the container using the RegisterOpenGeneric method and configure the non default values using 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;
}
These tests demonstrate the result is as expected:
[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);
}