Why is my Lazy<T> re-initializing every time I call Value? - c#

I have a member of my controller
private Lazy<MyCache> loadedComponentCache = new Lazy<MyCache>(() =>
{
MyCache instance = MyCacheb.Instance;
instance.LoadStuffAsync().Wait();
return instance;
}, LazyThreadSafetyMode.PublicationOnly);
that I'm using to lazy-call a long-running method LoadAsync() that will only need called if a certain API endpoint is hit after the user goes to the page.
[HttpGet]
public ActionResult GetStuff()
{
var results = from component in loadedComponentCache.Value.All()
// ...
}
Any idea why it's re-loading every time the API endpoint is hit? My understanding is that an instance of my controller is created only when the user goes to the page and thus this will only be hit once per API call per user visiting the page.

You could make loadedComponentCache static but that's not ideal. If you are using an IoC container you could register it as a singleton. These long lived objects are generally to be avoided though if possible.
If you you truely need this long lived cache then you should probably consider using something like Redis which is designed and optimised for this sort of scenario and can be distributed across multiple nodes. https://redis.io/topics/introduction

Related

Why is my scoped service being called as a new instance every time?

This is a practice ASP.NET project I'm using to better understand a few techniques, and while I've got Dependency Injection working, its not working quite as I want it to. I have a class that I want to use to store a history, so every time the user hits a submit button, it displays a result, and after the second time it starts displaying the history. Anyway I added the history to the DI as a scoped service, thinking that would mean it would be created and then remain the same instance for the duration of the session for that user. However according to the debugger it looks like the list never gets bigger than one, and thats at the point of adding the item to the list. So the code.
The object
{
public class RollHistory : IRollHistory
{
public List<IRollMessage> Entries { get; set; } = new List<IRollMessage>();
}
}
The DI
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddTransient<IDiceTray, DiceTray>();
services.AddTransient<IRollMessage, RollMessage>();
services.AddScoped<IRollHistory, RollHistory>();
}
The Controller constructor
public HomeController(ILogger<HomeController> logger, IDiceTray diceTray, IRollMessage rollMessage, IRollHistory rollHistory)
{
_logger = logger;
_diceTray = diceTray;
_rollMessage = rollMessage;
_rollHistory = rollHistory;
}
And the code for when the button gets clicked
[HttpPost]
public IActionResult Index(DiceRollModel diceRoll)
{
_diceTray.DiceRoll(diceRoll.DiceType, diceRoll.DiceCount, diceRoll.Bonus, diceRoll.VantageType);
_rollMessage.RollMessages(_diceTray);
diceRoll.RollResult = _rollMessage;
_rollHistory.Entries.Add(_rollMessage);
diceRoll.History = _rollHistory.Entries;
return View(diceRoll);
}
It's worth noting I've tried to code this at least 4 different ways with and without DI, the only way it works is if I use AddSingleton, while this might not be an issue because this app is unlikely to ever be live, its a poor excuse not to do it right.
I believe “scope” is by default per request which would explain that each submit gets is own service.
“Doing stuff right” is of course to some extend a matter of opinion. But my opinion would clearly be that I would avoid server-side session to avoid problems with scaling to more than one instance. There are also ways to support shared state, but this is difficult. To me singletons are not a code smell either, but they have their own problems.
Your problem might be solved by storing whatever state you need in the browser either in a cookie or localStorage. Your service would then have request scope, but it would read user state from browser causing “user scope” for the data. (But don’t rely on browser state to persist and remember it is modifiable to the user.)

How to build architecture better

