How to persist an object for longer than a request - asp.net

I'm trying to implement a very simple api. It should instantiate a tv object which status should be changed (from off to on and from on to off). My current problem is that after I change the status in a get-method the tv object is reinstantiated.
Is there a simple way to persist the object for longer than one request?
Here my code:
public class TvController : ApiController
{
Tv tv = new Tv(null, null, false);
// GET: api/Tv
[HttpGet]
public bool Get()
{
if (!tv.Status) { tv.Status = true; }
else { tv.Status = false; }
return tv.Status;
}
public class Tv
{
public string Channel { get; set; }
public string Volume { get; set; }
public bool Status { get; set; }
public Tv(string channel, string volume, bool status)
{
Channel = channel;
Volume = volume;
Status = status;
}
}
}

Since your API is stateless, you would need to figure out a way to persist the request with some sort of lookup table (perhaps you store the API key along with the object associated with it in a Redis node)
I'm not sure you can use Session with an API Controller as that likely defeats the whole point, but if you could, you would do the following.
And your code would need to be updated to do this:
if( Session["TVObject"] == null) {
Tv tv = new Tv(null, null, false);
Session.Add("TVObject", tv);
} else {
Tv tv = Session["TVObject"] as Tv;
}

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.

Realtime validation of a button, using fody

