Autofac variable name based injection - c#

I want to inject some ValueType variable values using autofac, as we do same for Interfaces. I don't want to additionally mention in bootstrapper that this class have named parameterized injection or with Key.
For eg: This is easily injectable
Registration =>
builder.RegisterType<SqlProvider>().As<ISqlProvider>();
Injection=>
MyClass(ISqlProvider provider)
So Can we do something like:
Registration =>
builder.RegisterType<int>().Named<int>("maxRetries");
Injection=>
MyClass(int maxRetries)

I think what you need is property injection.
http://autofac.readthedocs.io/en/latest/register/prop-method-injection.html
You could for the sake of simplicity use a factory or settingsprovider just like your sqlprovider.
Because i think you want to create some servicelayer.
Pseudocode:
private iSqlProvider _sqlprovider;
private ISettingsProvider _settingsProvider;
MyClass(ISqlProvider sqlprovider, ISettingProvider settingProvider)
{
_sqlprovider = sqlprovider;
_settingsProvider = settingsProvider
}
public MyClassModel GetMyAwesomeModels()
{
var settings = _setingsprovider.getSetting()
settings.maxTries
//do your magic with maxtries
}

You can use a custom parameter and a module to have this feature.
A module allows you to add custom parameter for every registration. A parameter is a special class that Autofac will call each time a registration is resolved to check whether it can provide the requested parameter.
For example :
public class ResolvedNamedParameter : Parameter
{
public override Boolean CanSupplyValue(ParameterInfo pi,
IComponentContext context,
out Func<Object> valueProvider)
{
if (pi.ParameterType.IsValueType
&& context.IsRegisteredWithName(pi.Name, pi.ParameterType))
{
valueProvider = () => context.ResolveNamed(pi.Name, pi.ParameterType);
return true;
}
else
{
valueProvider = null;
return false;
}
}
}
Then the module to add the parameter for every registration :
public class ResolvedNamedParameterModule : Module
{
protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry,
IComponentRegistration registration)
{
registration.Preparing += (sender, e) =>
{
e.Parameters = new Parameter[] { new ResolvedNamedParameter() }.Concat(e.Parameters);
};
base.AttachToComponentRegistration(componentRegistry, registration);
}
}
And you can use it like this :
builder.RegisterModule<ResolvedNamedParameterModule>();
builder.Register(c => 3).Named<Int32>("x");
builder.Register(c => 5).Named<Int32>("y");

Try this:
Injection=>
MyClass([KeyFilter("maxRetries")]int maxRetries)
Is that what you were looking for?

Related

How can I implement Named Service with parameters in Autofac?

I've a concrete Service and it's behaviour differs by it's parameters, I could not achieve to Register and Resolve it by Autofac. As you can see it is so easy to implement it by custom Container. How can I use Autofac for this requirement?
public class Container
{
Dictionary<string, MyService> _components = new Dictionary<string, MyService>();
void Register(string key,string param, string param2)
{
_components.Add(key, new MyService(param, param2, ResolveRepository()));
}
MyService ResolveMyService(string key)
{
return _components[key];
}
IRepository ResolveRepository()
{
throw new NotImplementedException();
}
}
public class MyService
{
public MyService(string param,string param2,IRepository rep ) { }
}
public interface IRepository { }
EDIT: I'm trying the solve registration in Autofac, but we have no Container during Registration process.
builder.RegisterType<MyService>()
.Named<MyService>("Service1")
.OnActivating(e =>
{
e.ReplaceInstance(new MyService("Service1", "param1-23", Container.Resolve<IRepository>()));
});
You can access the container during the Activating pseudo event by using e.Context
builder.RegisterType<MyService>()
.Named<MyService>("Service1")
.OnActivating(e =>
{
MyService s = new MyService("Service1",
"param1-23",
e.Context.Resolve<IRepository>())
e.ReplaceInstance();
});
but another option would be to use the WithParameter method.
builder.RegisterType<MyService>()
.Named<MyService>("Service1")
.WithParameter("param1", "Service1")
.WithParameter("param2", "param1-23");

Getting Attribute Value on Member Interception

