Cache an object in a .ashx handler - asp.net

I'm not sure of the best way to approach this, but here is my scenario.
Let's say I have an ashx handler that feeds out some data to an ajax request from the client. Something simple like:
public class Handler : IHttpHandler
{
private List<MyObject> data;
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
data = GetData();
string result;
switch (context.Request.QueryString["requestType"])
{
case "case":
result = Foo.Bar(data).GetJSON();
break;
// .. several more data conversitions / comparisions
}
context.Response.Write(result);
}
public class MyObject
{
public int Id { get; set; }
public string Data { get; set; }
}
}
How would I cache the list of MyObject's so it is not rebuilt every time a request is made to the service? Let's say the list of MyObject gets thousands of results and I want to keep it cached for ~1 minute. Could I make use of context.Cache?
Basically; I do not want to cache the handlers output. Just an object with data that resides in it.
Edit:
I am looking for something along the lines of:
data = (List<MyObject>) context.Cache["data"];
if (data == null || !data.Any())
{
data = GetData();
context.Cache.Insert("data", data, null, DateTime.Now.AddMinutes(1), System.Web.Caching.Cache.NoSlidingExpiration);
}

You can add result to HttpContext.Current.Cache, something like this :
var requestType = context.Request.QueryString["requestType"];
HttpContext.Current.Cache[requestType] = result;

Related

Masstransit: How to build a cache filter in the Mediator