I a trying to disable and enable a button based on user input. I implemented fody property changed Nuget package, to help me reduce my code a bit.
and it works, when I start typing, the breakpoint in my LoginViewModel gets hit and display my values ViewModel getting hit every time I type
but I can't seem to trigger CanLogin() method
[AddINotifyPropertyChangedInterface]
public class LoginPageViewModel {
public ICommand OpenRegisterPopupCommand { get; set; }
public ICommand Register { get; set; }
public ICommand Login { get; set; }
public Users Users { get; set; }
public bool IsPopUpOpen { get; set; }
public LoginPageViewModel() {
Users = new Users();
Login = new Command(LoginAction, CanLogin);
Register = new Command(RegisterAction);
OpenRegisterPopupCommand = new Command(() => {
IsPopUpOpen = true;
});
}
private void LoginAction(object obj) {
throw new NotImplementedException();
}
private bool CanLogin(object arg) {
if (Users != null && !string.IsNullOrEmpty(Users.Email) && !string.IsNullOrEmpty(Users.Password)) {
return true;
}
return false;
}
You are using a Command with a CanExecute method. If the CanExecute method returns false, the command will not ve able to be executed. But this validation does not happen all the time, you have to trigger it.
You have to call Login.ChangeCanExecute() when you modify any of the related properties (like Users, Users.Email or Users.Password), this will fire the CanExecute validation of the command.
Command documentation.

EF Core with Lazy Loading tracks unreachable Objects?

I am currently having troubles with entity framework core.
The application I am developing is supposed to help users plan their next business year by increasing/decreasing the quantity of a service they want to provide in the next year.
Based on their input the "worth" of a service is distributed pro rata to other "mini-services" that are contained in the changed service.
To do so I load the affected entries of the main service and the "mini-services" from Database via a repository which then uses Entity Framework.
public IEnumerable<OpsDistributionEntry> FilteredOpsDistributionEntries(int catalogId, IEnumerable<OpsDistributionEntry> filterEntries)
{
return _context.OpsDistributionEntries.FromSqlRaw(
$"SELECT * FROM OpsDistributionEntries WHERE (Id IN (SELECT OpsEntriesId FROM DistributionCatalogOpsDistributionEntry WHERE CatalogsId = {catalogId}) " +
$"AND EntityId IN ({string.Join(",", filterEntries.Select(x => x.EntityId))}))").ToList();
}
I then map those database objects to my domain objects via constructor.
var opsDistributionEntries = new OpsDistributionEntriesFromDatabaseObjects(
_repository.FilteredOpsDistributionEntries(_distCatalogId, _filterEntries));
public class OpsDistributionEntriesFromDatabaseObjects : IOpsDistributionEntries
{
private readonly IOpsDistributionEntries _distribution;
public OpsDistributionEntriesFromDatabaseObjects(IEnumerable<DatabaseObjects.OpsDistributionEntry> distribution)
{
_distribution = new OpsDistributionEntries(distribution.Select(x => new OpsDistributionEntryFromDatabaseObject(x)));
}
public IEnumerator<IOpsDistributionEntry> GetEnumerator()
{
return _distribution.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class OpsDistributionEntryFromDatabaseObject : IOpsDistributionEntry
{
public OpsDistributionEntryFromDatabaseObject(DatabaseObjects.OpsDistributionEntry opsDistributionEntry)
: this(opsDistributionEntry.Id, opsDistributionEntry.TotalCases, opsDistributionEntry.TotalEffectiveWeight, opsDistributionEntry.Provide,
opsDistributionEntry.Freeze,
new OpsFromDatabaseObject(opsDistributionEntry.Entity),
new DrgDistributionsFromDatabaseObjects(opsDistributionEntry.DrgDistribution))
{
}
private OpsDistributionEntryFromDatabaseObject(int id, int totalCases, double totalEffectiveWeight, bool provide, bool freeze, IOps ops,
IDrgDistributions drgDistribution)
{
Id = id;
TotalCases = totalCases;
TotalEffectiveWeight = totalEffectiveWeight;
Provide = provide;
Freeze = freeze;
Ops = ops;
DrgDistribution = drgDistribution;
}
public int Id { get; }
public int TotalCases { get; }
public double TotalEffectiveWeight { get; }
public bool Provide { get; }
public bool Freeze { get; }
public IOps Ops { get; }
public IDrgDistributions DrgDistribution { get; }
}
public sealed class OpsFromDatabaseObject : IOps
{
public OpsFromDatabaseObject(DatabaseObjects.Ops ops) : this(ops.Id, ops.Code, ops.Description, ops.Year)
{
}
private OpsFromDatabaseObject(int id, string code, string description, int year)
{
Id = id;
Code = code;
Description = description;
Year = year;
}
public int Id { get; }
public string Code { get; }
public string Description { get; }
public int Year { get; }
}
I pass the database objects on to different levels, but finally every value is assigned and every possible navigation property is mapped to an domain object.
With those mapped domain objects I recalculate the new "worth" of the service and the correlated "mini-services".
After calculation I again map my Domain Objects to DatabaseObjects.
DatabaseObjects.OpsDistributionEntry ToDatabaseObject() => new DatabaseObjects.OpsDistributionEntry
{
Id = Id,
EntityId = Ops.Id,
Freeze = Freeze,
Provide = Provide,
TotalCases = TotalCases,
TotalEffectiveWeight = TotalEffectiveWeight,
DrgDistribution = DrgDistribution.Select(x => x.ToDatabaseObject()).ToImmutableList(),
};
When I want to add those "updated" Objects to the context via repository
public void UpdateDistributionEntries(IEnumerable<OpsDistributionEntry> opsDistributionEntries)
{
if (opsDistributionEntries == null) throw new ArgumentNullException(nameof(opsDistributionEntries));
_context.OpsDistributionEntries.UpdateRange(opsDistributionEntries);
}
I am getting an Error that the Entities I want to updated are already being tracked by Entity Framework.
After some debugging I think that EF is still tracking the database objects I loaded for mapping the domain objects. I just use the database objects to map values to the domain objects and do not store any reference for them (as far as I understand).
Can any of you maybe tell me why they are still being tracked even if they are "unreachable". Or am I thinking wrong? Might this be because of Lazy Loading?
I've been debugging for almost 14 hours now :D Please someone give me a hint :D
Many thanks in advance

Asp.Net IdentityUser add custom List Property

I am fairly new to coding with asp.net so there might be an obvious answere to my question but I haven't found one yet.
So currently I am developing a site for project management and I want the users to get notified when an event happens, eg. they were added to a new project, a project has been updated etc.
For that I have expanded the IdentityUser Model with a new property List
public class CojectUser : IdentityUser
{
public List<Notification> Notifications { get; set; }
}
public class Notification
{
public int NotificationID { get; set; }
public string Message { get; set; }
public bool Seen { get; set; }
}
When an event happens I add them to the user's notification list and update the user via the userManager.
public class EventBroker<T> : IEventBroker<T>
{
private readonly UserManager<CojectUser> userManager;
public EventBroker(UserManager<CojectUser> userMgr, IUserValidator<CojectUser> userValid)
{
userManager = userMgr;
}
public async Task NotifyAsync(Message<T> message, List<UserRole> recipients)
{
foreach (var user in recipients)
{
var cojectUser = await userManager.FindByNameAsync(user.Name);
if (cojectUser != null)
{
if (cojectUser.Notifications == null)
{
cojectUser.Notifications = new List<Notification>();
}
cojectUser.Notifications.Add(new Notification
{
Message = message.Information,
Seen = false
});
IdentityResult result = await userManager.UpdateAsync(cojectUser);
if (!result.Succeeded)
{
throw new UserUpdateFailException();
}
}
}
}
}
}
I am able to save the custom data to the database, but I am unable to load it again from database.
When I want to display the user's notifications userManager retrieves an user object with null as notification list. Even though the data is stored in database.
public async Task<IActionResult> Index()
{
CojectUser user = await userManager.GetUserAsync(User);
if(user.Notifications == null)
{
user.Notifications = new List<Notification>();
}
return View(user);
}
Data in database:
Can anybody tell me what I am doing wrong?
UserManager don't eager load properties by default.
You should use DatabaseContext directly.
var user = _context.Users.Include(c => c.Notifications).Where(u => u.Id == user.Id).ToList();

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.

Resources