resolving instance of class inside of method using autofac - c#

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);
});

Related

c# get repository by entity type

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);

Autofac factory that resolves instances without depending on the container

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>();
}
}

Register all class that implements IValidator<SomethingValidatable> with Unity

Suppose I defined some validation logic like this:
public class Rule1 : IValidator<SomethingValidatable>
{
public IList<Error> Validate(SomethingValidatable item)
{
// apply validation logic to item
}
}
Somewhere in code I bind IValidator<SomethingValidatable> to one or more concrete Rule:
protected override void ConfigureContainer(IUnityContainer container)
{
container
.RegisterType<IValidator<SomethingValidatable>, Rule1>("a")
.RegisterType<IValidator<SomethingValidatable>, Rule2>("b");
}
and where I need this validation occurs I have somethin like:
public class HereIValidateClass
{
private readonly IValidator<SomethingValidatable>[] validators;
public HereIValidateClass(IValidator<SomethingValidatable>[] validators)
{
this.validators = validators;
}
public IList<Error> DoSomething()
{
foreach (var validator in validators)
{
var errors = validator.Validate(something);
// append to IList<Error>
}
return errors;
}
}
This do the job. But I would avoid to register explicitely one by one each rules. I really like to say: Dear Unity, register each class in a given namespace that implements IValidator<SomethingValidatable>.
How about an extension method for IUnityContainer:
public static class UnityExtensions
{
public static void RegisterValidators(this IUnityContainer container)
{
var types = AllClasses.FromLoadedAssemblies().GetValidators();
foreach (var type in types)
{
var interfaces = WithMappings.FromAllInterfaces(type);
foreach (var #interface in interfaces)
{
container.RegisterType(#interface, type);
}
}
}
public static IEnumerable<Type> GetValidators(this IEnumerable<Type> types)
{
var wantedType = typeof(IValidator<>);
return from type in types
from interfaceType in type.GetInterfaces()
where interfaceType.IsGenericType
&& !interfaceType.ContainsGenericParameters
&& wantedType.IsAssignableFrom(interfaceType.GetGenericTypeDefinition())
select type;
}
}
And if you need more info about the specific registrations you can use an attribute.
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class MyValidationAttribute : Attribute
{
public string RegistrationName { get; set; }
public MyValidationAttribute(string name)
{
RegistrationName = name;
}
}
And then change the extension method to something like this:
public static void RegisterValidators(this IUnityContainer container)
{
var types = AllClasses.FromLoadedAssemblies().GetValidators();
foreach (var type in types)
{
var interfaces = WithMappings.FromAllInterfaces(type);
foreach (var #interface in interfaces)
{
var myAttr = type.GetCustomAttributes<MyValidationAttribute>(false);
foreach (var attr in myAttr)
{
// Register with specific name.
container.RegisterType(#interface, type, attr.RegistrationName);
}
}
}
}
And change your registration:
protected override void ConfigureContainer(IUnityContainer container)
{
container.RegisterValidators();
}
It will register each implementation for IValidator, regardless of T.

Assigning methods to object at run-time - Design Pattern

I have created an architecture in my C# code which does exactly what I want, but seems it would be very difficult to maintain in the long-run and am hoping there's a design pattern / better architecture I could be pointed towards.
I have created an object Test which, again, does exactly what I need perfectly which has the following structure:
class Test
{
public static Dictionary<string, Func<Test, object>> MethodDictionary;
public double Var1;
public double Var2;
private Lazy<object> _test1;
public object Test1 { get { return _test1.Value; } }
private Lazy<object> _test2;
public object Test2 { get { return _test2.Value; } }
public Test()
{
_test1 = new Lazy<object>(() => MethodDictionary["Test1"](this), true);
_test2 = new Lazy<object>(() => MethodDictionary["Test2"](this), true);
}
}
What this allows me to do is, at run-time to assign a dictionary of functions to my Test object and the 2 properties Test1 & Test2 will use the functions loaded into it to return values.
The implementation looking somewhat as follows:
class Program
{
static void Main(string[] args)
{
Dictionary<string, Func<Test, object>> MethodDictionary = new Dictionary<string,Func<Test,object>>();
MethodDictionary.Add("Test1", TestMethod1);
MethodDictionary.Add("Test2", TestMethod2);
Test.MethodDictionary = MethodDictionary;
var x = new Test() { Var1 = 20, Var2 = 30 };
Console.WriteLine(x.Test1.ToString());
Console.WriteLine(x.Test2.ToString());
Console.ReadKey();
}
private static object TestMethod1(Test t)
{ return t.Var1 + t.Var2; }
private static object TestMethod2(Test t)
{ return t.Var1 - t.Var2; }
}
And it works great and has proven very efficient for large sets of Test objects.
My challenge is that if I ever want to add in a new method to my Test class, I need to add in the:
private Lazy<object> _myNewMethod;
public object MyNewMethod { get { return _myNewMethod.Value; } }
Update the constuctor with the key to look for in the dictionary
And, although that is pretty simple, I'd love to have a 1-line add-in (maybe some form of custom object) or have the properties read directly form the dictionary without any need for defining them at all.
Any ideas? ANY help would be great!!!
Thanks!!!
One of the ways in which you could achieve your desired behavior, is to use something that resembles a miniature IoC framework for field injection, tuned to your specific use case.
To make things easier, allow less typing in your concrete classes and make things type-safe, we introduce the LazyField type:
public class LazyField<T>
{
private static readonly Lazy<T> Default = new Lazy<T>();
private readonly Lazy<T> _lazy;
public LazyField() : this(Default) { }
public LazyField(Lazy<T> lazy)
{
_lazy = lazy;
}
public override string ToString()
{
return _lazy.Value.ToString();
}
public static implicit operator T(LazyField<T> instance)
{
return instance._lazy.Value;
}
}
Furthermore, we define an abstract base class, that ensures that these fields will be created at construction time:
public abstract class AbstractLazyFieldHolder
{
protected AbstractLazyFieldHolder()
{
LazyFields.BuildUp(this); // ensures fields are populated.
}
}
Skipping for a moment how this is achieved (explained further below), this allows the following way of defining your Test class:
public class Test : AbstractLazyFieldHolder
{
public double Var1;
public double Var2;
public readonly LazyField<double> Test1;
public readonly LazyField<double> Test2;
}
Note that these fields are immutable, initialized in the constructor. Now, for your usage example, the below snippet shows the "new way" of doing this:
LazyFields.Configure<Test>()
// We can use a type-safe lambda
.SetProvider(x => x.Test1, inst => inst.Var1 + inst.Var2)
// Or the field name.
.SetProvider("Test2", TestMethod2);
var x = new Test() { Var1 = 20, Var2 = 30 };
Console.WriteLine(x.Test1);
double test2Val = x.Test2; // type-safe conversion
Console.WriteLine(test2Val);
// Output:
// 50
// -10
The class below provides the services that support the configuration and injection of these field value.
public static class LazyFields
{
private static readonly ConcurrentDictionary<Type, IBuildUp> _registry = new ConcurrentDictionary<Type,IBuildUp>();
public interface IConfigureType<T> where T : class
{
IConfigureType<T> SetProvider<FT>(string fieldName, Func<T, FT> provider);
IConfigureType<T> SetProvider<F, FT>(Expression<Func<T, F>> fieldExpression, Func<T, FT> provider) where F : LazyField<FT>;
}
public static void BuildUp(object instance)
{
System.Diagnostics.Debug.Assert(instance != null);
var builder = _registry.GetOrAdd(instance.GetType(), BuildInitializer);
builder.BuildUp(instance);
}
public static IConfigureType<T> Configure<T>() where T : class
{
return (IConfigureType<T>)_registry.GetOrAdd(typeof(T), BuildInitializer);
}
private interface IBuildUp
{
void BuildUp(object instance);
}
private class TypeCfg<T> : IBuildUp, IConfigureType<T> where T : class
{
private readonly List<FieldInfo> _fields;
private readonly Dictionary<string, Action<T>> _initializers;
public TypeCfg()
{
_fields = typeof(T)
.GetFields(BindingFlags.Instance | BindingFlags.Public)
.Where(IsLazyField)
.ToList();
_initializers = _fields.ToDictionary(x => x.Name, BuildDefaultSetter);
}
public IConfigureType<T> SetProvider<FT>(string fieldName, Func<T,FT> provider)
{
var pi = _fields.First(x => x.Name == fieldName);
_initializers[fieldName] = BuildSetter<FT>(pi, provider);
return this;
}
public IConfigureType<T> SetProvider<F,FT>(Expression<Func<T,F>> fieldExpression, Func<T,FT> provider)
where F : LazyField<FT>
{
return SetProvider((fieldExpression.Body as MemberExpression).Member.Name, provider);
}
public void BuildUp(object instance)
{
var typedInstance = (T)instance;
foreach (var initializer in _initializers.Values)
initializer(typedInstance);
}
private bool IsLazyField(FieldInfo fi)
{
return fi.FieldType.IsGenericType && fi.FieldType.GetGenericTypeDefinition() == typeof(LazyField<>);
}
private Action<T> BuildDefaultSetter(FieldInfo fi)
{
var itemType = fi.FieldType.GetGenericArguments()[0];
var defValue = Activator.CreateInstance(typeof(LazyField<>).MakeGenericType(itemType));
return (inst) => fi.SetValue(inst, defValue);
}
private Action<T> BuildSetter<FT>(FieldInfo fi, Func<T, FT> provider)
{
return (inst) => fi.SetValue(inst, new LazyField<FT>(new Lazy<FT>(() => provider(inst))));
}
}
private static IBuildUp BuildInitializer(Type targetType)
{
return (IBuildUp)Activator.CreateInstance(typeof(TypeCfg<>).MakeGenericType(targetType));
}
}
Look at library https://github.com/ekonbenefits/impromptu-interface.
With it and using DynamicObject i wrote sample code that shows how to simplify adding new methods:
public class Methods
{
public Methods()
{
MethodDictionary = new Dictionary<string, Func<ITest, object>>();
LazyObjects = new Dictionary<string, Lazy<object>>();
}
public Dictionary<string, Func<ITest, object>> MethodDictionary { get; private set; }
public Dictionary<string, Lazy<object>> LazyObjects { get; private set; }
}
public class Proxy : DynamicObject
{
Methods _methods;
public Proxy()
{
_methods = new Methods();
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = _methods.LazyObjects[binder.Name].Value;
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
_methods.MethodDictionary[binder.Name] = (Func<ITest, object>)value;
_methods.LazyObjects[binder.Name] = new Lazy<object>(() => _methods.MethodDictionary[binder.Name](this.ActLike<ITest>()), true);
return true;
}
}
//now you can add new methods by add single method to interface
public interface ITest
{
object Test1 { get; set; }
object Test2 { get; set; }
}
class Program
{
static void Main(string[] args)
{
var x = new Proxy().ActLike<ITest>();
x.Test1 = new Func<ITest, object>((y) => "Test1");
x.Test2 = new Func<ITest, object>((y) => "Test2");
Console.WriteLine(x.Test1);
Console.WriteLine(x.Test2);
}
}
I don't know what you are trying to do, but I think you can use a simpler approach like this:
class Test
{
public static Dictionary<string, Func<Test, object>> MethodDictionary;
public double Var1;
public double Var2;
}
Calling the function is simple:
static void Main(string[] args)
{
Dictionary<string, Func<Test, object>> MethodDictionary = new Dictionary<string,Func<Test,object>>();
MethodDictionary.Add("Test1", TestMethod1);
MethodDictionary.Add("Test2", TestMethod2);
Test.MethodDictionary = MethodDictionary;
var x = new Test() { Var1 = 20, Var2 = 30 };
Console.WriteLine(Test.MethodDictionary["Test1"](x).ToString());
Console.WriteLine(Test.MethodDictionary["Test2"](x).ToString());
Console.ReadKey();
}

Conditional registering decorator for open-generic with autofac

I have these open-generics:
public interface IQuery<out TResult> {}
public interface ICacheableQuery<out TResult> : IQuery<TResult> {
string CacheKey { get; }
}
public interface IQueryHandler<in TQuery, out TResult>
where TQuery : IQuery<TResult> {
TResult Handle(TQuery query);
}
and this single decorator:
public class CacheableQueryHandlerDecorator<TQuery, TResult>
: IQueryHandler<TQuery, TResult>
where TQuery : ICacheableQuery<TResult> {
public TResult Handle(TQuery query) {
// doing stuffs....
}
}
What I want is to register decorator only for queries which are implementing ICacheableQuery<out TResult>. I'm registering components like this:
builder.RegisterAssemblyTypes(assemblies)
.AsClosedTypesOf(typeof (IQueryHandler<,>))
.AsImplementedInterfaces()
.Named("queryHandler",typeof(IQueryHandler<,>));
builder.RegisterGenericDecorator(
typeof(CacheableQueryHandlerDecorator<,>),
typeof(IQueryHandler<,>),
fromKey: "queryHandler");
But it registers the decorator for all types. Any idea? Thanks in advance.
Unfortunately, Autofac hasn't got this feature out-of-the-box. However, it can be achieved implementing a RegistrationSource:
public interface IGenericDecoratorRegistrationBuilder : IHideObjectMembers
{
IGenericDecoratorRegistrationBuilder Decorator(Type decoratorType, Func<Type, bool> filter = null, Func<Type, IEnumerable<Parameter>> paramsGetter = null);
}
public static class GenericDecorators
{
public class GenericDecoratorRegistration
{
public Type Type;
public Func<Type, bool> Filter;
public Func<Type, IEnumerable<Parameter>> ParamsGetter;
}
class GenericDecoratorRegistrationBuilder : IGenericDecoratorRegistrationBuilder
{
readonly List<GenericDecoratorRegistration> decorators = new List<GenericDecoratorRegistration>();
public IEnumerable<GenericDecoratorRegistration> Decorators
{
get { return decorators; }
}
public IGenericDecoratorRegistrationBuilder Decorator(Type decoratorType, Func<Type, bool> filter, Func<Type, IEnumerable<Parameter>> paramsGetter)
{
if (decoratorType == null)
throw new ArgumentNullException("decoratorType");
if (!decoratorType.IsGenericTypeDefinition)
throw new ArgumentException(null, "decoratorType");
var decorator = new GenericDecoratorRegistration
{
Type = decoratorType,
Filter = filter,
ParamsGetter = paramsGetter
};
decorators.Add(decorator);
return this;
}
}
class GenericDecoratorRegistrationSource : IRegistrationSource
{
readonly Type decoratedType;
readonly IEnumerable<GenericDecoratorRegistration> decorators;
readonly object fromKey;
readonly object toKey;
public GenericDecoratorRegistrationSource(Type decoratedType, IEnumerable<GenericDecoratorRegistration> decorators, object fromKey, object toKey)
{
this.decoratedType = decoratedType;
this.decorators = decorators;
this.fromKey = fromKey;
this.toKey = toKey;
}
public bool IsAdapterForIndividualComponents
{
get { return true; }
}
public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
var swt = service as IServiceWithType;
KeyedService ks;
if (swt == null ||
(ks = new KeyedService(fromKey, swt.ServiceType)) == service ||
!swt.ServiceType.IsGenericType || swt.ServiceType.GetGenericTypeDefinition() != decoratedType)
return Enumerable.Empty<IComponentRegistration>();
return registrationAccessor(ks).Select(cr => new ComponentRegistration(
Guid.NewGuid(),
BuildActivator(cr, swt),
cr.Lifetime,
cr.Sharing,
cr.Ownership,
new[] { toKey != null ? (Service)new KeyedService(toKey, swt.ServiceType) : new TypedService(swt.ServiceType) },
cr.Metadata,
cr));
}
DelegateActivator BuildActivator(IComponentRegistration cr, IServiceWithType swt)
{
var limitType = cr.Activator.LimitType;
var actualDecorators = decorators
.Where(d => d.Filter != null ? d.Filter(limitType) : true)
.Select(d => new { Type = d.Type, Parameters = d.ParamsGetter != null ? d.ParamsGetter(limitType) : Enumerable.Empty<Parameter>() })
.ToArray();
return new DelegateActivator(cr.Activator.LimitType, (ctx, p) =>
{
var typeArgs = swt.ServiceType.GetGenericArguments();
var service = ctx.ResolveKeyed(fromKey, swt.ServiceType);
foreach (var decorator in actualDecorators)
{
var decoratorType = decorator.Type.MakeGenericType(typeArgs);
var #params = decorator.Parameters.Concat(new[] { new TypedParameter(swt.ServiceType, service) });
var activator = new ReflectionActivator(decoratorType, new DefaultConstructorFinder(), new MostParametersConstructorSelector(),
#params, Enumerable.Empty<Parameter>());
service = activator.ActivateInstance(ctx, #params);
}
return service;
});
}
}
public static IGenericDecoratorRegistrationBuilder RegisterGenericDecorators(this ContainerBuilder builder, Type decoratedServiceType, object fromKey, object toKey = null)
{
if (builder == null)
throw new ArgumentNullException("builder");
if (decoratedServiceType == null)
throw new ArgumentNullException("decoratedServiceType");
if (!decoratedServiceType.IsGenericTypeDefinition)
throw new ArgumentException(null, "decoratedServiceType");
var rb = new GenericDecoratorRegistrationBuilder();
builder.RegisterCallback(cr => cr.AddRegistrationSource(new GenericDecoratorRegistrationSource(decoratedServiceType, rb.Decorators, fromKey, toKey)));
return rb;
}
}
Sample usage:
public interface IGeneric<T>
{
void SomeMethod();
}
class IntImpl : IGeneric<int>
{
public void SomeMethod() { }
}
class StringImpl : IGeneric<string>
{
public void SomeMethod() { }
}
class GenericDecorator<T> : IGeneric<T>
{
IGeneric<T> target;
public GenericDecorator(IGeneric<T> target)
{
this.target = target;
}
public void SomeMethod()
{
target.SomeMethod();
}
}
static void Configure(ContainerBuilder builder)
{
builder.RegisterType<IntImpl>().Named<IGeneric<int>>("generic");
builder.RegisterType<StringImpl>().Named<IGeneric<string>>("generic");
builder.RegisterGenericDecorators(typeof(IGeneric<>), "generic")
// applying decorator to IGeneric<string> only
.Decorator(typeof(GenericDecorator<>), t => typeof(IGeneric<string>).IsAssignableFrom(t));
}
Please note
You must key the registrations of decorated components because (as far as I know) there's no way to override these with dynamic registrations provided by the RegistrationSource.
In this solution the decorated component inherits the configuration of the decorated one (scoping, sharing, ownership, etc)

Categories