I have this CacheAttribute that accepts Duration Value like such
public class MyTestQuery : IMyTestQuery
{
private readonly ISomeRepository _someRepository;
public TestQuery(ISomeRepository someRepository)
{
_someRepository = someRepository;
}
[Cache(Duration = 10)]
public MyViewModel GetForeignKeysViewModelCache()
{
...code here...
return viewModel;
}
}
The Attribute looks like this
[AttributeUsage(AttributeTargets.Method)]
public class CacheAttribute : Attribute
{
public int Duration { get; set; }
}
When Intercepted using Castle.Proxy.IInterceptor it works but when I perform an Attribute.GetCustomAttribute either by IInvocation.MethodInvocationTarget or IInvocation.Method both returns a null value
Here it is in code
public class CacheResultInterceptor : IInterceptor
{
public CacheAttribute GetCacheResultAttribute(IInvocation invocation)
{
var methodInfo = invocation.MethodInvocationTarget;
if (methodInfo == null)
{
methodInfo = invocation.Method;
}
return Attribute.GetCustomAttribute(
methodInfo,
typeof(CacheAttribute),
true
)
as CacheAttribute;
}
public void Intercept(IInvocation invocation)
{
var cacheAttribute = GetCacheResultAttribute(invocation);
//cacheAttribute is null always
...more code here...
}
}
And this is how I register them
public class Bootstrapper
{
public static ContainerBuilder Builder;
public static void Initialise()
{
Builder = new ContainerBuilder();
...other codes in here...
CacheInstaller.Install();
var container = Builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
public class CacheInstaller
{
public static void Install()
{
Bootstrapper.Builder.RegisterType<CacheResultInterceptor>()
.SingleInstance();
Bootstrapper.Builder.RegisterAssemblyTypes(Assembly.Load("MyApplication.Web"))
.Where(t => t.Name.EndsWith("Query"))
.AsImplementedInterfaces()
.EnableInterfaceInterceptors()
.InterceptedBy(typeof(CacheResultInterceptor))
.SingleInstance();
}
}
My Expensive Method Class Ends with Query
Now the question is why invocation.MethodInvocationTarget and/or invocation.Method returns null?
What am I doing wrong?
Any other strategies so I can pass a parameter value without creating a Method for each value I can think of?
BTW I am using
Autofac 4.3.0.0
Autofac.Extras.DynamicProxy 4.2.1.0
Autofac.Integration.Mvc 4.0.0.0
Castle.Core 4.0.0.0
UPDATE 1
Here is what it returns when it runs for clarity
Here's what I found.
invocation.Method returns the method declaration on the interface, in your case IMyTestQuery.
On the other hand, invocation.MethodInvocationProxy returns the method that is going to be called when invoking invocation.Proceed(). This means it can be:
the next interceptor if you have several
a decorator if you have decorators over your interface
the final implementation of your interface
As you can see, MethodInvocationProxy is less deterministic than Method, which is why I would recommend you avoid using it, at least for what you're trying to achieve.
When you think about it, an interceptor should not be tied to an implementation as it proxies an interface, so why don't you put the [Cache] attribute at the interface level?
Using your code, I could successfully retrieve it when put on the interface.
Edit:
OK, I've put together a repository on GitHub that uses the specific versions of the NuGet packages you mentioned and shows how to retrieve an attribute on intercepted methods.
As a reminder, here are the used NuGet packages:
Microsoft.AspNet.Mvc v5.2.3
Autofac v4.3.0
Autofac.Mvc5 4.0.0
Autofac.Extras.DynamicProxy v4.2.1
Castle.Core v4.0.0
I created 2 query interfaces, IMyQuery and IMySecondQuery. Please note that as mentioned in my original answer, the [Cache] attributes are placed on the interfaces methods, not on the implementing classes.
public interface IMyQuery
{
[Cache(60000)]
string GetName();
}
public interface IMySecondQuery
{
[Cache(1000)]
string GetSecondName();
}
Then we have 2 very basic implementations of these classes. Not relevant at all, but for the sake of completeness:
public class DefaultMyQuery : IMyQuery
{
public string GetName()
{
return "Raymund";
}
}
public class DefaultMySecondQuery : IMySecondQuery
{
public string GetSecondName()
{
return "Mickaƫl Derriey";
}
}
And then the interceptor:
public class CacheResultInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
var cacheAttribute = invocation.Method.GetCustomAttribute<CacheAttribute>();
if (cacheAttribute != null)
{
Trace.WriteLine($"Found a [Cache] attribute on the {invocation.Method.Name} method with a duration of {cacheAttribute.Duration}.");
}
invocation.Proceed();
}
}
Note that the GetCustomAttribute<T> method is an extension method over MemberInfo present in the System.Reflection namespace.
Let's move on to the registration in the Autofac container. I tried to follow you registration style as much as I could:
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder
.RegisterType<CacheResultInterceptor>()
.SingleInstance();
builder
.RegisterAssemblyTypes(typeof(MvcApplication).Assembly)
.Where(x => x.Name.EndsWith("Query"))
.AsImplementedInterfaces()
.EnableInterfaceInterceptors()
.InterceptedBy(typeof(CacheResultInterceptor));
DependencyResolver.SetResolver(new AutofacDependencyResolver(builder.Build()));
The queries are then used in the HomeController:
public class HomeController : Controller
{
private readonly IMyQuery _myQuery;
private readonly IMySecondQuery _mySecondQuery;
public HomeController(IMyQuery myQuery, IMySecondQuery mySecondQuery)
{
_myQuery = myQuery;
_mySecondQuery = mySecondQuery;
}
public ActionResult MyQuery()
{
return Json(_myQuery.GetName(), JsonRequestBehavior.AllowGet);
}
public ActionResult MySecondQuery()
{
return Json(_mySecondQuery.GetSecondName(), JsonRequestBehavior.AllowGet);
}
}
What I did to test this is just put a breakpoint in the interceptor, F5 the application, open a browser and navigate to both http://localhost:62440/home/myquery and http://localhost:62440/home/myquery.
It did hit the interceptor and find the [Cache] attribute. In the Visual Studio Output window, it did show:
Found a [Cache] attribute on the GetName method with a duration of 60000.
Found a [Cache] attribute on the GetSecondName method with a duration of 1000.
Hopefully that helps you pinpoint what's going on in your project.
I pushed changes to the repository so that the first query calls the second one.
It still works. You should really make an effort and put some code on the question.

