Can't access memory cache - ASP.NET Core - c#

I want to store my data in memory cache and access it from another controller ,what i have done is
MemoryCache myCache = new MemoryCache(new MemoryCacheOptions());
//responseQID is my data i have already seen it contains data
myCache.Set("QIDresponse", responseQID);
in another controller i want to get this data :
MemoryCache myCache = new MemoryCache(new MemoryCacheOptions());
var inmemory= myCache.Get("QIDresponse");
inmemory is null, where am I doing wrong?

A common memory cache is provided by ASP.NET Core. You just need to inject it into each controller or other service where you want to access it. See the docs.
You need to ensure memory caching is registered, note this from the docs:
For most apps, IMemoryCache is enabled. For example, calling AddMvc,
AddControllersWithViews, AddRazorPages,
AddMvcCore().AddRazorViewEngine, and many other Add{Service} methods
in ConfigureServices, enables IMemoryCache. For apps that are not
calling one of the preceding Add{Service} methods, it may be necessary
to call AddMemoryCache in ConfigureServices.
To ensure this is the case, you add the registration within Startup.cs:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
...
Once memory caching is registered in Startup.cs, you can inject into any controller like this:
public class HomeController : Controller
{
private IMemoryCache _cache;
public HomeController(IMemoryCache memoryCache)
{
_cache = memoryCache;
}
...
Then use this _cache instance rather than using new to create one.

Related

Changing RedisCacheOptions after registering the StackExchangeRedisCache middleware

I'm trying to use Redis in my Asp.Net Core project. I added the packages StackExchange.Redis and Microsoft.Extensions.Caching.StackExchange.Redis to my project and the following code to register the middleware:
services.AddStackExchangeRedisCache(options =>
{
// do config
});
So far that works fine. But in this project Configuration settings are provided by a custom ConfigurationService which is pulling them from an external source. If changes in any of the configuration settings concerning Redis are detected I need to be able to reconfigure the StackExchangeRedisCache middleware.
How would I do that?
If I get the question right, does the case is... The project configuration setting was customize to fetch from another source by some interval ? Therefore, the setting on redis could change by time to time ?
If that's the case, I think we got to drop usage of AddStackExchangeRedisCache extensions. If you want to keep using this, here is some problem that got to solve:
Remove RedisCache singleton instance that previously register in ServiceCollection.
Remove config for RedisCacheOptions previously register in ServiceCollection
Register new RedisCacheOptions and RedisCache in ServiceCollection
Re-initialize ServiceProvider somehow
These step should be done atomicity, otherwise, exceptions likely to raise.
Quite cumbersome... does it ?
Another approach was to create a placeholder for IDistributedCache instance, register it as singleton and update the IDistributedCache instance if change was detected from the Configuration.
// Place holder, and register it as singleton
public class CacheAccessor : IDisposable
{
private RedisCache RedisCache { get; set; }
public void SetRedisCache(IOptions<RedisCacheOptions> options)
{
RedisCache?.Dispose();
RedisCache = new RedisCache(options);
}
public IDistributedCache LatestRedisCache => RedisCache;
public void Dispose()
{
RedisCache?.Dispose();
}
}
// Register it
services.AddSingleton<CacheAccessor>();
// Initialize it for the first time (write some extension or put it somewhere in program.cs would be nice.
// Run after the webhost instance got built and before it run)
// host was an instance of IHost
using var scope = host.Services.CreateScope();
var serviceProvider = scope.ServiceProvider;
serviceProvider.GetRequiredService<CacheAccessor>().SetRedisCache(Pass the option here).
// By time to time, if the config changed, extract the option and SetRedisCache again.
// And of course, get access to the cache would be difference
public IActionResult TestMethod([FromServices] CacheAccessor accessor)
{
var cache = accessor.LatestRedisCache;
}

Long lived HttpClient created by HttpClientFactory

I've read that HttpMessageHandlers are recycled every 2 minutes, but I'm not sure if a new one is assigned to an existing HttpClient?
I've tested it out by using SetHandlerLifetime(TimeSpan.FromSeconds(5)); and even after 2 minutes with countless requests, the httpClient is continuing to work, which is a good sign?
Does that mean that I have no cause for concern with DNS changes/socket exhaustion?
Inside ConfigureServices method:
var myOptions = app.ApplicationServices.GetService<IOptionsMonitor<MyOptions>>();
HttpClient httpClient = app.ApplicationServices.GetService<IHttpClientFactory>().CreateClient();
MyStaticObject.Configure(myOptions, httpClient);
EDIT: Added some sample code.
There are a few things to look at here, with the first being does MyStaticObject actually need to be static? If it does, I would recommend instead registering it as a Singleton so that you can still leverage dependency injection. Once you have done that, you can register IHttpClientFactory and use it from your code. Your ConfigureServices method may end up looking something like this
public void ConfigureServices(IServiceCollection services)
{
//The base extension method registers IHttpClientFactory
services.AddHttpClient();
services.AddSingleton<IMySingletonObject, MySingletonObject>();
}
Then in your consuming class, MySingletonObject in this case, you would configure it as such
public class MySingletonObject
{
private readonly IHttpClientFactory _clientFactory;
public MySingletonObject(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task SomeMethodThatUsesTheClient()
{
var client = _clientFactory.CreateClient();
//use the client
}
}
The reason for this is that IHttpClientFactory handles the lifetime and pool concerns for us. Per the docs:
Manages the pooling and lifetime of underlying HttpClientMessageHandler instances. Automatic management avoids common DNS (Domain Name System) problems that occur when manually managing HttpClient lifetimes.
This happens when you make the CreateClient call, so you want to do this inside the code using the client, as opposed to on startup of your application.
As a side note, if you do not need this class to be a singleton at all, you can use the extenion services.AddHttpClient<IMyClass, MyClass>() and inject an HttpClient directly into the class. The DI container will handle getting a client from the factory behind the scenes for you.
public static IServiceCollection AddApiClient(this IServiceCollection services,
Action<RgCommunicationClientOptions> action)
{
services.AddHttpClient<ISomeApiClient, SomeApiClient>()
.AddPolicyHandler(GetRetryPolicy())
.SetHandlerLifetime(TimeSpan.FromMinutes(4));
services.AddOptions();
services.Configure(action);
return services;
}
static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
//.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
.WaitAndRetryAsync(2, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
}
services.AddApiClient(options => options.BaseAddress = configuration.GetSection("ExternalServices:SomeApi")["Url"]);
You can inject HttpClient like above in the class via dependency injection. I have used Poly extension to add RetryPolicy. Also you can set the LifetTime of the handler by using SetHaandlerLifeTime. This way you can individually configure client for each client. In the logs you can see that httpHandler expires after 4 mins for the respective call. I have used the extension method to pass the options in the method, getting values via app settings.
Hope this helps.
Blockquote

Create global caching in class library at .net core 2.0

I have .net core 2.0 Web Api project.I have 10 web Api's and one class library project. I want to cache some data. So I create CacheHelper.cs in class library. And I inject to Startup.cs in web api projects. I want to call SetCommonCacheItems when webapi project start and getting anywhere in my controller. But when I call GetCities function, return null. What can be problem?
CacheHelper.cs (in class library project)
public class CacheHelper : ICacheHelper
{
private readonly IUnitOfWork unitOfWork;
MemoryCache cache = new MemoryCache(new MemoryCacheOptions());
public CacheHelper(IUnitOfWork unitOfWork)
{
this.unitOfWork = unitOfWork;
}
public void SetCommonCacheItems()
{
var cities= unitOfWork.CityRepo.GetAll();
cache.Set("city", cities);
}
public string GetCities()
{
string obj;
cache.TryGetValue<string>("city", out obj);
return obj;
}
}
Startup.cs (in Web API project)
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
services.AddScoped<IUnitOfWork, UnitOfWork>();
services.AddScoped<IJwtHelper, JwtHelper>();
services.AddScoped<IAuditHelper, AuditHelper>();
services.AddMemoryCache();
services.AddScoped<ICacheHelper, CacheHelper>();
services.AddMvc();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
That's because you are using your cache as a "scoped" service which means for every request the framework will create a new cache object, and the one you set at the app start will be lost.
Try this:
services.AddSingleton<ICacheHelper, CacheHelper>();
Important: This will break your unit of work as it will be a singleton as well within your cache.
I recommend to not use the unit of work inside the cache, instead just pass the results from your repo to your SetCommonCacheItems() as an argument.
Edit: Or you can use the built-in memory cache. In that case you don't have to use your cache helper as a singleton.
As services.AddMemoryCache(); register MemoryCache like this services.TryAdd(ServiceDescriptor.Singleton<IMemoryCache, MemoryCache>());, it means that cache is in singleton scope and will not be disposed after request is finished.
But in CacheHelper you have created MemoryCache via constructor, and, as soon as CacheHelper is disposed after request is finished, your cache will be lost.
To change this behavior, your have 2 options:
Add 'IMemoryCache' dependency into CacheHelper: public CacheHelper(IMemoryCache cache, IUnitOfWork unitOfWork) {}
Made CacheHelper to be singleton: services.AddScoped<ICacheHelper, CacheHelper>();

How to dynamically create and inject services in ASP.NET 5?

I'm in a situation where the classic functionality of vnext's DI container is not enough to provide me with the correct functionality. Let's say I have a DataService that gets data from a database like this:
public class DataService : IDataService, IDisposable {
public List<MyObject> GetMyObjects()
{
// do something to fetch the data...
return myObjects;
}
}
I can then register this service in the DI container during the configuration phase in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped(typeof(IDataService), typeof(DataService));
}
This ensures the correct lifecylce of the service (one per request scope), however, I need the service to access a different database when a different request is made. For simplicity reasons, let's say the following scenario applies:
when a request to my Web API is made, the DataService will access the currently logged in user, which contains a claim called Database which contains the information which database to use.
the DataService is then instantiated with the correct database connection.
In order to get the second step to work, I have created a constructor for the DataService like this:
public DataService(IHttpContextAccessor accessor)
{
// get the information from HttpContext
var currentUser = accessor.HttpContext.User;
var databaseClaim = currentUser.Claims.SingleOrDefault(c => c.Type.Equals("Database"));
if (databaseClaim != null)
{
var databaseId = databaseClaim.Value;
// and use this information to create the correct database connection
this.database = new Database(databaseId);
}
}
By using the currently logged in user and his claims, I can ensure that my own authentication middleware takes care of providing the necessary information to prevent attackers from trying to access the wrong database.
Of course adding the IDisposable implementation is required to cleanup any database connections (and gets called correctly using the scope lifecycle).
I can then inject the DataService into a controller like this
public MyController : Controller
{
private IDataService dataService;
public MyController(IDataService dataService)
{
this.dataService = dataService;
}
}
This all works fine so far.
My questions now are:
Is there another way to create the instance other than using the constructor of the DataService? Maybe accessing the object the IServiceCollection provides in a different place other than during the configration phase which runs only once? Maybe using my own OWIN middleware?
Is this method really safe? Could two requests made at the same time accidentally end up with the DataServiceintended for the other request and therefore end up giving out the wrong data?
What you have is fine.
Is there another way to create the instance other than using the constructor of the DataService? Maybe accessing the object the IServiceCollection provides in a different place other than during the configration phase which runs only once? Maybe using my own OWIN middleware?
Not really. You can use delegate registration but it's the same problem.
Is this method really safe?
Yes
Could two requests made at the same time accidentally end up with the DataServiceintended for the other request and therefore end up giving out the wrong data?
Nope. The IHttpContextAcessor uses AsyncLocal (http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html) to provide access to the "current" http context.

Correct using of DBContext

I have an application where multiple users can login. For that I have a kind of session object that stores a DBContext object. But the problem is, that the cached DBContext stores only the data of the logged user. When another user are also logged in, then the cached data is maybe older because it will be changed by the second user.
Is there a conceptional way to handle this. Instead of caching the DBContext object, I can create it every time I do an database request. Is this the correct way or is there a kind of event to catch to know that the database content is changed?
You should not be caching the DbContext object in any way. DbContext maintains state internally for multiple users automatically.
You create a new context when you open a controller to respond to a user request for data. In Entity Framework 6 this would look like
public class FeedItemController : ApiController
{
private LynxFeedAPI_Context db = new LynxFeedAPI_Context();
// GET api/FeedItem
public IQueryable<FeedItem> GetFeedItems()
{
return db.FeedItems;
}
It is done differently in EF 7 where Startup.cs is used to setup the Dependency Injection
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// Uncomment the following line to add Web API services which makes it easier to port Web API 2 controllers.
// You will also need to add the Microsoft.AspNet.Mvc.WebApiCompatShim package to the 'dependencies' section of project.json.
// services.AddWebApiConventions();
services.Configure<AppSettings>(configuration.GetConfigurationSection("AppSettings"));
// Add EF services to the services container.
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(configuration["Data:DefaultConnection:ConnectionString"]));
services.AddSingleton<IApplicationDbContext, ApplicationDbContext>();
services.AddSingleton<IProposalDataRepository, ProposalDataRepository>();
services.AddSingleton<IPositionDataRepository, PositionDataRepository>();
services.AddSingleton<IMandatoryReqDataRepository, MandatoryReqDataRepository>();
services.AddSingleton<IRatedReqDataRepository, RatedReqDataRepository>();
}
and is used by the controller
public class ProposalController : Controller
{
private readonly IProposalDataRepository repProposal;
public ProposalController(IProposalDataRepository repository) {
repProposal = repository;
}
[HttpGet]
public IEnumerable<Proposal> GetAll()
{
return repProposal.SelectAll();
}
where it is not necessary to ever make a call to create a new DbContext

Categories