I would like to have my own injection attribute so that I am not coupling my code to a particular IOC framework. I have a custom injection attribute that my code uses to denote that a property should be injected.
public class CustomInjectAttribute : Attribute {}
Fictitious example below...
public class Robot : IRobot
{
[CustomInject]
public ILaser Zap { get; set; }
...
}
In Ninject, you can setup an injection Heuristic to find that attribute, and inject like;
public class NinjectInjectionHeuristic : NinjectComponent, IInjectionHeuristic, INinjectComponent, IDisposable
{
public new bool ShouldInject(MemberInfo member)
{
return member.IsDefined(typeof(CustomInjectAttribute), true);
}
}
and then register the heuristic with the kernel.
Kernel.Components.Get<ISelector>().InjectionHeuristics.Add(new NinjectInjectionHeuristic());
How would I go about achieving this with StructureMap. I know StructureMap has its own SetterProperties and attributes, but I'm looking for a way to decouple from that as you can with Ninject in the above example.
Use the SetAllProperties() method in your ObjectFactory or Container configuration. For example:
new Container(x =>
{
x.SetAllProperties(by =>
{
by.Matching(prop => prop.HasAttribute<CustomInjectAttribute>());
});
});
This makes use of a handy extension method (that should be in the BCL):
public static bool HasAttribute<T>(this ICustomAttributeProvider provider) where T : Attribute
{
return provider.GetCustomAttributes(typeof (T), true).Any();
}
Related
I have a class that implements the IValidatableObject interface, in order to validate the incoming data introduced by the user. The problem is that in order to validate that data, I need to use a class that implements the data repository pattern, which is in another assembly. Something like this:
public class SelectedFilteringCriteria : IValidatableObject
{
private IFiltersRepository _filtersRepository;
public SelectedFilteringCriteria(IFiltersRepository filtersRepository)
{
_filtersRepository = filtersRepository;
}
public int? SelectedValue { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var results = new List<ValidationResult>();
var valueOk = _filtersRepository.GetFilters().Any(
filter => filter.Value == SelectedValue
);
if (!valueOk)
{
results.Add(new ValidationResult("Not good :("));
}
return results;
}
}
The dependency container I'm using is Ninject. I would like to know if there's a way to tell MVC to inject the repository class into the IValidatableObject when it's going to be created.
Nope, it seems it's not possible because the MutableObjectModelBinder class, which is the one MVC 5 uses to create the corresponding object (SelectedFilteringCriteria in my case) from the action parameters, uses System.Activator instead of resolving the dependencies SelectedFilteringCriteria could have using the current DependencyResolver.
A workaround for this could be to do this inside the constructor of SelectedFilteringCriteria:
public SelectedFilteringCriteria()
{
_filtersRepository = DependencyResolver.Current.GetService<IFiltersRepository>();
}
But that could drive to the Service Locator Antipattern. Your choice.
I am building the asp.net WebAPI project and have used Autofac as IOC. Now i am doing constructor based injection and calls the various methods of the business class from the controller class.
Now i want to pass some additional data into the business class via the public property IncomingUser such as this one :-
My interface looks like this :-
public interface IUserManager
{
string IncomingUser { set; get; }
Task<List<String>> GetUserPofiles(string Name);
}
This readonly property IncomingUser will be used inside various methods defined under the class UserManager.
public class UserManager : IUserManager
{
public string IncomingUser { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public async Task<List<String>> GetUserPofiles(string Name)
{
......Business Logic for the method......
}
}
From the API controller, i am setting the DI like this :-
public class myAPIController : ApiController
{
IUserManager _Manager;
public myAPIController(IUserManager Mang)
{
_Manager = Mang;
}
}
Please suggest, how should i set the property IncomingUser from the API controller class with the help of autofac DI or another way.
Until recently I used AutoFac which had the method AsImplementedInterfaces()
which does
Register the type as providing all of its public interfaces as services (excluding IDisposable).
that means (for example a service) I have some base interface and an interface for every concerte service-class
See the simple code below:
public interface IService {}
public interface IMyService: IService
{
string Hello();
}
public class MyService: IMyService
{
public string Hello()
{
return "Hallo";
}
}
// just a dummy class to which IMyService should be injected
// (at least that's how I'd do it with AutoFac.
public class MyClass
{
public MyClass(IMyService myService) { }
}
Basically I want to inject my service's interface (so to speak) and not the concrete service.
Now I have to use StructureMap but I struggle to find what I need.
There is AddAllTypesOf<T> but this would register the concrete type.
is this even possible with StructureMap and if so how?
so, I found the answer(s)
1.
first you could use
public class TestRegistry : Registry
{
public TestRegistry()
{
Scan(x =>
{
x.TheCallingAssembly();
x.RegisterConcreteTypesAgainstTheFirstInterface();
});
}
}
this will register every concrete class against the first interface which might be too broad.
2.
if so you can use the following code I adapted from http://structuremap.github.io/registration/auto-registration-and-conventions/.
I had to change Each() to foreach because of compilation errors and made the whole class generic.
public class AllInterfacesConvention<T> : IRegistrationConvention
{
public void ScanTypes(TypeSet types, Registry registry)
{
// Only work on concrete types
foreach (var type in types.FindTypes(TypeClassification.Concretes | TypeClassification.Closed).Where(x => typeof(T).IsAssignableFrom(x)))
{
if(type == typeof(NotInheritedClass))
{
continue;
}
// Register against all the interfaces implemented
// by this concrete class
foreach (var #interface in type.GetInterfaces())
{
registry.For(#interface).Use(type);
}
}
}
}
if you take the code sample from the link every concrete type would be included. With my changes only concerte classes which inherit from T will be included.
in your registry you would use it like that
public class TestRegistry : Registry
{
public TestRegistry()
{
Scan(x =>
{
x.TheCallingAssembly();
x.Convention<AllInterfacesConvention<YOUR_BASE_INTERFACE>>();
});
}
}
Be aware the structuremap's GetInstance will always resolve concrete classes no matter if you previously registered them.
See here https://stackoverflow.com/a/4807950/885338
In startup.cs I have
services.AddAuthorization(options =>
{
options.AddPolicy("RequireSomething", policy => policy.Requirements.Add(new SomeRequirement()));
});
What if the SomeRequirement -class needs a class that is only available through dependency injection, like below. I can't/don't want to instantiate SomeRequirement.
public class SomeRequirement : AuthorizationHandler<SomeRequirement>, IAuthorizationRequirement
{
ISomething _something;
public SomeRequirement(ISomething something)
{
_something = something;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SomeRequirement requirement)
{
//TODO Do stuff
return Task.CompletedTask;
}
}
I suggest there are two solution for this.
I assume that you have concrete implementation of ISomething. If you knew that that initialize that and pass it to parameter as parameter. This is short and sweet solution.
Second solution is bit awkward.
Default DI Container available in ASP.net Core does not support property injection.
Instead of using Default DI Container try to use Autofac or some other DI Container that support property injection.
Update 1: Possible Solution. ( Using Default DI of ASP.net Core)
public class SomeRequirement : AuthorizationHandler<SomeRequirement>, IAuthorizationRequirement
{
ISomething _something;
public SomeRequirement(ISomething something)
{
_something = something;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SomeRequirement requirement)
{
//TODO Do stuff
return Task.CompletedTask;
}
}
public interface ISomething
{
}
public class Something : ISomething
{
}
And In ConfigureService
services.AddSingleton<ISomething, Something>();
services.AddScoped(typeof(SomeRequirement));
services.AddAuthorization(options =>
{
options.AddPolicy("RequireSomething", policy => policy.Requirements.Add(services.BuildServiceProvider().GetRequiredService<SomeRequirement>()));
});
We are using StructureMap as container in my current project and all has been working well until we had to inject dependencies to a custom attribute we are using.
First, code for registering stuff in the container would look like this...
Container = new Container();
Registry = new Registry();
ServiceLocator = new StructureMapServiceLocator(Container);
Registry.For<IServiceLocator>().Use(ServiceLocator);
Registry.For<IHitCounter>().Add<HitCounter>();
Registry.For<IHitCountAttribute>().Use<HitCountAttribute>();
Registry.SetAllProperties(policy => policy.OfType<IHitCounter>());
Container.Configure(config => config.AddRegistry(Registry));
The kind of registering above do work for a normal class with setter injection (and constructor injection) but obviously not for an attribute class.
An attribute class could look like this...
public class HitCountAttribute : ActionFilterAttribute, IHitCountAttribute
{
public IHitCounter HitCounter { get; set; }
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
HitCounter.Count(HttpContext.Current.Request.Url.ToString());
base.OnResultExecuted(filterContext);
}
}
The interface look like this...
public interface IHitCountAttribute
{
IHitCounter HitCounter { get; set; }
void OnResultExecuted(ResultExecutedContext filterContext);
}
Is it even possible to inject dependencies to an attribute? As it is instantiated by the runtime...
Is there any other way to solve the problem with the dependencies?