I'm pretty new to ASP.Net Core, C#, OOP, Javascript … basically to everything I'm using at the moment. Last couple of months I've been reading and studying in order to start a new development project.
All in all I'm progressing steadily but I've bumped into an issue with IMemoryCache that I can't really get my head around (or is it with DI?).
I'm using ASP.Net Core 2.0 and VS2017. I have a solution with 2 projects, my main ASP.Net Core MVC Web app and a .Net Core 2.0 Class Library.
In the Class Library I added a class in which I want to use caching so I added IMemoryCache to the constructor.
To instantiate the class I have another constructor that calls the function GetModelGuidAsync.
public class MdsEntityCRUD
{
#region ClassConstruct
private readonly IMemoryCache _cache;
public MdsEntityCRUD(IMemoryCache cache)
{
_cache = cache;
}
public MdsEntityCRUD(ServiceClient client, string modelName, string entityName, string versionFlag)
{
this.client = client;
this.ModelId = Task.Run(async () => await GetModelGuidAsync(modelName)).Result;
this.EntityName = entityName;
mdsWS.Version ver = Task.Run(async () => await GetVersionByPolicyAsync(client, VersionPolicy.Flag, versionFlag, this.ModelId)).Result;
this.VersionId = ver.Identifier.Id;
}
public async Task<Guid> GetModelGuidAsync(string modelName)
{
Dictionary<string, Guid> modelNames;
if (!_cache.TryGetValue("ModelNames", out modelNames) || !modelNames.ContainsKey(modelName))
{
modelNames = await GetModelNamesAsync();
_cache.Set("ModelNames", modelNames, new MemoryCacheEntryOptions() { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(60) }); // CFG
}
return modelNames[modelName];
}
I've added services.AddMemoryCache(); to ConfigureServices in Startup of my MVC project.
I'm calling the class from a Controller in my MVC project using the second constructor.
At runtime I get an error at the if (!_cache.TryGetValue-statement. These are the details shown in Exception Helper and from the exception window:
System.AggregateException HResult=0x80131500 Message=One or more
errors occurred. (Object reference not set to an instance of an
object.) Source=System.Private.CoreLib StackTrace: at
System.Threading.Tasks.Task.ThrowIfExceptional(Boolean
includeTaskCanceledExceptions) at
System.Threading.Tasks.Task`1.GetResultCore(Boolean
waitCompletionNotification) at
mdXmdsWS.MdsEntityCRUD..ctor(ServiceClient client, String modelName,
String entityName) in C:\Users..\MdsEntityCRUD.cs:line 59 at
MDS_MVC_Proto2.Controllers.DataExplorerController.EntityDataSource(DataManagerRequest
dmr) in C:\Users..\Controllers\DataExplorerController.cs:line 165
at Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object
target, Object[] parameters) at
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__12.MoveNext()
Inner Exception 1: NullReferenceException: Object reference not set to
an instance of an object.
and from ($exception).InnerException
InnerException {System.NullReferenceException: Object reference not set to an instance of an object. at
Microsoft.Extensions.Caching.Memory.CacheExtensions.TryGetValue[TItem](IMemoryCache
cache, Object key, TItem& value) at
mdXmdsWS.MdsEntityCRUD.d__18.MoveNext() in
C:\Users..\MdsEntityCRUD.cs:line 84
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task) at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at
mdXmdsWS.MdsEntityCRUD.<>c__DisplayClass16_0.<<-ctor>b__0>d.MoveNext()
in C:\Users..\MdsEntityCRUD.cs:line 59} System.Exception
{System.NullReferenceException}
I don't know why I'm getting the error:
Is it because the cache is in the Class library and not in the MVC project?
Is it because I'm trying to use the cache in a function executed from the constructor?
Am I missing some configuration?
Eager to learn where I'm going wrong ...
As pointed in the comments, Dependency Injection (DI)/Inversion of Control (IoC) is no magic. At its very base, Dependency Injection just means: "Pass instance of object A to the constructor/method instead of new-ing it inside the constructor/method".
var service = new MyService(new MyDependency());
This above is already dependency injection, you inject a dependency to MyService and if its accepts a base class/interface, its implementation can be changed without changing MyService. This approach is commonly called "poor mans DI", without any external tools or libraries.
Then there are DI/IoC frameworks, which make that easier, so you don't have to new the instances yourself and inject it into the services as well as manage the objects live times for you (should every class get the same instance? (Singleton Lifetime) Or every time a new instance? (Transient Lifetime) Should specific group of classes get one instance and other group another ones? (Scoped Lifetime)).
When you use DI/IoC with or without a framework, you have to use it all the way down. There is no magic involved. The IoC frameworks do the same as above, new-ing a class and its dependencies and pass to the their constructors.
In case of ASP.NET Core the controller is resolved via the built-in DI/IoC Framework, so you can inject any registered class (inside Startup's ConfigureServices method).
In your case you obviously are new-ing the class with the second constructor, so the first one is never called and never setting the _cache member variable. When you try to access it, you get the dreaded NullReferenceException exception. You have to add the dependency to the main constructor.
If like in your case you like have to new a class, you have to pass the dependencies in yourself. In your case, you need to inject IMemoryCache into your controller and then pass it to your new-ed class.
IMemoryCache cache
public class MyController : Controller
{
private readonly MdsEntityCRUD mdsCrud;
public MyController(IMemoryCache memoryCache)
{
ServiceClient client = ...;
mdsCrud = new MdsEntityCRUD(memoryCache, client, "modelname", "entityName", "v1");
}
}
This way you get the correct IMemoryCache instance from the Controller which you can pass to the services.
There are other, more elegant ways to abstract it (using factory classes or factory methods within ConfigureServices) to perform that, but its out of scope of the question.
Related
So iv just been thrown into some new code at work and its in C#. Now I'm not very familiar with C# and there are some things I really don't understand and the main one is Injecting into methods.
Now there is a WebAPI and it has controller that uses a class named "LocalFileStorage" which is a dependency from another project, the constructor for that class looks like this.
public class LocalFileStorageHandler : IStorageHandler
{
*Bunch of private variables
public LocalFileStorageHandler(DbContext dbContext, IConfiguration configuration, ILogger<LocalFileStorageHandler> logger)
{ code here}
Now in the controller class every method that uses the LocalFileStorage gets it injected as a parameter. Here is a example:
public async Task<IActionResult> ApiMapper([FromBody] dynamic request, [FromServices] IStorageHandler storageHandler)
Also in the project startup.cs we can find this line:
services.AddScoped<IStorageHandler, LocalFileStorageHandler>();
Now my understanding is that for each separate request made the Addscoped makes sure that the method gets its own instance of LocalFileStorage handler. I also understand that the "[FromServices]" attribute causes this instance to be injected into the method. However the one thing I don't understand and cant find anywhere in the code is where the LocalFileStorage objects get their "In parameters" for their constructor?
As by my understanding each injected instance of LocalFileStorage should also receive the parameters:
DbContext dbContext, IConfiguration configuration, ILogger<LocalFileStorageHandler> logger
What am i missing here ?
Kind regards
The DI container injects the dependencies for you. So somewhere, the DbContext, IConfiguration and ILogger has been registered/setup.
Then when you use the FromServices attribute, the DI container will try to resolve the type for you and inject all dependencies (if they are registered, if not, an exception will be thrown)
IConfiguration and ILogger are usually setup when building the host. DbContext are (usually) registered by using the AddDbContext extension method.
Link to configuration for ILogger: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-6.0#logging-providers
Link to configuration for IConfiguration: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-6.0#default-configuration
Link to Dependency Injection fundamentals in ASP.Net Core : https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-6.0
I am using AutoFac in my Web API application (using the latest versions available as of time this question was posted). One of my service dependencies is an AuditService which uses an instance of by DbContext type (let's call it MyDbContext for now). Most of my services and the MyDbContext type are all registered using InstancePerRequest. For my AuditService I want to make an exception, I always want to inject an owned (new) instance of my MyDbContext.
Question: Using AutoFac registrations, how do I register my AuditService in such a way that it always gets an owned (new) instance of MyDbContext?
What could work:
I could hard code the creation of MyDbContext in the constructor of AuditService thus circumventing AutoFac all together.
I could use PropertyInjection or MethodInjection and provide a new instance of MyDbContext in the Life Time event OnActivating
I could define a second interface on MyDbContext and provide a second registration and use InstancePerOwned.
Do I have to pick one of these above options (if so I would lean towards 3) or am I missing something simple? Is there a way to define what I want in my registration code?
// registration called in web api startup code
public void RegisterAutofac(ContainerBuilder builder)
{
builder.RegisterType<MyDbContext>()
.As<IMyDbContext>()
.InstancePerRequest();
builder.RegisterType<BusinessService>()
.As<IBusinessService>()
.InstancePerRequest();
builder.RegisterType<AuditService>()
.As<IAuditService>()
.InstancePerRequest();
}
public class AuditService
{
// expects an isolated instance on this request
private readonly IMyDbContext _dbContext;
public AuditService(IMyDbContext dbContext)
{
_dbContext = dbContext;
}
}
public class BusinessService
{
// expect a shared IMyDbContext instance across the request
private readonly IMyDbContext _dbContext;
public BusinessService(IMyDbContext dbContext)
{
_dbContext = dbContext;
}
}
Solution Attempts with InstancePerOwned
This causes an exception
builder.RegisterType<MyDbContext>()
.As<IMyDbContext>()
.InstancePerRequest()
.InstancePerOwned<AuditService>();
Autofac.Core.DependencyResolutionException: "No scope with a tag matching 'AuditService' is visible from the scope in which the instance was requested. If you see this during execution of a web application, it generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario). Under the web integration always request dependencies from the dependency resolver or the request lifetime scope, never from the container itself.
at Autofac.Core.Lifetime.MatchingScopeLifetime.FindScope(ISharingLifetimeScope mostNestedVisibleScope)
at Autofac.Core.Resolving.InstanceLookup..ctor(IComponentRegistration registration, IResolveOperation context, ISharingLifetimeScope mostNestedVisibleScope, IEnumerable`1 parameter
I tried reversing the order of InstancePerOwned and InstancePerRequest calls but this seems to have no effect, the same MyDbContext instance is reused for both BusinessService and AuditService instances in the same request. This was tested with object.ReferenceEquals from in an ApiController and passed in both instance's _dbContext fields.
builder.RegisterType<MyDbContext>()
.As<IMyDbContext>()
.InstancePerOwned<AuditService>()
.InstancePerRequest();
Try switching from InstancePerRequest to InstancePerLifetimeScope. In most apps this generally behaves the same and is the way to share registrations across apps that both do and don't have per-request semantics anyway. (Which is to say, this is pretty common.)
Once you have InstancePerLifetimeScope on your context object, you can use Owned<T> in your AuditService constructor to get a fresh copy.
So...
builder.RegisterType<MyDbContext>()
.As<IMyDbContext>()
.InstancePerLifetimeScope();
then...
public AuditService(Owned<IMyDbContext> dbContext)
Note your AuditService will be responsible for disposing of the dbContext when it's done, so you'll have to handle that manually (that's part of using Owned<T>). But if you've already got some one-off stuff going on, that shouldn't be a big deal.
I use repository method to get all data from DB.
Here is the code of it:
public class ExperienceRepository
{
private readonly ToSeeDatabaseContext _context;
public ExperienceRepository(ToSeeDatabaseContext context)
{
_context = context;
}
public List<Experience> GetAllExperiences()
{
return _context.Experience.ToList();
}
}
I need to call GetAllExperience from controller.
So at first I need to declare repo as private property
I do it like this
private ExperienceRepository _exprepo = new ExperienceRepository();
But it says, it need
Severity Code Description Project File Line Suppression State
Error CS7036 There is no argument given that corresponds to the required formal parameter 'context' of 'ExperienceRepository.ExperienceRepository(ToSeeDatabaseContext)' TooSeeWeb C:\Users\EugeneSukhomlyn\source\Workspaces\TooSee\Too See Web\Too See Web\Too See Web\Controllers\ExperienceController.cs 14 Active
How I can solve it?
Since you are using dependency injection, the preferred way would be to inject the DB context to the repository.
You probably have already code similar to this in the ConfigureServices method of your Startup.cs file (or in the place where you configure your service collection if you are not using ASP.NET Core) to set up the context for dependency injection (if you don't you should add it):
services.AddDbContext<ToSeeDatabaseContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("ToSeeDatabaseContext")));
Since your experience repository already accepts ToSeeDatabaseContext, it is already ready for dependency injection. Now you have to inform the DI framework about ExperienceRepository, so that it can inject it to its consumers. So in ConfigureServices you add:
services.AddTransient<ExperienceRepository, ExperienceRepository>();
Now can use dependency injection whenever you want to need the repository. In your consumer (eg. an ASP.NET page) you can use constructor injection to get a repository:
class MyExperienceConsumer {
private ExperienceRepository _exprepo;
public MyExperienceConsumer(ExperienceRepository exprepo) {
_exprepo = exprepo;
}
}
If your consumer is an ASP.NET page controller, this is all you need to do, since the MVC framework will create the controller for you and use DI to give you the repository. If you need to instantiate the consumer yourself you need to do so with the help a service provider from the DI framework, so that it can do its magic (assuming you have a service collection). When you use ActivatorUtilities, the DI framework will inject the repository into the constructor:
IServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
MyExperienceConsumer consumer =
ActivatorUtilities.CreateInstance<MyExperienceConsumer>(serviceProvider);
In any case, you can use the DI framework to do the heavy lifting for you.
Your ExperienceRepository class have one constructor that requires a ToSeeDatabaseContext as parameter.
You are trying to create a instance ExperienceRepository with no parameters. The compiler can't find a constructor which doesn't take any parameters, producing the compiler error.
I'm new to C#/ASP coming from a Java world. I've read this article: https://docs.asp.net/en/latest/fundamentals/dependency-injection.html#service-lifetimes-and-registration-options which wisely warns about the dangers associated with injecting a dependency with a smaller scope. Unfortunately it does not explain how to solve this issue in C#/ASP.
In Java there's a concept of Provider
interface Provider<T> { T get(); }
which, among other things helps to solve the scoping issue:
whenever a binding for some type T is register we can inject an automatically generated instance of Provider<T> instead of T and then get an instance of T whenever it is needed: an automatically generated Provider makes sure that we get an instance appropriate for the current scope (whatever this scope is: HTTP request, HTTP session or other custom scopes). The standard DI framework built into ASP.NET core does not have anything like this, but I thought in C# it should be very easy to implement as C# generics don't suck like java's do (https://docs.oracle.com/javase/tutorial/java/generics/erasure.html). So I've created the following class:
public class Provider<T>: IProvider<T> {
private readonly IServiceProvider serviceProvider;
public Provider(IServiceProvider serviceProvider) {
this.serviceProvider = serviceProvider;
}
public T IProvider<T>.Get() {
return serviceProvider.GetService<T>();
}
}
and I attemtped to use it the following way:
public class SingletonService : ISingletonService {
private readonly IProvider<IScopedService> scopedServiceProvider;
public SingletonService(IProvider<IScopedService> scopedServiceProvider) {
this.scopedServiceProvider = scopedServiceProvider;
}
public string PerformMyTask() {
var scopedDependency = scopedServiceProvider.Get();
// do something with scopedDependency to verify we get instances
// appropriate for the current scope
}
}
and in my Startup class:
public void ConfigureServices(IServiceCollection services) {
services.AddSingleton<ISingletonService, SingletonService>();
services.AddScoped<IScopedService, ScopedService>();
services.AddTransient<IProvider<IScopedService>, Provider<IScopedService>>();
// other bindings here
}
Unfortunately this does not work the way I intended as IServiceProvider instance seems to be also scoped to the current HTTP request and I get exactly the same instance of ScopedDependency from my provider during processing of different requests :(
Any hints how can I solve this problem?
Is there any "higher level" object than ServiceProvider maybe, bound roughly to application lifecycle (not to the current request) that creates instances of request scoped objects (or of ServiceProvider itself) that I can inject into my Provider objects instead of ServiceProvider? For example in Java if I use google Guice as a DI framework there is an Injector object, usually created at the startup of an application which holds all the type bindings and has a method
<T> T getInstance(Class<T> type);
which checks what is the current scope and returns a corresponding instance.
edit:
I think that one possible way to do it would be to get a new reference to instance of ServiceProvider each time in the Proivder<T>.Get() method instead of injecting in the constructor and storing as an instance var. This way my components would still not be polluted with a reference to the framework specific IServiceProvider as it would be hidden from them in the implementation of Provider<T> that they access via the abstract IProvider<T> interface. I can't however find on the web if it's possible to get such a reference from my Provider class and how to do this. Any pointers in this direction would be appreciated :)
Thanks!
ok, found it:
public class Provider<T> : IProvider<T> {
IHttpContextAccessor contextAccessor;
public Provider(IHttpContextAccessor contextAccessor) {
this.contextAccessor = contextAccessor;
}
T IProvider<T>.Get() {
return contextAccessor.HttpContext.RequestServices.GetService<T>();
}
}
and in Startup:
public void ConfigureServices(IServiceCollection services) {
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<ISingletonService, SingletonService>();
services.AddScoped<IScopedService, ScopedService>();
services.AddTransient<IProvider<IScopedService>, Provider<IScopedService>>();
// other bindings
}
:)
see https://github.com/aspnet/Hosting/issues/793 for more details about using and registering HttpContextAccessor
I'm having problems resolving a repository when calling it from a console app. Everything works fine when running the application (.NET 4, C#, Entity Framework, Unity) as normal, but I've created a standalone console app that will be run from the task scheduler to import feeds. I'm very close to giving up and do a dirty hack and write a script to call a webpage instead of using a console app, but I thought I'd at least try to understand why it isn't working first.
I'm new to both Entity Framework and Unity, so please bear with me and let me know if I've left out any important information.
This is the error I'm getting:
Resolution of the dependency failed, type = "MyNamespace.Domain.Template.IRepository`2[MyNamespace.Domain.Employees.OrganisationalUnit,System.Guid]", name = "(none)".
Exception occurred while: while resolving.
Exception is: NullReferenceException - Object reference not set to an instance of an object.
at Microsoft.Practices.Unity.UnityContainer.DoBuildUp(Type t, Object existing, String name, IEnumerable`1 resolverOverrides)
at Microsoft.Practices.Unity.UnityContainer.Resolve(Type t, String name, ResolverOverride[] resolverOverrides)
at Microsoft.Practices.Unity.UnityContainerExtensions.Resolve[T](IUnityContainer container, ResolverOverride[] overrides)
at MyNamespace.Infrastructure.FeedDefinition.GetOrganisationalUnit(String name, OrganisationalUnit parent) in C:\FeedDefinition.cs:line 552
This is the console app code:
static void Main(string[] args)
{
if (args.Length > 0)
{
MyNamespace.Appliance.Configuration.Initialise.InitialiseContainer();
ImportFeedProcessor importFeedProcessor = new ImportFeedProcessor();
importFeedProcessor.Run(args[0]);
}
}
And this is where it fails:
IRepository<OrganisationalUnit, Guid> organisationalUnitRepository =
Context.Instance.Container.Resolve<IRepository<OrganisationalUnit, Guid>>();
If anyone can help me understand what's going wrong I'd be very grateful!
UPDATE:
Here's the (important) bits from the initialise class:
public static void InitialiseContainer()
{
UnityContainer container = new UnityContainer();
// Use HttpContext for registering instances against for live
IContext context = HttpContextWrapper.Instance;
// Register the HttpContext as the default context to use
container.RegisterInstance<IContext>(HttpContextWrapper.Instance);
// repositories
container.RegisterType<IRepository<Employee, Guid>, EmployeeRepository>(
new UnityContextLifetimeManager(context),
new InjectionConstructor(true, "OrganisationalUnit.Parent.Parent"));
container.RegisterType<IRepository<OrganisationalUnit, Guid>, EntityFrameworkRepository<OrganisationalUnit, Guid>>(
new UnityContextLifetimeManager(context),
new InjectionConstructor("Parent.Parent"));
// Create and populate a new Unity container from configuration
Context.Instance.Container = container;
}
Is it perhaps the HttpContext that does it?
Thanks,
Annelie
One option you could consider is creating two different Initialise classes (I'm guessing that is your Unity bootstrapper class).
The one you have can be used for your web application.
The other one should be non-web specific. Remove any reference to HttpContext (it won't be available in a Console app) and UnityContextLifetimeManager (assuming this is HttpContext specific as well).