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
Related
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>()));
});
In broader terms what I am trying to achieve with Autofac is to pass the dependant (a.k.a. parent) object to its dependencies.
For example:
interface IDependency {}
class Dependant
{
IDependency Dependency { get; set; }
}
class ConcreteDependency : IDependency
{
ConcreteDependency(Dependant dependant) { /* ... */ }
}
I am hoping this could work, because Dependant breaks the dependency loop using property injection (meaning you can create an instance of Dependant, before having to resolve IDependency). Whilst, if both classes used ctor-injection this wouldn't be possible.
Specifically, I am trying to inject the current ASP.NET MVC controller instance to one of its dependencies.
Take a look at:
public abstract class ApplicationController : Controller
{
public ILogger Logger { get; set;}
}
public class SomeController : ApplicationController
{
[HttpPost]
public ActionResult Create(FormCollection formData)
{
// something fails...
this.Logger.Log("Something has failed.");
}
}
public interface ILogger
{
public void Log(string message);
}
public class TempDataLogger : ILogger
{
private ControllerBase controller;
public NullLogger(ControllerBase controller)
{
this.controller = controller;
}
public void Log(string message)
{
this.controller.TempData["Log"] = message;
}
}
In plain English the above code uses TempData as a way of "logging" messages (maybe to print it out in a nice way in view-layout or something...).
Simple enough all controllers are registered in Autofac:
builder.RegisterControllers(typeof(MvcApplication).Assembly)
.PropertiesAutowired(); // not strictly necessary
But then, how can I tweak the ILogger registration below to make it work?
builder.RegisterType<TempDataLogger>()
.As<ILogger>()
.InstancePerRequest();
Is this even possible in Autofac?
Thank you.
In case anyone else is interested, the solution below is the closest I was able to get so far:
builder.RegisterControllers(typeof(MvcApplication).Assembly)
.PropertiesAutowired() // not strictly necessary
.OnActivating(e => ((ApplicationController)e.Instance).Logger = new TempDataLogger((ApplicationController)e.Instance));
... and therefore, no need to;
builder.RegisterType<TempDataLogger>()
.As<ILogger>()
.InstancePerRequest();
I have an interface class:
public interface IStartUpTask
{
bool IsEnabled { get; }
void Configure();
}
I have multimple classes implementing the same interface
One of the classes looks like this:
public class Log4NetStartUpTask : IStartUpTask
{
public bool IsEnabled { get { return true; } }
public void Configure()
{
string log4netConfigFilePath = ConfigurationManager.AppSettings["log4netConfigFilePath"];
if (log4netConfigFilePath == null)
throw new Exception("log4netConfigFilePath configuration is missing");
if (File.Exists(log4netConfigFilePath) == false)
throw new Exception("Log4Net configuration file was not found");
log4net.Config.XmlConfigurator.Configure(
new System.IO.FileInfo(log4netConfigFilePath));
}
}
How can i tell Ninject that i want all the classes implementing the IStartUpTask to bind to themself automatically?
I found an example using StructureMap which does this, but i don't know how to do it in Ninject.
Scan(x => {
x.AssemblyContainingType<IStartUpTask>();
x.AddAllTypesOf<IStartUpTask>();
x.WithDefaultConventions();
});
How can i tell Ninject that i want all the classes implementing the
IStartUpTask to bind to themself automatically?
First of all, let me tell you that Ninject binds all classes to themselves automatically. You do not need to do anything special for that.
Having said that, I understand that you might want the explicit binding if you want to change scope or attach names or metadata. In this case read-on.
I do not know if it is possible to do what you are after in vanilla ninject, but you can use ninject.extensions.conventions. Using this library you can write:
Kernel.Bind(x =>
x.FromThisAssembly()
.SelectAllClasses()
.InheritedFrom<IStartUpTask>()
.BindToSelf());
you can call it explicit in your code:
...
Bind<IStartUpTask>().To<Log4NetStartUpTask>();
Bind<IStartUpTask>().To<SomeOtherStartUpTask>();
...
Use it in SomeClass
public class SomeClass
{
private readonly List<IStartUpTask> startUpTaskList;
public SomeClass(IEnumerable<IStartUpTask> startUpTaskList)
{
this.startUpTaskList = startUpTaskList;
}
foreach (var startUpTask in this.startUpTaskList)
{
...
}
}
I have this interface:
public interface IUserProfileService
{
// stuff
}
Implemented by:
public class UserProfileService : IUserProfileService
{
private readonly string m_userName;
public UserProfileService(string userName)
{
m_userName = userName;
}
}
I need this injected into a controller like this:
public class ProfilesController : BaseController
{
private readonly IUserProfileService m_profileService;
public ProfilesController(IUserProfileService profileService)
{
m_profileService = profileService;
}
}
I don't know how I can register this interface and its implementation into Ninject container so that userName param is passed in when the Ninject inits an instance of this service.
Any ideas how I can achieve this?
The technical ninject answer is to use constructor arguments like so:
Bind<IUserProfileService>().To<UserProfileService>().WithConstructorArgument("userName", "karl");
Of course you need to figure out where "karl" comes from. It really depends on your app. Maybe its a web app and it's on the HttpContex? I don't know. If it gets rather complicated then you might want to write a IProvider rather than doing a regular binding.
One alternative is to inject a factory and create your dependency using Create(string userName).
public class UserProfileServiceFactory
{
public IUserProfileService Create(string userName)
{
return new UserProfileService(userName);
}
}
It might seem off to have to create another class but the benefits mostly comes when UserProfileService takes in additional dependencies.
The trick is to not inject the username in that class. You call this class a service so it would probably work transparantly with multiple users. I see two solutions:
Inject an abstraction into the service that represents the current user:
public class UserProfileService : IUserProfileService
{
private readonly IPrincipal currentUser;
public UserProfileService(IPrincipal currentUser)
{
this.currentUser = currentUser;
}
void IUserProfileService.SomeOperation()
{
var user = this.currentUser;
// Do some nice stuff with user
}
}
Create an implementation that is specific to the technology you are working with, for instance:
public class AspNetUserProfileService : IUserProfileService
{
public AspNetUserProfileService()
{
}
void IUserProfileService.SomeOperation()
{
var user = this.CurrentUser;
// Do some nice stuff with user
}
private IPrincipal CurrentUser
{
get { return HttpContext.Current.User; }
}
}
If you can, go with option one.
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();
}