We used the Masstransit Mediator to write request/response "Consumers" called from API controllers. Before the consumer is taken action, some ConsumeFilters take place: Logging, Validation and DBTransaction. Next I would like to implement a Cache Filter using simple Microsoft In-Memory Cache. The filter should check if the request object is already in cache, if not the consumer pipe is called and the cache object is added, else the cached object should be returned immediatly.
I could not figure out how write such a filter. Do I need two filters? If I call RespondAsync from ConsumeContext how can a use a generic response type?
Has someone done it before, or should I do I directly in consumer?
Seems like something that should be in the consumer itself. The cache itself could be a dependency of the consumer, which is a single instance and injected into the consumer via the constructor. That way, it would be able to check if the results are in the cache before calling the backing service with the request detail.
Hiding that in a filter seems a little specific to the message type, so within the consumer will likely be easier for developers to understand later.
I figured out a solution to integrate in-memory caching as mass transit scope filter. Currently it is only used in a mediator. Some prequists:
All messages are records (with value bases equal methods)
The query request object inherits from ApplicationQueryRequest (a record)
The query response objects inherits from ApplicationResponse (a record)
The request object has an attribute named Cached Attribute:
[AttributeUsage(AttributeTargets.Class)]
public sealed class CacheAttribute : Attribute
{
public CacheAttribute(int slidingExpireSecs = 30, int absoluteExpireSecs = 100)
{
SlidingExpireSecs = slidingExpireSecs;
AbsoluteExpireSecs = absoluteExpireSecs;
}
public int SlidingExpireSecs { get; }
public int AbsoluteExpireSecs { get; }
}
Therefore each request object can have a cache attribute like:
[Cache]
public record FooRequest
{
}
Target is that the filter automatically fetches data from the cache and stores data in it.
First initialize the mediator with all consumer and send fiters, in our case only one scope filter exists but must be added for send and consume:
services.AddMediator(
configurator =>
{
(context, cfg) =>
{
cfg.UseSendFilter(typeof(CacheScopeFilter<>), context);
cfg.UseConsumeFilter(typeof(CacheScopeFilter<>), context);
Additionally the ICacheScope must be of
services.AddScoped...
THe scope filter looks like this:
public class CacheScopeFilter<T> :
IFilter<SendContext<T>>,
IFilter<ConsumeContext<T>> where T : class
{
private readonly ILogger<T> logger;
private readonly IMemoryCache memoryCache;
private readonly ICacheScope cacheScope;
private CacheOptions cacheOptions;
public CacheScopeFilter(ILogger<T> logger, IOptionsMonitor<CacheOptions> options, IMemoryCache memoryCache, ICacheScope cacheScope)
{
this.logger = logger;
cacheOptions = options.CurrentValue;
options.OnChange(
opts =>
{
logger.LogInformation($"Set Memory Cache enabled: {opts.EnableMemoryCache}");
cacheOptions = opts;
});
this.memoryCache = memoryCache;
this.cacheScope = cacheScope;
}
public async Task Send(ConsumeContext<T> context, IPipe<ConsumeContext<T>> next)
{
var requestName = typeof(T).Name;
logger.LogInformation($"----- Start check cache application query request {requestName} {context.Message}");
cacheScope.RequestKey = null;
if (context.TryGetMessage<ApplicationQueryRequest>(out var requestContext))
{
if(!cacheOptions.EnableMemoryCache)
{
logger.LogInformation("Cache is disabled");
await next.Send(context);
return;
}
var cacheAttribute = (CacheAttribute)Attribute.GetCustomAttribute(
requestContext.Message.GetType(),
typeof(CacheAttribute));
if (cacheAttribute == null)
{
await next.Send(context);
return;
}
cacheScope.RequestKey = typeof(T).FullName + ";" + JsonConvert.SerializeObject(context.Message);
cacheScope.SlidingExpireSecs = cacheAttribute.SlidingExpireSecs;
cacheScope.AbsoluteExpireSecs = cacheAttribute.AbsoluteExpireSecs;
if (memoryCache.TryGetValue(cacheScope.RequestKey, out ApplicationResponse cacheResponse))
{
logger.LogInformation($"Take data from cache {requestName} {context.Message}, CacheKey: {cacheScope.RequestKey}");
await context.RespondAsync(cacheResponse);
return;
}
logger.LogInformation($"Data not in cache, fetching data {requestName} {context.Message}");
}
await next.Send(context);
logger.LogInformation($"----- Finish check cache application query request {requestName} {context.Message}");
}
public async Task Send(SendContext<T> context, IPipe<SendContext<T>> next)
{
var requestName = typeof(T).Name;
logger.LogInformation($"----- Start handling cache application query response {requestName} {context.Message}");
var isCachedSet = context.TryGetPayload<CacheDoneMarker>(out _);
if (context.Message is ApplicationResponse && (cacheScope.RequestKey != null) && !isCachedSet)
{
logger.LogInformation($"Cache data {requestName} {context.Message}, CacheKey: {cacheScope.RequestKey}");
var cacheEntryOptions = new MemoryCacheEntryOptions().
SetSlidingExpiration(TimeSpan.FromSeconds(cacheScope.SlidingExpireSecs)).
SetAbsoluteExpiration(TimeSpan.FromSeconds(cacheScope.AbsoluteExpireSecs));
memoryCache.Set(cacheScope.RequestKey, context.Message, cacheEntryOptions);
context.GetOrAddPayload(() => new CacheDoneMarker());
}
await next.Send(context);
logger.LogInformation($"----- Finish handling cache application query response {requestName} {context.Message}");
}
public void Probe(ProbeContext context)
{
context.CreateFilterScope("cache");
}
}
public class CacheScope : ICacheScope
{
public string RequestKey { get; set; }
public int SlidingExpireSecs { get; set; }
public int AbsoluteExpireSecs { get; set; }
}
// Scope injected !
public interface ICacheScope
{
public string RequestKey { get; set; }
public int SlidingExpireSecs { get; set; }
public int AbsoluteExpireSecs { get; set; }
}
This should also work for distributed cache, but not added yet.

The optimal way to decouple business logic from controller

The rule is that controllers shouldn't have business logic, instead they should delegate it to the services. But when we do that, we can't handle all possible cases and return appropriate HTTP response.
Let's look at an example. Let's say that we are building some kind of a social network, and we need to create an endpoint for rating (liking or disliking) a post.
First let's take a look at an example where we delegate the logic to the service, this is our controller action:
public IActionResult Rate(long postId, RatingType ratingType)
{
var user = GetCurrentUser();
PostRating newPostRating = _postsService.Rate(postId, ratingType, user);
return Created(newPostRating);
}
Do you see a problem in this? What if there is no post with the given id, how would we return a not found response? What if user has no permissions to rate a post, how would we return a forbidden response?
PostsService.Rate can only return a new PostRating, but what about other cases? Well, we could throw an exception, we would need to create a lot of custom exception, so that we can map them to the appropriate HTTP responses. I don't like to use exceptions for this, I think there is a better way to do handle these cases instead of exceptions. Because I think that cases when post doesn't exist and when user has no permissions aren't exceptional at all, they're just normal cases just like rating a post successfully.
What I propose, is handling that logic in a controller instead. Because in my opinion, that should be a controllers responsibility anyway, to check all of the permissions before commiting an action. So this is how I would do it:
public IActionResult Rate(long postId, RatingType ratingType)
{
var user = GetCurrentUser();
var post = _postsRepository.GetByIdWithRatings(postId);
if (post == null)
return NotFound();
if (!_permissionService.CanRate(user, post))
return Forbidden();
PostRating newPostRating = new PostRating
{
Post = post,
Author = user,
Type = ratingType
};
_postRatingsRepository.Save(newPostRating);
return Created(newPostRating);
}
This is the way it should be done in my opinion but I bet that someone would say that this is too much logic for the controller, or that you shouldn't use a repository in it.
If you don't like using a repository in controller than where instead would you put a method that gets or saves posts? In service? So there would be PostsService.GetByIdWithRatings and PostsService.Save that would do nothing else but just call PostsRepository.GetByIdWithRatings and PostsRepository.Save. This so unnecessary and only causes boilerplate code.
Update:
Maybe someone will say to check the permissions using PostsService and then call PostsService.Rate. This is bad because it involves more unnecessary trips to database. For an example, it would probably be something like this:
public IActionResult Rate(long postId, RatingType ratingType)
{
var user = GetCurrentUser();
if(_postsService.Exists(postId))
return NotFound();
if(!_postsService.CanUserRate(user, postId))
return Forbidden();
PostRating newPostRating = _postsService.Rate(postId, ratingType, user);
return Created(newPostRating);
}
Do I even need to explain any further why this is bad?
There's a number of ways to handle this, but the closest thing to a "best practice" method is probably using a result class. For example, if your service method creates a rating and then returns that rating it created, you instead return an object that encapsulates the rating along with other relevant information, such as success status, error messages, if any etc.
public class RateResult
{
public bool Succeeded { get; internal set; }
public PostRating PostRating { get; internal set; }
public string[] Errors { get; internal set; }
}
Then, your controller code would become something like:
public IActionResult Rate(long postId, RatingType ratingType)
{
var user = GetCurrentUser();
var result = _postsService.Rate(postId, ratingType, user);
if (result.Succeeded)
{
return Created(result.PostRating);
}
else
{
// handle errors
}
}
What I did (just now) is created new class ApiResult
public class ApiResult
{
public int StatusCode { get; private set; } = 200;
public string RouteName { get; private set; }
public object RouteValues { get; private set; }
public object Content { get; private set; }
public void Ok(object content = null)
{
this.StatusCode = 200;
this.Content = content;
}
public void Created(string routeName, object routeValues, object content)
{
this.StatusCode = 201;
this.RouteName = routeName;
this.RouteValues = routeValues;
this.Content = content;
}
public void BadRequest(object content = null)
{
this.StatusCode = 400;
this.Content = content;
}
public void NotFound(object content = null)
{
this.StatusCode = 404;
this.Content = content;
}
public void InternalServerError(object content = null)
{
this.StatusCode = 500;
this.Content = content;
}
}
And a controller base class with a single method TranslateApiResult
public abstract class CommonControllerBase : ControllerBase
{
protected IActionResult TranslateApiResult(ApiResult result)
{
if (result.StatusCode == 201)
{
return CreatedAtAction(result.RouteName, result.RouteValues, result.Content);
}
else
{
return StatusCode(result.StatusCode, result.Content);
}
}
}
And now in controller I do:
[ApiController]
[Route("[controller]/[action]")]
public class MyController : CommonControllerBase
{
private readonly IMyApiServcie _service;
public MyController (
IMyApiServcie service)
{
_service = service;
}
[HttpGet]
public async Task<IActionResult> GetData()
{
return TranslateApiResult(await _service.GetData());
}
}
In your services you inject repositories and other dependencies:
public class MyApiServcie : IMyApiServcie
{
public async Task<ApiResult> GetData()
{
var result = new ApiResult();
// do something here
result.Ok("success");
return result;
}
}
Now, the reason for Api prefix before the Service is that this service is not meant to be the final service containing all logic.
At this point I would split the business logic into different domains so the services (or facades) would end up without Api prefix in them just to differentiate between i.e. CarService. Preferably these services will not know of anything related to API responses, statuses etc. It's up to you how implement it, though.

It is possible to make a basic CRUD on JSON file?

I have a news site in ASP.NET MVC, where all the data comes from the homepage of the database. And this is generating a large traffic to my hosting server.
I have sessions like: Politics, Sports, World, among others, and each session is a different request made to the database. But each request only a small number of data is returned from the database, maximum 8 results for query. In total there are 21 requests made to the database.
I thought about making a data.json file and persist the session information in this file and show them in the frontend.
But as the file data.json and not a database, I have to do all the work to relocate the information when an action to delete or edit is performed.
Is there a better way to get to the point I want, considering that the site is already published?
I would put a caching layer in between the site and the db so when you do one of these calls to the db it first checks the cache to see if the objects are there, if they are then it returns them from memory (super quick), if not it gets from db and populates the cache for next time. You must remember to clear the cache when the data in the DB gets changed though.
Cache Interface:
public interface ICacheProvider
{
void Add(string key, object value);
void Add(string key, object value, TimeSpan timeout);
object Get(string key);
object this[string key] { get; set; }
bool Remove(string key);
bool Contains(string key);
void ClearAll();
}
Cache Implementation:
public class HttpCacheProvider : ICacheProvider
{
/// <summary>
/// The http cache
/// </summary>
private readonly Cache _cache;
/// <summary>
/// How often to expire the cache
/// </summary>
public int CacheExpiryMinutes { get; set; }
public HttpCacheProvider()
: this(HttpContext.Current.Cache, 60)
{
}
public HttpCacheProvider(int cacheExpiryMinutes)
: this(HttpContext.Current.Cache, cacheExpiryMinutes)
{
}
public HttpCacheProvider(Cache cache, int cacheExpiryMinutes)
{
_cache = cache;
CacheExpiryMinutes = cacheExpiryMinutes;
}
public void Add(string key, object value)
{
if (_cache[key] == null)
_cache.Insert(key, value, null, DateTime.Now.AddMinutes(CacheExpiryMinutes), Cache.NoSlidingExpiration);
}
public void Add(string key, object value, TimeSpan timeout)
{
if (_cache[key] == null)
_cache.Insert(key, value, null, DateTime.Now.Add(timeout), Cache.NoSlidingExpiration);
}
public object Get(string key)
{
return _cache[key];
}
public bool Contains(string key)
{
return (this[key] != null);
}
public object this[string key]
{
get { return _cache[key]; }
set { _cache[key] = value; }
}
public bool Remove(string key)
{
return _cache.Remove(key) != null;
}
public void ClearAll()
{
var enumerator = _cache.GetEnumerator();
while (enumerator.MoveNext())
{
_cache.Remove(enumerator.Key.ToString());
}
}
}
Usage:
private ICacheProvider _cacheProvider;
public IEnumerable<Something> GetData(int param1, int param2)
{
var cacheKey = string.Format("{0}-{1}", param1, param2);
if (!_cacheProvider.Contains(cacheKey))
{
try
{
// get data from db
// add that data to cahce
_cacheProvider.Add(cacheKey, nodes);
}
catch (Exception ex)
{
// blah, logging
}
}
return _cacheProvider[cacheKey] as IEnumerable<Something>;
}

Web method return JSON result in two level (In kendoUI Datasource)

This the server side Code
[System.Web.Services.WebMethod]
public static object GetDevelopers()
{
return new DqListViewModel(DQContext.Service._IDqs_IssueRepository.SelectList().ToArray(), 10);
}
View Model
public class DqListViewModel
{
public Array Data { get; set; }
public int Count { get; set; }
public DqListViewModel(Array data, int count)
{
this.Data = data;
this.Count = count;
}
}
This is the JSON return Value
why the JSON result has tow level object. I am not supposed to have "d" level?
Please check the below link. http://encosia.com/a-breaking-change-between-versions-of-aspnet-ajax/
This is not an issue from Knedo-ui but it is the functionality of the Asp.net
Please try with the below link, may be it will help you.
How to bind JSON child array to Kendo grid

How do I test response data using nUnit?

Django has a very handy test client/dummy web browser that one can use in test cases to verify the correctness of HTTP responses (e.g., status codes, context/model data). It does not require you to have the web server running, as it deals directly with the framework to simulate the calls.
I'd really love an nUnit (or similar) equivalent that we can slip right into our test suites. We're working in MVC3 and 4, and want to check things like successful 301 redirects, that model validation is correct, and that ViewModel data is correct in the views.
What's the best solution for this?
ViewModel Data should be easy to check with the following:
public T GetViewModelFromResult<T>(ActionResult result) where T : class
{
Assert.IsInstanceOf<ViewResult>(result);
var model = ((ViewResult)result).Model;
Assert.IsInstanceOf<T>(model);
return model as T;
}
[Test]
public void TheModelHasTheOrder()
{
var controller = new MyController();
var result = controller.MyActionMethod();
var model = GetViewModelFromResult<MyModel>();
Assert.That(model, Is.SameAs(???));
}
As for the model validation, if you are using the out of the box .net property attributes like [Required] etc, you can be pretty sure they will work fine, and won't need testing.
To explicitly test the [Required] etc attributes on your object you will have extract the built in .net validation into another class. Then use that class in your controllers to validate your objects, instead of the Model.IsValid property on your controller.
The model validator class:
public class ModelValidator : IModelValidator
{
public bool IsValid(object entity)
{
return Validate(entity, new List<ValidationResult>());
}
public IEnumerable<ValidationResult> Validate(object entity)
{
var validationResults = new List<ValidationResult>();
Validate(entity, validationResults);
return validationResults;
}
private static bool Validate(object entity, ICollection<ValidationResult> validationResults)
{
if (entity != null)
{
var validationContext = new ValidationContext(entity, null, null);
return Validator.TryValidateObject(entity, validationContext, validationResults);
}
return false;
}
}
This could be verifiable in unit tests with the following:
public class MySampleEntity
{
[Required]
public string X { get; set; }
[Required]
public int Y { get; set; }
}
[TestFixture]
public class ModelValidatorTests
{
[Test]
public void GivenThePropertiesArePopulatedTheModelIsValid()
{
// arrange
var _validator = new ModelValidator();
var _entity = new MySampleEntity { X = "ABC", Y = 50 };
// act
var _result = _validator.IsValid(_entity);
// assert
Assert.That(_result, Is.True);
}
}

Resources