I have an ASP.NET Core application which calls a service from another library. The
service works with an external API, which requires a sessionId. We have to call a Login API method to get the sessionId. How long this sessionId lives and when it can be changed - we don't know. Rule is: sessionId can be valid for 1 request, for 10 requests, for 100 requests, can be valid 1 minute, 10 minutes, 1 day... Nobody knows it.
The service has many methods to call similar APIs:
public class BillRequest
{
private readonly HttpClient client;
public BillRequest()
{
client = new HttpClient
{
BaseAddress = new Uri("https://myapi.com/api/v2/")
};
}
public async Task<List<Dto1>> CustomerBankAccountListAsync(int start, int count)
{
List<KeyValuePair<string, string>> nvc = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("sessionId", CURRENT_SESSION_ID)
};
var customerStream = await client.PostAsync("List/CustomerBankAccount.json", new FormUrlEncodedContent(nvc));
var customerString = await customerStream.Content.ReadAsStringAsync();
//....
}
public async Task<List<Dto2>> Method2(int start, int count)
{
List<KeyValuePair<string, string>> nvc = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("sessionId", CURRENT_SESSION_ID)
};
var customerStream = await client.PostAsync("List/Method2.json", new FormUrlEncodedContent(nvc));
var customerString = await customerStream.Content.ReadAsStringAsync();
//....
}
// logic to get SessionId here
public async Task LoginAsync()
{
}
How to implement to save this sessionId inside service?
There are many options to implement:
Call Login method every time before calling a method. Easy to implement, but bad approach, because we have many unnecessary requests then and use the sessionId only once
Save the sessionId on web application level and try to catch exception, when any method gets an 'invalid sessionId' back, and then call Login method, which will return a new sessionId. In this case we have to pass sessionId to constructor of BillRequest class. It works, but I don't like to move responsibility of service to other, because it's internal responsibility of service how to work with API.
Save sessionId inside the service itself and recall Login method inside service, when old sessionId is considered invalid, rewrite it by new etc. But how to save it as "static" in memory? I don't want to save it to any external places (file system, cloud etc), but I can't save to variable of class too, because object of class can be recreated...
I'd suggest certain mental shift here towards functional programming.
Think of sessionID as of a stream of independet values rather than a single object. Then your problem can be redefined in a following (semantically equivalent) way: given a typed stream (string in your case), how to observe its flow and react on incomming changes, which your source code does not control?
Well, there is an answer, proven by an Enterprise™: reactive extensions.
Techinically such a shift impliest that you're dealing with an IObservable<string> inside of your controller, which either can be injected via the standard .NET Core DI approach, or simply defined by the constructor. That's quite flexible, since rX gives your fully testable, unbelivable powerful toolset to deal with taks of this kind; rX is also compatible with native Task and hence, async/await feature. Nice fact is that it is really easy to inject required behavior from an outerworld and decorate exising observable with a more appropriate one: so, you're safe: once 3rd party's service logic changes, you can adopt your codebase almost instantly and painlessly.
What is gonna be inside that IObservable<string>? Well, I can't say, since you did not give enough information. It might be an interval asking remote server whether current sessionID is still valid and in case not - runs relogin procedure and notifies it's subscrivers about new value; it might be a timer responsible for compile-time known rule of expiration, it might be as sophisticated logic as you need: rX is flexible enough not to limit you on what can be achieved with it as long as you deal with (possible infinite) streams.
As a consequence, it means that you don't need any global value. Just subscribe to a stream of session ids and take latest - the one which is currently valid, do the job and dispose your subscription. It is not expensive and won't hit performance; neither would mess up concurency. Wrap rX into Task and await it, if you'd like to stick to a common .NET fashion.
P.S. 99% of what you would need to deliver an implementation is already there; you just need to combine it.

How to cache data from repository call