Accessing contextual information during lambda registration in Autofac? [duplicate]

This question already has an answer here:
Autofac: Register component and resolve depending on resolving parent
(1 answer)
Closed 6 years ago.
With Ninject I can do something like this:
Bind<ILogger>().ToMethod(context =>
{
// Get type info
var type = context.Request.Target.Member.DeclaringType;
var logger = new ConcreteLogger(type);
Kernel.Get<IFoo>().DoFoo(logger);
return logger;
});
How can I do this with Autofac?
This is the code I have:
builder.Register(context => {
var type = ?????
var logger = new ConcreteLogger(type);
context.Resolve<IFoo>().DoSomething(logger);
return logger;
}).As<ILogger>();
I can see in the debugger that context is actually of the type Autofac.Core.Resolving.InstanceLookup which has a member ComponentRegistration.Target but I cannot access that because InstanceLookup is an internal class.
It appears I can do this, but it doesn't give me the type information of the class that requires this injected type:
builder.Register(context => {
var lookup = c as IInstanceLookup;
var target = lookup.ComponentRegistration.Target as ComponentRegistration;
var logger = new ConcreteLogger(target.Activator.LimitType);
context.Resolve<IFoo>().DoSomething(logger);
return logger;
}).As<ILogger>();
What you need is to inject a component based on "parent" component. With Autofac you register components and these components doesn't known who need them.
By the way, you can do what you want by implementing a custom Module. Exemple :
public class TestModule : Module
{
protected override void AttachToComponentRegistration(
IComponentRegistry componentRegistry,
IComponentRegistration registration)
{
registration.Preparing += (sender, e) =>
{
Parameter parameter = new ResolvedParameter(
(pi, c) =>
{
return pi.ParameterType == typeof(ILogger);
}, (pi, c) =>
{
var p = new TypedParameter(typeof(Type),
e.Component.Activator.LimitType);
return c.Resolve<ILogger>(p);
});
e.Parameters = e.Parameters.Union(new Parameter[] { parameter });
};
base.AttachToComponentRegistration(componentRegistry, registration);
}
}
and register the module like this :
builder.RegisterModule<TestModule>();
this way, each time a component will be resolved, it will add a new parameter knowing the type being constructed to create the ILogger dependency.
Be aware that by doing this you may have captive dependency : a dependency that was built for a component but used for another one. It can happens if your ILogger registration has a different scope, for example a singleton scope.

