I'm having a little trouble wrapping my head around the Ninject Factory Extension.
I have the following class structure
public class Resource
{
public IResourceLoader ResourceLoader {get;set;}
public Resource(IResourceLoader ResourceLoader)
{
this.ResourceLoader = ResourceLoader ;
}
}
public class Banner : Resource
{
public Banner([Named("pngLoader")] IResourceLoader ResourceLoader)
:base(ResourceLoader)
{ }
}
public class MetaData : Resource
{
public MetaData ([Named("xmlLoader") IResourceLoader ResourceLoader)
:base(ResourceLoader)
{ }
}
interface IResourceLoader
{
object resource {get;}
}
public class XMLLoader : IResourceLoader
{
public resource { return "this was sent by xml loader"; }
}
public class PNGLoader : IResourceLoader
{
public resource { return "this was sent by png loader"; }
}
I'm trying to implement convention based filtering based on the Named attribute as show here. So I implemented the following interface.
interface IResourceLoaderFactory
{
IResourceLoader GetxmlLoader();
IResourceLoader GetpngLoader()
}
And then my bindings in the dependency resolver look like
kernel.Bind<IResourceLoader>().To<XMLLoader>().NamedLikeFactoryMethod((IResourceLoaderFactory f) => f.GetxmlLoader());
kernel.Bind<IResourceLoader>().To<PNGLoader>().NamedLikeFactoryMethod((IResourceLoaderFactory f) => f.GetpngLoader());
Assuming the above is correct, I don't know how to proceed to have it so that Ninject gives Banner or MetaData the correct IResourceLoader based on the [Named] in the constructor that it passes it to the base constructor.
I'm using all of this in an mvc 5 application like
public class HomeController
{
public ActionResult Index(/* do banners and meta need to be asked for here? */)
{
/* or do I need to instantiate them here/? */
Banner banner = new Banner(/* what to put here? */);
Meta meta = new Meta(/* what to put here? */);
...
}
}
Thanks
Let me try to answer your question, i'm not a 100% sure i've understand your question correctly, so please give me feedback if i haven't.
Now, your basic problem is that you want to inject an IResourceLoader - but depending on what you inject it into, it should either be an XMLLoader or a PNGLoader.
You've correctly identified named bindings as one possible solution for choosing the appropriate IResourceLoader.
However, you don't need to combine NamedLikeFactory and [Named]-Attribute pattern to achieve what you want, one of those is enough, and here the [Named]-Attribute is probably the better alternative of the two (there is a third which i'll get to later).
So here's what you do:
public const string XmlLoaderName = "XmlLoader";
public const string PngLoaderName = "PngLoader";
Bind<IResourceLoader>().To<XMLLoader>()
.Named(XmlLoaderName);
Bind<IResourceLoader>().To<PNGLoader>()
.Named(PngLoaderName);
And then you specify the appropriate type in the ctor (as you did):
public class Banner : Resource
{
public Banner([Named(pngLoaderName)] IResourceLoader ResourceLoader)
:base(ResourceLoader)
{ }
}
public class MetaData : Resource
{
public MetaData ([Named(xmlLoaderName) IResourceLoader ResourceLoader)
:base(ResourceLoader)
{ }
}
and that's basically it!
now to use it in your controller all you've got to do is:
public class HomeController
{
public HomeController(Banner baner, MetaData metaData)
{
...
}
}
no need to use a factory. Except, in case you need to instantiate a Banner orMetaData instance per request, in which case you would create a factory interface:
public interface IResourceFactory
{
Banner CreateBanner();
MetaData CreateMetaData();
}
which is bound like:
Bind<IResourceFactory>().ToFactory();
// ToFactory is an extension method from Ninject.Extensions.Factory
which will be used like:
public class HomeController
{
private readonly IResourceFactory resourceFactory;
public HomeController(IResourceFactory resourceFactory)
{
this.resourceFactory = resourceFactory;
}
public ActionResult Index()
{
var banner = this.resourceFactory.CreateBanner();
....
}
}
Alternative for Named binding: You could also use a conditional binding like:
Bind<IResourceLoader>().To<XMLLoader>()
.WhenInjectedInto<Banner>();
and corresponding Banner ctor:
public Banner(IResourceLoader resourceLoader)
{
...
}
This alternative can make sense if there's a very limited set of classes which get a ResourceLoader injected. Or if you can't add an [Named]-Attribute to the ctor (for example because it's a third party library...).
Another alternative altogether again would be to give Ninject more information on how to construct a type. For Example:
Bind<Banner>().ToSelf()
.WithConstructorArgument(
typeof(IResourceLoader),
ctx => ctx.Kernel.Get<XmlLoader>());
Related
I am trying to develop a DisplayName Attribute which has an interface for localization service, which is already registered at startup and working if injected in a constructor.
How can I get the localization service interface to be instantiated since I cant use a construction injection?
This is my code
public class MyDisplayNameAttribute : System.ComponentModel.DisplayNameAttribute
{
private string _resourceValue = string.Empty;
private ILocalizationService _localizationService;
public MyDisplayNameAttribute(string resourceKey)
: base(resourceKey)
{
ResourceKey = resourceKey;
}
public string ResourceKey { get; set; }
public override string DisplayName
{
get
{
_resourceValue = _localizationService.GetLocaleString(ResourceKey);
return _resourceValue;
}
}
public string Name
{
get { return nameof(MyDisplayNameAttribute); }
}
}
Thanks
I hope you could solve the problem, this is a very simple solution but as you knew it's an anti-pattern :
public class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
public LocalizedDisplayNameAttribute(string Name) : base(Name)
{
}
public override string DisplayName
{
get
{
var _localizationService= new HttpContextAccessor().HttpContext.RequestServices.GetService<ILocalizationService>();
return _localizationService.Get(base.DisplayNameValue).Result;
}
}
}
I Hope it helps others at least ;)
Dependency injection is working with invertion of control (https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2). So framework controls your app and when instantiating your clsses injects dependencies requested via constructor.
Refer also here How to create a class without constructor parameter which has dependency injection
So I suspect that it is not possible to inject dependency without using constructor.
May be if you describe you intention there may be another good solution.
This question already has an answer here:
Mock HttpRequest in ASP.NET Core Controller
(1 answer)
Closed 4 years ago.
I'm having the following setup:
public class ExampleBaseController : Microsoft.AspNetCore.Mvc.Controller
{
public UserDetails UserDetails => Request.GetUserDetailsFromHttpHeaders();
}
public class ExampleConcreteController : ExampleBaseController
{
// UserDetails is being used in here
// this is the class under test
I need to be able to inject UserDetails during production run and also be able to mock it during tests.
Since UserDetails depends on Request and Request is a member of Microsoft.AspNetCore.Mvc.Controller I do not know how to achieve this.
If you want to mock something, you should first allow mocking on it. If you want to mock UserDetails you should allow mocking on its getter and pass required context inside newly crafted contract:
public class ExampleBaseController : Microsoft.AspNetCore.Mvc.Controller
{
private readonly IUserDetailsProvider _userDetailsProvider;
public UserDetails UserDetails => _userDetailsProvider.Get(Request);
public ExampleBaseController(IUserDetailsProvider userDetailsProvider)
{
_userDetailsProvider = userDetailsProvider;
}
}
So, in test you mock IUserDetailsProvider to return some "foobar". In production you just invoking GetUserDetailsFromHttpHeaders() method on passed inside Request.
To answer question about Request and Controller relations. Controller depends on Request, yes, and Microsoft thought that it will be good to strongly merge them together instead of passing dependency, for example like this:
public class FooBarController : Microsoft.AspNetCore.Mvc.Controller
{
private readonly System.Web.HttpRequestBase _request;
public FooBarController(System.Web.HttpRequestBase request)
{
_request = request;
}
}
Or even like this:
public class FooBarController : Microsoft.AspNetCore.Mvc.Controller
{
public void ProcessRequest(System.Web.HttpRequestBase request)
{
//request here
}
}
They instead used Property injection, which leaves developer with no way to affect injection. This is a problem. But not unsolvable - you just pass context inside (by delegate, by interface, by reference), if you need one of those coupled together objects.
It could be not so convinient as solution, proposed by #eocron, but still:
public interface IWithUserDetails
{
UserDetails UserDetails();
}
public class ExampleBaseController : Microsoft.AspNetCore.Mvc.Controller, IWithUserDetails
{
public UserDetails UserDetails()
{
return Request.GetUserDetailsFromHttpHeaders();
}
}
Same name for class and method is not a best way to do, but it was like it in the example with a property
Here is another point of view:
public interface IUserDetailsProviderOptions
{
Func<UserDetails> UserDetailsProvider { get; set; }
}
public class DefaultUserDetailsProviderOptions : IUserDetailsProviderOptions
{
public Func<UserDetails> UserDetailsProvider {get; set;}
}
public class ExampleBaseController : Microsoft.AspNetCore.Mvc.Controller
{
private readonly Func<UserDetails> _userDetailsProvider;
public UserDetails UserDetails => _userDetailsProvider();
public ExampleBaseController(IUserDetailsProviderOptions options)
{
_userDetailsProvider = options.UserDetailsProvider ??
Request.GetUserDetailsFromHttpHeaders;
}
}
Register in Startup.cs like this:
services.AddSingleton<IUserDetailsProviderOptions, DefaultUserDetailsProviderOptions>();
In testing you could do:
public class StubUserDetailsOption : IUserDetailsProviderOptions
{
public Func<UserDetails> UserDetailsProvider { get; set; } = () => new StubDetails();
}
var controller = new ExampleBaseController(new StubUserDetailsOption());
and do testing.
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.
I am using Ninject 3 in an MVC5-based website, and trying to work out how to get DI to work with a type that tests properties of a Uri.Host value passed into its constructor. I'd like the binding to somehow provide the current URL. The minimal structure I've tried initially is:
public class StructuredUrlTester : IStructuredUrlTester
{
// Expose public getters for parts of the uri.Host value
bool MyBooleanProperty { get; private set; }
public StructuredUrlTester(Uri uri)
{
// Test the value of uri.Host and extract parts via regex
}
}
// In Global.asax.cs
public class MvcApplication : NinjectHttpApplication
{
protected override IKernel CreateKernel()
{
kernel.Bind<IStructuredUrlTester>()
.To<StructuredUrlTester>()
.InTransientScope();
.WithConstructorArgument("uri", Request.Url);
}
}
// In MyController.cs
public class MyController : Controller
{
private readonly IStructuredUrlTester _tester;
public ContentPageController(IStructuredUrlTester tester)
{
this._tester = tester;
}
public ActionResult Index()
{
string viewName = "DefaultView";
if (this._tester.MyBooleanProperty)
{
viewName = "CustomView";
}
return View(viewName);
}
}
As the CreateKernel() call happens before the Request object is available, the .WithConstructorArgument() part throws an exception ("System.Web.HttpException: Request is not available in this context").
How can I provide the binding of interface to concrete type, whilst also being able to provide the e.g. HttpContext.Current.Request.Url value (available within the Controller) to the constructor of the concrete type, at run-time when it's available?
Wrap the desired functionality in an abstraction:
public interface IUriProvider {
Uri Current { get; }
}
Refactor the tester class:
public class StructuredUrlTester : IStructuredUrlTester {
// Expose public getters for parts of the uri.Host value
bool MyBooleanProperty { get; private set; }
public StructuredUrlTester(IUriProvider provider) {
Uri uri = provider.Current;
// Test the value of uri.Host and extract parts via regex
}
}
The provider implementation should wrap the Request.Url:
public class UriProvider : IUriProvider {
public Uri Current { get { return HttpContext.Current.Request.Url; } }
}
And note that the Current property should actually be called within the action of a controller where HttpContext and its request are available.
public class ActionFilterVersionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.Request.Headers.Any(x => x.Key == "SetInternalVersion"))
{
// determine somehow that the **InternalSystem implementation** should be resolved when the controller class is instantiated with the **ISystem constructor** parameter
}
else
{
// determine somehow that the **ExternalSystem implementation** should be resolved when the controller class is instantiated with the **ISystem constructor** parameter
}
base.OnActionExecuting(actionContext);
}
}
I have ExternalSystem/InternalSystem with the ISystem interface.
How can I tell autofac to inject the ExternalSystem or InternalSystem into the instantiated controller as ISystem instance depending on the string value I pass in the ActionFilter or maybe message handler.
I know I can do stuff like:
builder.RegisterType<InternalSystem>().As<ISystem>().Keyed<ISystem>("Internal");
where I can use a func<string,ISystem> factory to resolve the class during runtime but this is not what I want to do.
Actually I need to register the ISystem within the the action filter, but then I would need somehow to pass the container into the filter, but that is not what I want...and prolly its also not possible.
// Action: returns external or internal value
public string Get()
{
return resolvedISystem.Get();
}
Of course I could resolve the ISystem depending on the func factory within each single action or put behavior into a base controller where I check for the header, but I really would prefer the action filter as it can be just globally registerd ONE time, but for each new controller I have to subclass the base controller.
Base controller sample with pseudo code , because the base.Request is null which needs another workaround/fix...
public class BaseController : ApiController
{
public BaseController(Func<string, ISystem> dataServiceFactory)
{
string system = base.Request.Headers.Any(x => x.Key == "SetInternalVersion") ? "internal" : "external";
System = dataServiceFactory(system);
}
public ISystem System { get; set; }
}
UPDATING the container is also marked as OBSOLETE by the Autofac author.
Thus I do not want to add registrations in my filter/handler and update/build the container again.
I think you should not use ActionFilter at all. You have a controller dependency which should be resolved properly based on the information coming from request. Here is a possible solution. You can use a static HttpContext.Current property in order to extract request header.
System classes:
public interface ISystem { }
public class ExternalSystem : ISystem { }
public class InternalSystem : ISystem { }
SystemKeyProvider:
public enum SystemKey
{
External,
Internal
}
public interface ISystemKeyProvider
{
SystemKey GetSystemKey();
}
public class SystemKeyProvider : ISystemKeyProvider
{
private const string HeaderKey = "SetInternalVersion";
private readonly HttpRequest _request;
public SystemKeyProvider(HttpRequest request)
{
_request = request;
}
public SystemKey GetSystemKey()
{
return (_request.Headers[HeaderKey] != null) ?
SystemKey.Internal :
SystemKey.External;
}
}
Controller constructor: ValuesController(ISystem system)
Autofac container registration:
var builder = new ContainerBuilder();
builder.Register(c => HttpContext.Current.Request).As<HttpRequest>().InstancePerRequest();
builder.RegisterType<SystemKeyProvider>().AsImplementedInterfaces();
// service registration
builder.RegisterType<ExternalSystem>().Keyed<ISystem>(SystemKey.External);
builder.RegisterType<InternalSystem>().Keyed<ISystem>(SystemKey.Internal);
builder.Register(c =>
c.ResolveKeyed<ISystem>(c.Resolve<ISystemKeyProvider>().GetSystemKey()))
.As<ISystem>();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
GlobalConfiguration.Configuration.DependencyResolver =
new AutofacWebApiDependencyResolver(builder.Build());
In this solution I created a SystemKeyProvider wrapper class which is responsible for providing appropriate key in order to resolve ISystem.
Demo:
When no SetInternalSystem header is present.
Then the dependency is resolved as ExternalSystem.
When SetInternalSystem header is present.
Then the dependency is resolved as InternalSystem.