I am working in a multi-layered web application that has ASP.NET MVC as its front-end client. A particular page of this web application is taking a very long time to load. Around 30 seconds.
I downloaded dotTrace and ran it on my application (following this tutorial). I found out that the reason my application is slow.
It turns out it is because one particular method that I have does a load of work (takes time), and that same method gets called a total of 4 times.
Here is a screenshot from dotTrace showing the above:
The method in question is GetTasks(). So in order to improve the speed of the web application I want to cache the data returned from GetTasks() for each request.
If my thinking is correct, this would really improve on the speed issues I am having.
My question is, how can I achieve this? I have never done such a thing before. For each new request, how can I cache the data returned from GetTasks(), and use that for all subsequent calls to GetTasks().
Have you considered the Cache Aside pattern?
You can implement it easily using LazyCache
//probably in my constructor (or use dependency injection)
this.cache = new CachingService()
public List<MyTasks> GetTasks()
{
return cache.GetOrAdd<List<MyTasks>>("get-tasks", () = > {
//go and get the tasks here.
});
}
For more information see https://alastaircrabtree.com/the-easy-way-to-add-caching-to-net-application-and-make-it-faster-is-called-lazycache/
One of the most popsular solution is to cache results. I can shouw you my solution.
First of all install Nuget package: LazyCache
Then you can use wrapper that I've created wrapper: code. You can extract and interface or whatever.
Then you can use it like this:
private readonly CacheManager cacheManager = new CacheManager();
// or injected via ctor
public IEnumerable<Task> GetTasks()
{
return this.cacheManager.Get("Tasks", ctx => this.taskRepository.GetAll());
}
public void AddTask(Task task)
{
this.taskRepository.Create(task);
/// other code
// we need to tell the cache that it should get fresh collectiion
this.cacheManager.Signal("Tasks");
}

Ajax call to the Asp.net Web API controller

As part of the project we have implemented ASP.Net Web API, which returns the Json data, which is consumed by the Javascript using Angular JS on the client.
Controller code is straight forward (Trimmed description):
public class CardController : ApiController
{
// code
[HttpGet]
public CardDataGetUI GetCardDataUI(int userID, int dashBoardID, int cardID)
{
// Access the application Cache object using HttpRuntime (System.Web.Caching)
var blCache = HttpRuntime.Cache;
// Create a user specific BL access key by concatenating the user ID
string userBLAccessKey = WebAPIConstant.BlUserDashboardCard + userID;
// Access the BL object stored in the Cache
accessBL = (Bl)blCache[userBLAccessKey];
// Other Code
// Fetch the data for the control being passed
cardDataUI = accessBL.GetCardDataUI(dashBoardID, cardID);
return (cardDataUI)
}
}
The above mentioned GetCardDataUI delivers the card data for different type of control like chart, map and grid on a same UI screen, so what UI does is make an Asynchronous call to all in one go, currently I have BL (business layer) object being accessed from application wide cache, which is an issue for Multi threaded access, as they would share same object, so I have converted that to a local copy and initialized the one for each call to the controller. However that is also good enough till the each ajax call is having it's unique controller instance to call the method. However in this case it seems the http call they make have same instance thus modifying the input variable of each call thus leading to unexpected result and exception, since it is modifying the internal DS at run time. It is akin to calling the static method
Ideally I did not expected a multi-threaded call to the business layer, but it seems in Angular JS client has to make such calls, they cannot be synchronous.
Currently I have resolved the situation by introducing a lock in the controller, which certainly allows one thread at a time
However was looking for a solution like each Ajax call can have it's own controller instance, when it make the http get call.
We also have an option of modifying the above mentioned controller method like:
public CardDataGetUI[] GetCardDataUI(int userID, int dashBoardID, int[] cardID)
{
// Code
}
In this case there will be one call for all cards and I will call the data fetch in a for loop, thus synchronizing the operation, but this is not much different from locking the controller, preferable will be a separate controller instance for each AJAX call
Any suggestion?

How to implement a caching model without violating MVC pattern?

