How to avoid redundant second query to database with using MVVM pattern on view model:
public class DataFormViewModel : INotifyPropertyChanged
{
private int companyId
public int CompanyId
{
get { return companyId; }
set
{
companyId = value;
RaisePropentyChanged("FindingStatuses");
RaisePropentyChanged("StatusCount");
}
}
public List<FindingStatus> FindingStatuses
{
get
{
return FindingStatusService.GetAvalableStatuses(CompanyId);
}
}
public int StatusCount
{
get { return FindingStatuses.Count; }
}
}
i.e. when CompanyId was changed by DataBinder FindingStatuses will be executed and then StatusCount will be executed, that will execute FindingStatuses again.
I'm not sure I'd bind the property directly to a database operation in the first place. Why not have a local List<FindingStatus> representing the "last fetched" statuses, and then explicitly refresh it?
Apart from anything else, property access is usually expected to be reasonably cheap - making a database call every time you access either of those properties sounds like a bad idea to me.
Like Jon already mentioned, accessing properties are expected to be cheap, something you can do a thousand times without any sideeffect.
I would cache the result of your database access and return the cached object on any following request. Ie
private IList<FindingStatus> _findingStatuses;
public IList<FindingStatus> FindingStatuses
{
get
{
if (_findingStatuses == null)
{
_findingStatuses = FindingStatusService.GetAvalableStatuses(CompanyId);
}
return _findingStatuses;
}
}
And then you would of course have to clear your cache before raising the notification
public int CompanyId
{
get { return companyId; }
set
{
companyId = value;
_findingStatuses = null;
RaisePropentyChanged("FindingStatuses");
RaisePropentyChanged("StatusCount");
}
}
The best way to avoid multiple (and useless) queries to the database, is implement a simple cache layer in the Data Access Layer.
1- Ask the cache if he already has an updated result
2- Else query the database
Here is a cache class you can try: http://www.codeproject.com/KB/recipes/andregenericcache.aspx
Related
This is actually 2 questions in one.
I have an asp.net mvc application where I have to load a list of Modules, its just a simple list with ID, modulename and a class name to render it on the view with font awesome.
My model is like this:
public class Module
{
[Key]
public int Id { get; set; }
public string ModuleName { get; set; }
public string FontAwesomeClass { get; set; }
}
Because the module list is a Partial View that will render some icons on the top navigation bar, I dont want that for each refresh of the app, it goes to the DB, so it must be cached(I am using Azure REDIS Cache, not relevant for the question anyway), so instead of calling the DB context directly from the controller, I am calling a Cache Class that will check if the cache object exists, if not it will retrieve it from DB, if it does, it will return it from cache.
This my solution structure:
http://screencast.com/t/uayPYiHaPCav
Here is my controller Module.cs
public ActionResult GetModules()
{
return View(Cache.Module.GetModules());
}
As you can see the Controller does not have any logic where to get the data from.
Here is the Module.cs (on the Cache Namespace)
public class Module
{
private AppDataContext dbApp = new AppDataContext();
//Load modules from cache or from database
public static List<Models.Module> GetModules()
{
IDatabase cache = Helper.Connection.GetDatabase();
List<Models.Module> listOfModules = (List<Models.Module>)cache.Get("Modules");
if (listOfModules == null)
{
return dbApp.ModuleList.ToList();
}
else
{
return listOfModules;
}
}
}
Here I have a compiler error which I am not sure how to best fix it:
Error CS0120 An object reference is required for the non-static field,
method, or property 'Module.dbApp'
So that was my first question.
The 2nd question is more about the design pattern, do you consider this correct or not? the way I am trying to get the data from Cache, and its actually the Cache class which checks if data is on it or if it has to go to the DB.
First Question: make your private member static
private static AppDataContext dbApp = new AppDataContext();
2nd Question: your cache strategy seems pretty standard. The only thing is that you might want to expire cache data. For example, the cached data can get old and the longer it stays in the cache the older it gets. You might at some point want to expire it and get fresh data again.
Update:
#EstebanV for code sample (this off the top of my head, don't assume that it compiles):
/**
ICachedPersonDao abstracts away the caching mechanism
away from the core of your application
**/
public CachedPersonDao : ICachedPersonDao
{
private IPersonDao personDao = null;
public CachedPersonDao(IPersonDao personDao)
{
this.personDao = personDao;
}
public Person GetPersonById(int id){
bool isInCache = CACHE.SomeFunctionThatChecksInYourCache(id);
if (isInCache)
{
return CACHE.SomeFunctionThatReturnsTheCachedPerson(id);
}
else
{
//Well it's not in the cache so let's get it from the DB.
return this.personDao.GetPersonById(id);
}
}
}
/**
IPersonDao abstracts database communication
away from the core of your application
**/
public class PersonDao : IPersonDao
{
public Person GetPersonById(int id)
{
/** Get the person by id from the DB
through EntityFramework or whatever
**/
}
}
Usage:
In your controller, use ICachedPersonDao if you want to attempt to get from cache or use IPersonDao if you want to get it directly from the database without checking the cache.
Like I said, you should learn Dependency Injection it will help "inject" these dependencies into the classes that uses them.
I say again, this is off the top of my head. It won't compile. It's just to illustrate the concept.
When my user in the students Role login to the system, he can select various classes that he's enrolled. I already have a filter that'll redirect him to the select class page so he must select a class to access the system, and change it anytime he wants and the whole system's context will change.
As for now, i'm storing IdClass in the session variable, using the code below, and the system uses it to filter all the related queries and functions, like showing all the lessons from the current class. My question is: is this a good practice? Is this right or is there any better and efficient way? I'm trying to follow patterns.
[Serializable]
public sealed class Session
{
private const string SESSION_FOO = "STUDYPLATFORM_GUID";
private Session()
{
this.IdClass= 0; // Construct it to 0 so it evaluate as there's no Class selected.
}
/* This is the session's public IdClass that
i can get and set throughout the application. */
public int IdClass { get; set; }
public static Session Current
{
get
{
if (HttpContext.Current.Session[SESSION_FOO] == null)
{
HttpContext.Current.Session[SESSION_FOO] = new Session();
}
return HttpContext.Current.Session[SESSION_FOO] as Session;
}
}
}
I'm fairly new to WPF and I've been reading many tutorials and yet while I could find many guides that showed how to bind data to textboxes and such, I couldn't find anything about navigating such data through back/forward/etc. buttons.
This is my current situation: I have a Customer class containing data on a single customer and a Customers class which is an ObservableCollection of customer.
Then data is loaded from an sqlite database (and this opens another can of worms because I don't know the exact approach for working this out but it doesn't really pertain to the current issue since I more or less got it to work) and every customer is added to the collection.
Then in the ViewModel for the main form I have the following stuff:
private Customer _objCustomer;
private Customers _customers;
private Customer _selectedCustomer;
public Customer Selection { get { return _selectedCustomer; }
set
{
if (object.ReferenceEquals(value, _selectedPartecipante)) { return; }
_selectedCustomer = value;
base.OnPropertyChanged("Selection");
}
}
public Customers customers { get { return _partecipanti; }
set { _customers = value; base.OnPropertyChanged("customers"); } }
public Customer customer { get { return _objCustomer; }
set { _objCustomer = value; base.OnPropertyChanged("customer"); } }
public string Name { get { return _objCustomer.Name; } set { _objCustomer.Name = value; base.OnPropertyChanged("Name"); } }
public int Id { get { return _objCustomer.Id; } }
public SubscriptionsViewModel()
{
_customers = Customers.LoadCustomers(); //This one loads the items from the database
_objCustomers = _customers.First();
_selectedCustomer = _objCustomer;
}
This is probably wrong but I still can't find a way to fix it, what am I supposed to work to get navigation working?
And how do I get the data in the current record to be saved when pressing a certain button on the form?
Try to put the exact property in OnPropertyChanged("Customers");
In your case, the property is lower case.
Lets imaging the we have model:
public class InheritModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string OtherData { get; set; }
}
We have a controller with View, that represents this model:
private InheritModel GetAll()
{
return new InheritModel
{
Name = "name1",
Description = "decs 1",
OtherData = "other"
};
}
public ActionResult Index()
{
return View(GetAll());
}
Now we can edit this in View, change some data and post in back to server:
[HttpPost]
public ActionResult Index(InheritModel model)
{
var merged = new MergeModel();
return View(merged.Merge(model, GetAll()));
}
What i need to do:
In view we have a reproduction of model
User change something and post
Merge method need to compare field-by-field posted model and previous model
Merge method create a new InheritModel with data that was changed in posted model, all other data should be null
Can somebody help me to make this Merge method?
UPDATE(!)
It's not a trivial task. Approaching like:
public InheritModel Merge(InheritModel current, InheritModel orig)
{
var result = new InheritModel();
if (current.Id != orig.Id)
{
result.Id = current.Id;
}
}
Not applicable. It's should be Generic solution. We have more than 200 properties in the model. And the first model is built from severeal tables from DB.
public InheritModel Merge(InheritModel current, InheritModel orig)
{
var result = new InheritModel();
if (current.Id != orig.Id)
{
result.Id = current.Id;
}
if (current.Name != orig.Name)
{
result.Name = current.Name;
}
... for the other properties
return result;
}
Another possibility is to use reflection and loop through all properties and set their values:
public InheritModel Merge(InheritModel current, InheritModel orig)
{
var result = new InheritModel();
var properties = TypeDescriptor.GetProperties(typeof(InheritModel));
foreach (PropertyDescriptor property in properties)
{
var currentValue = property.GetValue(current);
if (currentValue != property.GetValue(orig))
{
property.SetValue(result, currentValue);
}
}
return result;
}
Obviously this works only for 1 level nesting of properties.
Per topic, it seems that what you want is a sort of "change tracking" mechanism which is definitely not trivial or simple by any means. Probably, it makes sense to use any modern ORM solution to do that for you, does it?
Because otherwise you need to develop something that maintains the "context" (the 1st level object cache) like EF's ObjectContext or NH's Session that would be generic solution.
Also, there is no information on what happens at the lower level - how do you actualy save the data. Do you already have some mechanism that saves the object by traversing it's "non-null" properties?
I have a similar project experience, which made me thought a lot about the original design. Think the following question:
You have a view that representing a model, then users modified
something of the model in the view, all the CHANGES are posted to
server and the model is modified, and then it's saved to database
probably. What's posted to the server on earth?
An instance of InheritModel? No. You want the changes only. It's actually part of InheritModel, it's a InheritModel Updater, it's an instance of Updater<InheritModel>. And in your question you need to merge two models, because your Update method looks like:
public InheritModel Update(InheritedModel newModel)
{
//assign the properties of the newModel to the old, and save it to db
//return the latest version of the InheritedModel
}
Now ask yourself: why do I need a whole instance of InheritedModel when I just want to update one property only?
So my final solution is: posting the changes to the controller, the argument is something like a Updater<TModel>, not TModel itself. And the Updater<TModel> can be applied to a TModel, the properties metioned in the updater is assigned and saved. There shouldn't a MERGE operation.
I am sure that I have made some painfully obvious blunder(s) that I just cannot see. I am hoping one of you can set me straight.
I my session management is working perfectly except that if a user on one machine enters data, a user who starts a session on another machine will also retreive the session information from the first. Not so good. :(
I call my sessions like this:
UserInfo userinfo = UserInfo.Session;
My session mgt class uses this:
static UserInfo userInfo;
static public UserInfo Session
{
get
{
if (userInfo == null)
{
userInfo = new UserInfo();
userInfo.ResetSessionTime();
}
return userInfo;
}
}
I read and write the data like this. I realize that I could serialize the entire class, but it seems like a lot more overhead to serialize and deserialize an entire class each time the class is called as opposed to just grabbing the one or two items I need.
Decimal _latitude;
private String SessionValue(String sKey, String sValue, String sNewValue)
{
String sRetVal = "";
if (sNewValue == null)//not wanting to update anything
{
if (sValue == null)//there is no existing value
{
sRetVal = (String)System.Web.HttpContext.Current.Session[sKey];
}
else
{
sRetVal = sValue;
}
}
else
{
System.Web.HttpContext.Current.Session[sKey] = sNewValue;
sRetVal = sNewValue;
}
return sRetVal;
}
public Decimal Latitude
{
get { return SessionValue("Latitude", _latitude); }
set { _latitude = SessionValue("Latitude", _latitude, value); }
}
Thanks for your help
1) You're using statics for your UserInfo, which means that a single instance of this class is shared among all requests coming to your web server.
2) You're not only storing values in the session (which isn't shared among users) but also in an instance variable, which in this case WILL be shared among users.
So the value of _latitude is causing you this issue. A simple solution is this:
public class Userinfo
{
public Decimal Latitude
{
get { return System.Web.HttpContext.Current.Session["Latitude"]; }
set { System.Web.HttpContext.Current.Session["Latitude"] = value; }
}
}
A better, more testable version would be:
public class UserInfo
{
private HttpSessionStateWrapper _session;
public UserInfo(HttpSessionStateWrapper session)
(
// throw if null etc
_session = session;
)
public Decimal Latitude
{
get { return _session["Latitude"]; }
set { _session["Latitude"] = value; }
}
}
In the second instance, within a request you just construct a new instance of the HttpSessionStateWrapper (using the current Session) and pass it to the UserInfo instance. When you test, you can just pass in a mock Wrapper.
No matter what, the UserInfo instance shouldn't be shared among sessions and it should write and read directly from the Session. Don't try to prematurely optimize things by keeping local versions of your session values. You aren't saving any time and you're just opening yourself up to bugs.
This happens because you store your user info in a static field. Static instances are shared between all requests, and lives the entire lifetime of your application.
In other words, all your users will get the same UserInfo instance from UserInfo.Session.
To fix this you could:
Serialize the whole class into session. I don't know which other properties you have, but I would guess it would not be too much of an overhead.
Create an instance of UserInfo per request, so that the user always reads from a new instance, which in turn will refresh it's values from Session.