Now I know that this question will be a little scarce on details, but this is because I first want to tey to avoidgiving out too much code and info about a company project.
Suffice it to say: I am working on a blazor serverside application website.Its a relativly simple Site, on which Users can log on and have a User Page and Course selection Page, on which they can see courses and their appointments. If they click on the appointments they have "made" that appointment.
Now in practice everything is basically working but due to company restrictions I had to do some workarounds here and there and I want to change something that bothers me greatly. Just so you know: For our DB requests, we cannot use EF or anything similar. We are also told to avoid having a DB request everytime a user enters a Page. So we basically have to "preload" all data. Due to the fact that I wasnt quite sure at the beginning how server apps worked storage wise,I made a Scoped UserCache service, that makes a request to the DB when the User is logged in and then fills its Lists with all relevant data.So the Service has a List of all appointments, Courses and what have you. The Pages the use the Lists of that Service to get their Data and do their thing.
Now I think we can all see some issues with this. Like for example if a user opens multiple tabs after login, more and more services with filled lists get created.Its also just not required, since its all stored serverside anyways, so the Login restriction doesnt add to security.
So I am thinking about changing the Scoped Service to a Singleton MainCache Service which means that the moment I boot the Site a DB request is made, the Lists get filled and the usage for the Pages stays roughly the same. (Dont worry I have a DB watcher so if changes on the DB occour the Site notices, and applies the changes to the lists). But now I am kinda scared of what I for lack of a better term would call threading issues. This change would mean that everyone is using the same Lists and objects. Now the DB isnt massive and there wont be many overlapping edits.
The edits also dont go into the object directly, but once confirmed its end to the DB which then adjusts its tables and THEN the Site adapts its objects.The only time Users "edit" something is when they click on the appointments for getting in and create accounts or chnage their passwords.
Admins can make slightly more edits like creating a Course and Its appointments and editing the Details of them, but that SHOULD not actually happen whilstthe Site is under "active" usage. Basically at the start of a timeframe all Corses and Appointments should stand and edits are only made if someone messed up.There will also be one Admin for now meaning that there wont be Admins getting in eachothers way during editing.
We will have 6 Courses at most, with each course having 3 or 4 appointments at most (5 if it really comes to it)
If we get lucky we will get our hands on 13 students for the duration of the event and after the appointments are over, we get rid of the student accounts(after a bit of buffer time) and the appointments.
So all in all there wont be that much data getting in the way. But Id still like to her from you guys, if I should have threading worries, and or if I should dosomething completly different.
But to give you a bit of Code to work withHere you get a bit of the Cache Service
public ModelService<User> UserService { get; set; }public ModelService<RoleLevel> RoleService { get; set; }public ModelService<Course> CourseService { get; set; }public ModelService<Appointment> AppointmentService { get; set; }public ModelService<JoinedUserAppointments> JoinService { get; set; }Theese ModelServices are Singletons already, since we only use their methods. They dont hold lists or data to edit.
Each of theese Services gets a corresponding List. The JoinsService represents a n-m relation Table.Each table has a Model, and all Models share a interface and Baseclass, so that I can write my Methods generic.
we have a ModelChangedEvent which takes this as an arg
public class ModelChangedEventArgs<T> : EventArgs where T : ModelLayout, IModelLayout<T>{ public T NewVal { get; set; } public T OldVal { get; set; } public ModelChangedEventArgs(T newVal, T oldVal) { NewVal = newVal; OldVal = oldVal; }}And here is the Method in the Cache taht reacts to DB Changes
private void ModelChanged<T>(object sender, ModelChangedEventArgs<T> args) where T : ModelLayout, IModelLayout<T>, new() { ILog log = _logger.Log().Start(sender, args); try { //this simply gets me the List I need. So if T is User it gets me the List of all Users. List<T> masterList = GetMasterList<T>(); T? foundEntry = masterList.FirstOrDefault(m => m.Id == args.NewVal.Id); if (foundEntry == null) { T newVal = new(); newVal.Adapt(args.NewVal, this); masterList.Add(newVal); } else { if (args.NewVal.DeletedDate != null) { masterList.Remove(foundEntry); } foundEntry.Adapt(args.NewVal, this); } OnCacheChanged?.Invoke(); } catch (Exception e) { log.Error(e); } log.End(); }As you can see, its written as generics. The Reason I give Adapt the foundEntry parameter, is that args.NeVal isnt the original object and also doesnt have "calculated" values filled in like for example the NewVal for a User might have the RileID but not the Role object attributes filled in.