I have an ASP.NET MVC 3 (Razor) Web Application, with a particular page which is highly database intensive, and user experience is of the upmost priority.
Thus, i am introducing caching on this particular page.
I'm trying to figure out a way to implement this caching pattern whilst keeping my controller thin, like it currently is without caching:
public PartialViewResult GetLocationStuff(SearchPreferences searchPreferences)
{
var results = _locationService.FindStuffByCriteria(searchPreferences);
return PartialView("SearchResults", results);
}
As you can see, the controller is very thin, as it should be. It doesn't care about how/where it is getting it's info from - that is the job of the service.
A couple of notes on the flow of control:
Controllers get DI'ed a particular Service, depending on it's area. In this example, this controller get's a LocationService
Services call through to an IQueryable<T> Repository and materialize results into T or ICollection<T>.
How i want to implement caching:
I can't use Output Caching - for a few reasons. First of all, this action method is invoked from the client-side (jQuery/AJAX), via [HttpPost], which according to HTTP standards should not be cached as a request. Secondly, i don't want to cache purely based on the HTTP request arguments - the cache logic is a lot more complicated than that - there is actually two-level caching going on.
As i hint to above, i need to use regular data-caching, e.g Cache["somekey"] = someObj;.
I don't want to implement a generic caching mechanism where all calls via the service go through the cache first - i only want caching on this particular action method.
First thought's would tell me to create another service (which inherits LocationService), and provide the caching workflow there (check cache first, if not there call db, add to cache, return result).
That has two problems:
The services are basic Class Libraries - no references to anything extra. I would need to add a reference to System.Web here.
I would have to access the HTTP Context outside of the web application, which is considered bad practice, not only for testability, but in general - right?
I also thought about using the Models folder in the Web Application (which i currently use only for ViewModels), but having a cache service in a models folder just doesn't sound right.
So - any ideas? Is there a MVC-specific thing (like Action Filter's, for example) i can use here?
General advice/tips would be greatly appreciated.
An action attribute seems like a good way to achieve this. Here's an example (disclaimer: I am writing this from the top of my head: I've consumed a certain quantity of beer when writing this so make sure you test it extensively :-)):
public class CacheModelAttribute : ActionFilterAttribute
{
private readonly string[] _paramNames;
public CacheModelAttribute(params string[] paramNames)
{
// The request parameter names that will be used
// to constitute the cache key.
_paramNames = paramNames;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
var cache = filterContext.HttpContext.Cache;
var model = cache[GetCacheKey(filterContext.HttpContext)];
if (model != null)
{
// If the cache contains a model, fetch this model
// from the cache and short-circuit the execution of the action
// to avoid hitting the repository
var result = new ViewResult
{
ViewData = new ViewDataDictionary(model)
};
filterContext.Result = result;
}
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
base.OnResultExecuted(filterContext);
var result = filterContext.Result as ViewResultBase;
var cacheKey = GetCacheKey(filterContext.HttpContext);
var cache = filterContext.HttpContext.Cache;
if (result != null && result.Model != null && cache[key] == null)
{
// If the action returned some model,
// store this model into the cache
cache[key] = result.Model;
}
}
private string GetCacheKey(HttpContextBase context)
{
// Use the request values of the parameter names passed
// in the attribute to calculate the cache key.
// This function could be adapted based on the requirements.
return string.Join(
"_",
(_paramNames ?? Enumerable.Empty<string>())
.Select(pn => (context.Request[pn] ?? string.Empty).ToString())
.ToArray()
);
}
}
And then your controller action could look like this:
[CacheModel("id", "name")]
public PartialViewResult GetLocationStuff(SearchPreferences searchPreferences)
{
var results = _locationService.FindStuffByCriteria(searchPreferences);
return View(results);
}
And as far as your problem with referencing the System.Web assembly in the service layer is concerned, that's no longer a problem in .NET 4.0. There's a completely new assembly which provides extensible caching features : System.Runtime.Caching, so you could use this to implement caching in your service layer directly.
Or even better if you are using an ORM at your service layer probably this ORM provides caching capabilities? I hope it does. For example NHibernate provides a second level cache.
I will provide general advices and hopefully they will point you to the right direction.
If this is your first stab at caching in your application, then don't cache HTTP response, cache the application data instead. Usually, you start with caching data and giving your database some breathing room; then, if it's not enough and your app/web servers are under huge stress, you can think of caching HTTP responses.
Treat your data cache layer as another Model in MVC paradigm with the all subsequent implications.
Whatever you do, don't write your own cache. It always looks easier than it really is. Use something like memcached.
My answer is based on the assumption that your services implement an interface, for example the type of _locationService is actually ILocationService but is injected with a concrete LocationService. Create a CachingLocationService that implements the ILocationService interface and change your container configuration to inject that caching version of the service to this controller. The CachingLocationService would itself have a dependecy on ILocationService which would be injected with the original LocationService class. It would use this to execute the real business logic and concern itself only with pulling and pushing from cache.
You don't need to create CachingLocationService in the same assembly as the original LocationService. It could be in your web assembly. However, personally I'd put it in the original assembly and add the new reference.
As for adding a dependency on HttpContext; you can remove this by taking a dependency on
Func<HttpContextBase>
and injecting this at runtime with something like
() => HttpContext.Current
Then in your tests you can mock HttpContextBase, but you may have trouble mocking the Cache object without using something like TypeMock.
Edit: On further reading up on the .NET 4 System.Runtime.Caching namespace, your CachingLocationService should take a dependency on ObjectCache. This is the abstract base class for cache implementations. You could then inject that with System.Runtime.Caching.MemoryCache.Default, for instance.
It sounds like you're trying to cache the data you are getting from your database. Here's how I handle this (an approach that I've seen used in many open-source MVC projects):
/// <summary>
/// remove a cached object from the HttpRuntime.Cache
/// </summary>
public static void RemoveCachedObject(string key)
{
HttpRuntime.Cache.Remove(key);
}
/// <summary>
/// retrieve an object from the HttpRuntime.Cache
/// </summary>
public static object GetCachedObject(string key)
{
return HttpRuntime.Cache[key];
}
/// <summary>
/// add an object to the HttpRuntime.Cache with an absolute expiration time
/// </summary>
public static void SetCachedObject(string key, object o, int durationSecs)
{
HttpRuntime.Cache.Add(
key,
o,
null,
DateTime.Now.AddSeconds(durationSecs),
Cache.NoSlidingExpiration,
CacheItemPriority.High,
null);
}
/// <summary>
/// add an object to the HttpRuntime.Cache with a sliding expiration time. sliding means the expiration timer is reset each time the object is accessed, so it expires 20 minutes, for example, after it is last accessed.
/// </summary>
public static void SetCachedObjectSliding(string key, object o, int slidingSecs)
{
HttpRuntime.Cache.Add(
key,
o,
null,
Cache.NoAbsoluteExpiration,
new TimeSpan(0, 0, slidingSecs),
CacheItemPriority.High,
null);
}
/// <summary>
/// add a non-removable, non-expiring object to the HttpRuntime.Cache
/// </summary>
public static void SetCachedObjectPermanent(string key, object o)
{
HttpRuntime.Cache.Remove(key);
HttpRuntime.Cache.Add(
key,
o,
null,
Cache.NoAbsoluteExpiration,
Cache.NoSlidingExpiration,
CacheItemPriority.NotRemovable,
null);
}
I have those methods in a static class named Current.cs. Here's how you can apply those methods to your controller action:
public PartialViewResult GetLocationStuff(SearchPreferences searchPreferences)
{
var prefs = (object)searchPreferences;
var cachedObject = Current.GetCachedObject(prefs); // check cache
if(cachedObject != null) return PartialView("SearchResults", cachedObject);
var results = _locationService.FindStuffByCriteria(searchPreferences);
Current.SetCachedObject(prefs, results, 60); // add to cache for 60 seconds
return PartialView("SearchResults", results);
}
I've accepted #Josh's answer, but thought i'd add my own answer, because i didn't exactly go with what he suggested (close), so thought for completeness i'd add what i actually did.
The key is i am now using System.Runtime.Caching. Because this exists in an assembly which is .NET specific and not ASP.NET specific, i have no problems referencing this in my service.
So all i've done is put the caching logic in the specific service layer methods that need the caching.
And an important point, im working off System.Runtime.Caching.ObjectCache class - this is what get's injected into the constructor of the service.
My current DI injects a System.Runtime.Caching.MemoryCache object. The good thing about the ObjectCache class is that it is abstract and all the core methods are virtual.
Which means for my unit tests, i have created a MockCache class, overriding all methods and implementing the underlying cache mechanism with a simple Dictionary<TKey,TValue>.
We plan to switch over to Velocity soon - so again, all i need to do is create another ObjectCache deriving class and i'm good to go.
Thanks for the help everyone!

Categories