Autofac - resolve by argument name

I'm migrating an application from Ninject to Autofac.
We used a special naming convention for injecting app settings into constructors:
public class Example{
public Example(AppSetting settingName){
...
}
}
AppSetting parameter was injected automatically using ConfigurationManager.AppSettings["settingName"].
In Ninject this was accomplished by using a custom provider:
public class AppSettingProvider : Ninject.Activation.IProvider
{
public object Create(IContext context)
{
var varName = ((Context)context).Request.Target.Name;
var value = new AppSetting(ConfigurationManager.AppSettings[varName]);
if (value.Value == null)
{
... log ...
}
return value;
}
public Type Type
{
get { return typeof(AppSetting); }
}
}
I was not able to find an alternative for this feature in Autofac. If this is not possible in an automated way I'm ok with looping over all app settings during the initial configuration step.
Any idea what to do?
Thanks,
Vilem
I have created a solution using this SO question:
Register string value for concrete name of parameter
and subsequently improved it using Travis Illig's suggestion.
Currently this seems to work exactly the same as the Ninject equivalent.
Here's the result:
public class AppSettingsModule : Module
{
protected override void AttachToComponentRegistration(
IComponentRegistry componentRegistry,
IComponentRegistration registration)
{
// Any time a component is resolved, it goes through Preparing
registration.Preparing += InjectAppSettingParameters;
}
private void InjectAppSettingParameters(object sender, PreparingEventArgs e)
{
// check if parameter is of type AppSetting and if it is return AppSetting using the parameter name
var appSettingParameter = new ResolvedParameter((par, ctx) => par.ParameterType == typeof(AppSetting), (par, ctx) => new AppSetting(ConfigurationManager.AppSettings[par.Name]));
e.Parameters = e.Parameters.Union(new List<Parameter>{ appSettingParameter});
}
}

Automapper Custom Resolver - Inject Repository into constructor

I am trying to create a custom resolver for automapper which needs to access one of my data repositories to retreive the logged in users account.
Here is my code so far...
public class FollowingResolver : ValueResolver<Audio, bool>
{
readonly IIdentityTasks identityTasks;
public FollowingResolver(IIdentityTasks identitTasks)
{
this.identityTasks = identitTasks;
}
protected override bool ResolveCore(Audio source)
{
var user = identityTasks.GetCurrentIdentity();
if (user != null)
return user.IsFollowingUser(source.DJAccount);
return false;
}
}
However I am getting this error:
FollowingResolver' does not have a default constructor
I have tried adding a default contrstructor but my repository never gets initialised then.
This is my autoampper initialisation code:
public static void Configure(IWindsorContainer container)
{
Mapper.Reset();
Mapper.Initialize(x =>
{
x.AddProfile<AccountProfile>();
x.AddProfile<AudioProfile>();
x.ConstructServicesUsing(container.Resolve);
});
Mapper.AssertConfigurationIsValid();
}
Am I missing something, is it even possible to do it like this or am I missing the boat here?
Found the solution shorlty after...i was forgetting to add my resolvers as an IoC container.
Works great now!
I was getting the same error using Castle Windsor while trying to inject a service.
I had to add:
Mapper.Initialize(map =>
{
map.ConstructServicesUsing(_container.Resolve);
});
before Mapper.CreateMap calls.
Created a ValueResolverInstaller like this:
public class ValueResolverInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly()
.BasedOn<IValueResolver>()
.LifestyleTransient());
}
}
and the ValueResolver itself:
public class DivergencesResolver : ValueResolver<MyClass, int>
{
private AssessmentService assessmentService;
public DivergencesResolver(AssessmentService assessmentService)
{
this.assessmentService = assessmentService;
}
protected override int ResolveCore(MyClass c)
{
return assessmentService.GetAssessmentDivergences(c.AssessmentId).Count();
}
}

Categories