I would like to create an extension method to make it easy to register a specific dependency. But that dependency wants to use IMemoryCache. But it is possible that the application has already registered IMemoryCache, so in that case I would like to use that.
What's the best way to do use that optional dependency?
This is the class I want to register:
public class MyThing : IMyThing
{
public MyThing(IMemoryCache cache)
{
...
}
...
}
I can create a class to make it easy to register the class:
public static class MyThingRegistration
{
public static void AddMyThing(this IServiceCollection services)
{
services.AddScoped<IMyThing, MyThing>();
services.AddMemoryCache(); <--------- This might be an issue
}
}
This issue is that if the application has already done services.AddMemoryCache(); with specific options, my registration will override those, right?
What's the best way to check if IMemoryCache is already registered, and if not, then register it?
Or maybe and IMemoryCache instance can be given to the extension method?
This issue is that if the application has already done services.AddMemoryCache(); with specific options, my registration will override those, right?
No it wont.
/// <summary>
/// Adds a non distributed in memory implementation of <see cref="IMemoryCache"/> to the
/// <see cref="IServiceCollection" />.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection" /> to add services to.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
public static IServiceCollection AddMemoryCache(this IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
services.AddOptions();
services.TryAdd(ServiceDescriptor.Singleton<IMemoryCache, MemoryCache>());
return services;
}
Source code
Because of the TryAdd, if it is already registered/added it wont add it again
Adds the specified descriptor to the collection if the service type hasn't already been registered.
Related
I'm trying to integrate Dependency Injection with Unity.MVC to my project. With the default configuration that Unity has, controllers works perfectly fine but I would like to implement DI en other classes...
This is the configuration I use:
I added this line on global.asax UnityMvcActivator.Start();
public static class UnityMvcActivator
{
/// <summary>
/// Integrates Unity when the application starts.
/// </summary>
public static void Start()
{
FilterProviders.Providers.Remove(FilterProviders.Providers.OfType<FilterAttributeFilterProvider>().First());
FilterProviders.Providers.Add(new UnityFilterAttributeFilterProvider(UnityConfig.Container));
DependencyResolver.SetResolver(new UnityDependencyResolver(UnityConfig.Container));
// TODO: Uncomment if you want to use PerRequestLifetimeManager
// Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
}
/// <summary>
/// Disposes the Unity container when the application is shut down.
/// </summary>
public static void Shutdown()
{
UnityConfig.Container.Dispose();
}
}
The last one comes by default and works perfectly with the controllers
public static class UnityConfig
{
#region Unity Container
private static Lazy<IUnityContainer> container =
new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterTypes(container);
return container;
});
/// <summary>
/// Configured Unity Container.
/// </summary>
public static IUnityContainer Container => container.Value;
#endregion
/// <summary>
/// Registers the type mappings with the Unity container.
/// </summary>
/// <param name="container">The unity container to configure.</param>
/// <remarks>
/// There is no need to register concrete types such as controllers or
/// API controllers (unless you want to change the defaults), as Unity
/// allows resolving a concrete type even if it was not previously
/// registered.
/// </remarks>
public static void RegisterTypes(IUnityContainer container)
{
// NOTE: To load from web.config uncomment the line below.
// Make sure to add a Unity.Configuration to the using statements.
// container.LoadConfiguration();
// TODO: Register your type's mappings here.
// container.RegisterType<IProductRepository, ProductRepository>();
if (Global.BOT_CONECTADO)
{
container.RegisterSingleton<ISeguidorRepository, DBSeguidorRepository>();
container.RegisterSingleton<IActivoRepository, DBActivoRepository>();
container.RegisterSingleton<ICategoriaRepository, DBCategoriaRepository>();
container.RegisterSingleton<IJuegoSecundarioRepository, DBJuegoSecundarioRepository>();
container.RegisterSingleton<ISaludosRepository, DBSaludosRepository>();
}
else
{
container.RegisterSingleton<ISeguidorRepository, FakeSeguidorRepository>();
container.RegisterSingleton<IActivoRepository, FakeActivoRepository>();
container.RegisterSingleton<ICategoriaRepository, FakeCategoriaRepository>();
container.RegisterSingleton<IJuegoSecundarioRepository, FakeJuegoSecundarioRepository>();
container.RegisterSingleton<ISaludosRepository, FakeSaludosRepository>();
}
//DependencyResolver.SetResolver(new IDConfigurador(container));
}
}
In the last one I added my registers, but When I try to use them on other contructors, those that are not controllers, they don't work. Looking for answers on google I found this solution that could help me:
container.Resolve()
I tried to use it but I get the following error ==> "The method 'IUnityContainer.Resolve(Type, string, params ResolverOverride[])' non generic cannot be used with arguments of type"
// CONSTRUCTOR
public RepositorioEncuesta()
{
// Error línea de abajo: El método 'IUnityContainer.Resolve(Type, string, params ResolverOverride[])' no
// genérico no se puede usar con argumentos de tipo
_activos = UnityConfig.Container.Resolve<IActivoRepository>();
_seguidores = new DBSeguidorRepository();
_juegos = new DBJuegoSecundarioRepository();
}
How could I use my DI integration to use it on other constructors? Why the Resolve method is not working?
PD: I also tried using a generic, but does not work either.
var activos = UnityConfig.Container.Resolve<IActivoRepository>();
I have been reading the official Session and application state documentation and have stumbled upon the following paragraph:
Loading Session asynchronously
The default session provider in ASP.NET Core loads the session record
from the underlying IDistributedCache store asynchronously only if the
ISession.LoadAsync method is explicitly called before the TryGetValue,
Set, or Remove methods. If LoadAsync is not called first, the
underlying session record is loaded synchronously, which could
potentially impact the ability of the app to scale.
To have applications enforce this pattern, wrap the
DistributedSessionStore and DistributedSession implementations with
versions that throw an exception if the LoadAsync method is not called
before TryGetValue, Set, or Remove. Register the wrapped versions in
the services container.
The wrapping itself is not an issue for me, but in order to implement it, I need:
Reference to the original implementation
Registering the wrapped version
Currently, I have created the following wrapper class:
public class WrappedDistributedSession : ISession
{
private DistributedSession _service;
private bool loaded = false;
public WrappedDistributedSession(DistributedSession service)
{
_service = service;
}
public bool IsAvailable => _service.IsAvailable;
public string Id => _service.Id;
public IEnumerable<string> Keys => _service.Keys;
public void Clear() => _service.Clear();
public Task CommitAsync() => _service.CommitAsync();
public Task LoadAsync()
{
loaded = true;
return _service.LoadAsync();
}
public void Remove(string key)
{
if(loaded)
{
_service.Remove(key);
} else
{
throw new Exception();
}
}
public void Set(string key, byte[] value)
{
if (loaded)
{
_service.Set(key, value);
}
else
{
throw new Exception();
}
}
public bool TryGetValue(string key, out byte[] value)
{
if (loaded)
{
return _service.TryGetValue(key, out value);
}
else
{
throw new Exception();
}
}
}
And I have registered it in the Startup.ConfigureServices
services.AddScoped<ISession, WrappedDistributedSession>();
Obviously, since I am writing this question, my solution does not work. Where did I go wrong and how does one "Register the wrapped versions in the services container"?
Use at your risk. This seems to work in Configure method just after sessions.
This solution is an adaptation from this unit test:
https://github.com/dotnet/aspnetcore/blob/cd0eab88eaa230fa276c27ab5dc71ea267efe14f/src/Middleware/Session/test/SessionTests.cs#L654-L656
app.UseSession();
app.Use(async (context, next) =>
{
await context.Session.LoadAsync();
await next();
});
Or as a more qualified wrapper extension:
public static class SesssionAsyncExtensions
{
/// <summary>
/// Have sessions be asyncronous. This adaptation is needed to force the session provider to use async calls instead of syncronous ones for session.
/// Someone surprisingly for something that seems common, Microsoft didn't make this aspect super nice.
/// </summary>
/// <param name="app">App builder instance.</param>
/// <returns>App builder instance for chaining.</returns>
/// <remarks>
/// From Microsoft Documentation (https://learn.microsoft.com/en-us/aspnet/core/fundamentals/app-state?view=aspnetcore-5.0):
/// The default session provider in ASP.NET Core will only load the session record from the underlying IDistributedCache store asynchronously if the
/// ISession.LoadAsync method is explicitly called before calling the TryGetValue, Set or Remove methods.
/// Failure to call LoadAsync first will result in the underlying session record being loaded synchronously,
/// which could potentially impact the ability of an application to scale.
///
/// See also:
/// https://github.com/dotnet/aspnetcore/blob/d2a0cbc093e1e7bb3e38b55cd6043e4e2a0a2e9a/src/Middleware/Session/src/DistributedSession.cs#L268
/// https://github.com/dotnet/AspNetCore.Docs/issues/1840#issuecomment-454182594
/// https://bartwullems.blogspot.com/2019/12/aspnet-core-load-session-state.html
/// </remarks>
public static IApplicationBuilder UseAsyncSession(this IApplicationBuilder app)
{
app.UseSession();
app.Use(async (context, next) =>
{
await context.Session.LoadAsync();
await next();
});
return app;
}
}
It seems you need to implement ISessonStore too (which is actually mentioned in the documentation you quoted), as it's the only one registered in AddSession extension method.
public static IServiceCollection AddSession(this IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
services.AddTransient<ISessionStore, DistributedSessionStore>();
services.AddDataProtection();
return services;
}
ISessionStore (and hence DistributedSessionStore) has a Create (see source) method which returns ISession. Here you need to return your custom implementation.
https://github.com/aspnet/Session/blob/rel/1.1.0/src/Microsoft.AspNetCore.Session/SessionServiceCollectionExtensions.cs#L27-L29
Then you can add before AddSession with
services.AddTransient<ISessionStore, AsyncDistributedSessionStore>();
I have 7 services running under Service Fabric. I decided to create a generic OwinCommunicationsListener class since the code can be very generic.
I noticed that the template for Service Fabric sets up the startup class (which configures the pipeline) as a static class and the stateless service class passes it as an action
internal sealed class WebService : StatelessService
{
public WebService(StatelessServiceContext context)
: base(context)
{ }
/// <summary>
/// Optional override to create listeners (like tcp, http) for this service instance.
/// </summary>
/// <returns>The collection of listeners.</returns>
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new ServiceInstanceListener[]
{
new ServiceInstanceListener(serviceContext => new OwinCommunicationListener(Startup.ConfigureApp, serviceContext, ServiceEventSource.Current, "ServiceEndpoint"))
};
}
}
If I need to do DI, I need to pass those objects to the startup class. From the template, the only way I can see to do that is to either set those objects up in OwinCommunicationsListener or pass parameters to OwinCommunicationsListener - either of which will mean OwinCommunicationsListener is less generic.
I noticed in the WordCount example, they went with a normal startup class and passed a reference to it over to OwinCommunicationsListenter. This way the ServiceClass can pass some objects to Startup which it can use for DI and OwinCommunicationsListener can remain generic.
public class WordCountWebService : StatelessService
{
public WordCountWebService(StatelessServiceContext context)
: base(context)
{
}
/// <summary>
/// Creates a listener for Web API with websockets.
/// </summary>
/// <returns>The OWIN communication listener.</returns>
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new[]
{
new ServiceInstanceListener(initParams => new OwinCommunicationListener("wordcount", new Startup(MyDIObject), initParams))
};
}
}
If I think of the stateless service class as the brains and the OwinCommunicationsListener class as a generic helper shared by several services, it seems I should go the same route as the wordcount example and have a non-static startup class. Are there any downsides to this approach? I wondered why the templates would not use that approach when the idea of microservices is that we will have a lot of them and generic scaffolding like this can improve maintenance and reliability.
The wordcount example is what I would recommend. No downsides that I know of.
Have you looked at ASP.NET Core? It gets even easier with IWebHost with its built-in DI. Here is an example: https://github.com/vturecek/service-fabric-xray/blob/master/src/xray.Data/DataService.cs
Given the following registration code...
ILogger takes a single string parameter as a constructor argument.
IJob implementations all take an ILogger as a constructor argument.
// register the 'default' logger.
builder.RegisterType<Logger>().As<ILogger>()
.WithParameter(new NamedParameter("category", "default"));
Lots of registrations clipped out for brevity...
// register the 'job' logger. (this is a problem, a duplicate...)
builder.RegisterType<Logger>().As<ILogger>()
.WithParameter(new NamedParameter("category", "job"));
// register some classes that need a different logger parameter.
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.Where(_ => typeof(IJob).IsAssignableFrom(_))
// how to do this...
.WithLoggerWithCategoryJob();
How do I use the category=default logger for all my class registrations except the IJob ones.
I would like to use the category=job logger for all those.
This solution demonstrates how to provide a default parameter for the logger, and to override the logging parameter just for IRepository<> implementations.
The code can quite easily be enhanced to provide a set of overrides depending on the implemented interfaces.
/// <summary>
/// The logging module.
/// </summary>
public class LoggingModule : Module
{
/// <summary>
/// Override to add registrations to the container.
/// </summary>
/// <remarks>
/// Note that the ContainerBuilder parameter is unique to this module.
/// </remarks>
/// <param name="builder">The builder through which components can be registered.</param>
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<NewLogger>()
.As<ILogger>()
.WithParameter(new NamedParameter("category", "Default"));
base.Load(builder);
}
/// <summary>
/// Override to attach module-specific functionality to a component registration.
/// </summary>
/// <remarks>
/// This method will be called for all existing <i>and future</i> component
/// registrations - ordering is not important.
/// </remarks>
/// <param name="componentRegistry">The component registry.</param><param name="registration">The registration to attach functionality to.</param>
protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration)
{
registration.Preparing += (sender, args) =>
{
var type = args.Component.Activator.LimitType;
if (type.GetInterfaces().All(_ => !_.IsGenericType || _.GetGenericTypeDefinition() != typeof(IRepository<>)))
return;
var pm = new ResolvedParameter(
(p, c) => p.ParameterType == typeof(ILogger),
(p, c) => c.Resolve<ILogger>(new NamedParameter("category", "RepositoryLogger")));
args.Parameters = args.Parameters.Union(new[] { pm });
};
base.AttachToComponentRegistration(componentRegistry, registration);
}
}
I have just installed the mvc4 rc update and I am trying to build an api application with little luck.
I am using ninject but cant get my controllers to load. I keep getting an error
Type 'Api.Controllers.ConsumerController' does not have a default constructor
I am very new to mvc and using injection so please bear with me.
I havent done anything special to the default binding that is created via nuget
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
return kernel;
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IConsumerRepository>().To<ConsumerRepository>();
}
}
My controller looks like
private readonly IConsumerRepository _repository;
public ConsumerController(IConsumerRepository repository)
{
_repository = repository;
}
[HttpGet]
public IQueryable<Consumer> Get(Guid id)
{
return _repository.Get(id).AsQueryable();
}
What do I need to do to get the api controllers to work with ninject?
Sorry if this is simple stuff
I tried your suggestion Michael however after changing the the webcommon.cs to this
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
return kernel;
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IConsumerRepository>().To<ConsumerRepository>();
}
I get an error when
var kernel = new StandardKernel();
is called
Method 'GetFilters' in type 'Ninject.Web.WebApi.Filter.DefaultFilterProvider' from assembly 'Ninject.Web.WebApi, Version=3.0.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7' does not have an implementation.
What am I missing?
I asked Brad Wilson about this and it has changed in MVC4 RC.
GlobalConfiguration.Configuration.ServiceResolver has been moved to GlobalConfiguration.Configuration.DependencyResolver
Use this implementation to create a Ninject DependencyResolver for your Web Api:
https://gist.github.com/2417226
In NinjectWebCommon.cs:
// Register Dependencies
RegisterServices(kernel);
// Set Web API Resolver
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
This generic error message
Type 'Api.Controllers.ConsumerController' does not have a default constructor
can also occur if you do not make your constructor public, or the dependency cannot be resolved by the IoC container maybe because of a missing argument.
The error message is misleading to say the least.
You can install the NuGet package WebApiContrib.IoC.Ninject and add the following line of code to NinjectWebCommon.cs
GlobalConfiguration.Configuration.DependencyResolver = new NinjectResolver(kernel);
For someone landing here while searching '...does not have a default constructor' looking for an easy way to debug silently failing configurations:
Remove constructor-injected objects until the constructor can be invoked
For each of the previously injected, now uninitialized objects, invoke using:
ServiceLocator.Current.GetInstance<[OBJ-TYPE]>()
The offending object will cause an ActivationException with a descriptive Message. You'll have something to go for.
Remember to remove call to ServiceLocator post-fix. Ie. this is not a recommendation to use the service locator anti pattern outside debugging.
have you registered the container with the frawework? I prefer using autofac, here is an example of how to use autofac with API. http://alexmg.com/post/2012/03/08/Autofac-ASPNET-Web-API-%28Beta%29-Integration.aspx
Also, Mark Seeman has a good post on DI in general with WebAPI
http://blog.ploeh.dk/2012/03/20/RobustDIWithTheASPNETWebAPI.aspx
From Ploeh:
GlobalConfiguration.Configuration.ServiceResolver.SetResolver(
t => this.container.Kernel.HasComponent(t) ?
this.container.Resolve(t) :
null,
t => this.container.ResolveAll(t).Cast<object>());
The above has to be performed in the global.asax
Hopefully this helps someone else...
I was having the same issue and it was related to me moving class responsible for registering assembly in charge of initializing controllers. Moved out of web into framework project.
Using Autofac but same would apply for other containers.
Was calling:
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
Which works fine when it's within web application, but threw above exception when moved to framework project as the executing assembly no longer contains the controllers.
Instead had to update to:
builder.RegisterApiControllers(Assembly.GetCallingAssembly());
It seems Ninject didn't throw an exception as it generally does when your IOC dependencies aren't quite set up right. Instead it made it look like I hadn't registered the WebAPI dependency resolver which I certainly did. Here was my solution to this problem but from what I've found it could be MANY DIFFERENT types of setup issues. Just re-check everything in the dependency chain. Hopefully it helps someone!
The controller:
public class ContestsController : ApiController
{
//Ninject wouldn't inject this CTOR argument resulting in the error
public ContestsController(IContestEntryService contestEntryService)
{
The dependency:
public class ContestEntryService : IContestEntryService
{
public ContestEntryService(IContestsContext contestsContext)
{
The incorrect configuration:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IContestsContext>()
.To<ContestsContext>()
.InRequestScope();
kernel.Bind(x =>
x.FromAssembliesMatching("MyNameSpace.*")
.SelectAllClasses()
.BindAllInterfaces()
);
The correct configuration:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind(x =>
x.FromAssembliesMatching("MyNameSpace.*")
.SelectAllClasses()
.BindAllInterfaces()
);
kernel.ReBind<IContestsContext>()
.To<ContestsContext>()
.InRequestScope();
Generally Ninject is pretty good about reporting these sorts of errors so I really got thrown for a loop on this one!
I know its an old post, but i found the solution at some link so sharing here. Hope it helps.
http://weblogs.asp.net/hajan/archive/2013/03/16/quick-tip-how-to-make-ninject-work-with-asp-net-web-api.aspx?CommentPosted=true#commentmessage
if any anyone is still having problems, please listen for some reason ninject is not working how we would expect with mvc 4. In your web api, you need to write this code
public DefaultController() : base() { }
This removes the error saying about no default constructor, then when you need to get your data from the get method write this code:
public IEnumerable<YourModelGoesHere> Get()
{
return context.YourData;
}
Keep in mind, you will have to access your db class here as well, for instance:
DefaultConnection context = new DefaultConnection();
just install Ninject.MvcXXX package, where XXX - version of MVC...
I had this error message, too, but it just turned out that one of my interfaces weren't actually implemented by any classes (I forgot to add it to the class declaration).