Am modifying the complex function which is already written where they are using the below code :
private List<string> Values()
{
if (ViewBag.Sample == null)
{
ViewBag.Sample = TestData();
}
}
// where TestData() hits the DB and returns corresponding result
Values() is called multiple places in the same file where this will return by hitting the DB TestData() first time and from next calls it will directly return from ViewBag.
Is this is a good approach ?
What are all the alternative approach we have in MVC to handle this scenario ?As DB hit is a costly call we need to use some other techniques.
Thanks
You could either keep your data in session like this:
Session['*your session key*'] = TestData();
And then retrieve it like this:
var myData = Session['*your session key*'] as YourObject //cast it to an object if you need to.
Or you could use caching:
System.Web.HttpRuntime.Cache[cacheKey] = TestData
And retrieving:
var myData =System.Web.HttpRuntime.Cache[cacheKey] as YourObject
That code should ensure that you only touch the database the first time the method is invoked.
If the same data is used on multiple pages you could also have a lot at the Cache- or Session class.
If size of the data retrieved from database is not very big then you can use Cache
Otherwise you can store data in Session as well.
You have the options to keep the data like Session, Cache.
[OutputCache(Duration = 60)] // Caches for 60 seconds
private List<string> Values()
{
if (ViewBag.Sample == null)
{
ViewBag.Sample = TestData();
}
}
MVC Model Binder
See Sample code
Related
I am currently writing an application where I am getting data from the DB and passing it to the object which will later be used to send the data off else where. The issue I am having in testing is that the data doesn't hold in the object as when it is used later, I get null value errors. I know we can store these in sessions, but I also know you can use objects and have done it before at previous jobs, but do not recall what I am missing to maintain the information. Would I need to pass the object(s) from method to method until the job is done?
the first sample here shows where it is prepping the object.
public void FtpInitialize()
{
_LogController.LogToFile(ValidateMessage.BeginInitialization);
//Loading FTPClient object with ClientInfo Object data we got from DataBase. FTPClient object is the object that sends the data.
_LogController.FTPTraceLogToFile();
ClientInfo = _RepositoryController.GetClientInfo(ClientInfo);
if (ClientInfo == null)
ClientInfo.ClientDataExists = false;
else
{
FTPClient.Host = ClientInfo.Host;
FTPClient.Port = ClientInfo.Port;
FTPClient.EncryptionMode = FtpEncryptionMode.Explicit;
FTPClient.Credentials = new NetworkCredential(ClientInfo.UserName, ClientInfo.Password);
FTPClient.DataConnectionType = FtpDataConnectionType.EPSV; //according library documentation, auto passive is broken as of 10/20/2016
FTPClient.EnableThreadSafeDataConnections = ClientInfo.EnableThreadSafeConnection;
FTPClient.DataConnectionConnectTimeout = ClientInfo.DataConnectionTimeout;
FTPClient.DataConnectionReadTimeout = ClientInfo.DataReadTimeout;
FTPClient.ConnectTimeout = ClientInfo.ConnectionTimeout;
FTPClient.ReadTimeout = ClientInfo.ReadTimeout;
FTPClient.SocketPollInterval = ClientInfo.SocketPollInterval;
FTPClient.SocketKeepAlive = ClientInfo.KeepSocketAlive;
FTPClient.ValidateCertificate += new FtpSslValidation(OnValidateCertificate);
_LogController.LogToFile(ValidateMessage.ClientDataLoaded);
ClientInfo.ClientDataExists = true;
}
}
then below when I hit this part, it shows as null
public bool SendData()
{
short errorCount = 0;
using (FTPClient)
{
if (ClientInfo.ClientDataExists)
{
This looks like you are using local variables in your methods. This is why the data "disappears".
For example if you are pulling data from your database you should have some kind of model class and container so that the data can persist until you are done with it.
EX: You have a database that contains information on people (name, dob, etc).
You should have a class that defines person and possibly a List to store said people. You call your database and pull X person objects into your List variable. You then can pass your List into the methods. Note: You can ditch the List (or whatever collection you use) if you are doing it one at a time.
The implementation of this really depends on how you want the objects to persist. If you know you will only pull one person object at a time you can declare var databasePerson from the database. Use the List if you will be pulling more information and then pop off the object when your are down with it.
Hope that helps.
I have a dropdown menu that when you select an option value submit the form, and to avoid repetitive database calls I am storing my non-sensitive object in a session.
private List<Employee> stafflist
{
get { return Session["stafflist"] as List<Employee>; }
set { Session["stafflist"] = new Employee(); }
}
private void RemoveStaff()
{
Session.Remove("stafflist");
}
however in my
[HttpPost]
public ActionResult index (...)
{
//why can't I get the list of staff like this?
ViewBag.staff=stafflist.Where(..).toList();
//is the below still needed? i thought i
//have a session variable declare above,
//and to avoid 30x repetitive db calls?
//also note when i include the below the code runs fine,
//however, if i take it out it doesn't. i would like to avoid repetitive db calls
stafflist=db.Employee.toList();
}
First of all, you should not prevent to query the database. Proper caching is hard to get right, and a database is perfectly capable of performing queries and caching data.
If you're absolutely sure you want to circumvent the database, and query clientside (i.e. in the controller) then you need to pull the entire staff list from the database at least once per visitor.
You could do that in the first GET call to this controller, assuming the user will always visit that:
[HttpGet]
public ActionResult Index (...)
{
var cachedStaff = db.Employee.toList();
Session["stafflist"] = cachedStaff;
}
Then in the POST, where you actually want to do the database query (again, consider letting the database do what it's good at), you can query the list from the session:
[HttpPost]
public ActionResult Index (...)
{
var cachedStaff = Session["stafflist"] as List<Employee>();
// TODO: check cachedStaff for null, for when someone posts after
// their session expires or didn't visit the Index page first.
var selectedStaff = cachedStaff.Where(..).ToList();
// the rest of your code
}
Then the property you introduced can be used as syntactic sugar to clean up the code a bit:
private List<Employee> CachedStaff
{
get { return Session["stafflist"] as List<Employee>; }
set { Session["stafflist"] = value; }
}
[HttpGet]
public ActionResult Index (...)
{
CachedStaff = db.Employee.toList();
}
[HttpPost]
public ActionResult Index (...)
{
// TODO: this will throw an ArgumentNullException when
// the staff list is not cached, see above.
var selectedStaff = CachedStaff.Where(..).ToList();
// the rest of your code
}
A session is unique for the current user and the current session. That means that when the user closes the browser, the session information is lost. The session is also lost if the session cookie is removed. Read about state management.
If you want to have a global staff list that is available for all users you need to use something else. Caching is the most common case then.
you probably have it already figured it out, just in case I leave here what it worked for me.
First you create a new session variable based on an object created (in this case the object usr will be empty):
User usr = new User();
Session["CurrentUSR"]=usr;
where you want to use the new object, you will have to cast the session variable and point it to a new object created in that particular page:
User usr= new User(); //at this point the usr object is empty, now you are going to replace this new empty object with the session variable created before
usr=Session["CurrentUSR"] as User();
In case you have a list, the best course of action would be to create a List<> of that particular object.
I am getting users and their data from external webservice. I cache those items because I don't want to hit web service every time. Now, If user update any of their information, I am saving it through webservice. But I don't want to get the latest data from web service as it takes lot of time. Instead I want to update my cache. Can I do that ? If so, what would be the best way ? Here is my Code
List<User> users = appSecurity.SelectUsers();
var CacheKey = string.Format("GetUserList_{0}", currentUser);
CacheFactory.AddCacheItem(CacheKey, users, 300);
CacheFactory is a class where I handle Adding, Clearing and Removing cache. Below is the code
public static void RemoveCacheItem(string key)
{
Cache.Remove(key);
}
public static void ClearCache()
{
System.Collections.IDictionaryEnumerator enumerator = Cache.GetEnumerator();
while (enumerator.MoveNext())
{
RemoveCacheItem(enumerator.Key.ToString());
}
}
public static void AddCacheItem<T>(string key, T value, double timeOutInSeconds)
{
var Item = GetCacheItem<T>(key);
if (Item != null)
{
RemoveCacheItem(key);
Item = value;
}
Cache.Insert(key, value, null, DateTime.Now.AddSeconds(timeOutInSeconds), System.Web.Caching.Cache.NoSlidingExpiration);
}
The answer is yes, it can be done. It can also be done in many different ways depending on what you want to solve. At the basic level you can create a cache by using a List<T> or Dictionary<T,T> to store your data.
When you get information from the external web-service, you push the data into your List or Dictionary. You can then use that data throughout your application. When you need to update that cache, you update the value in the List/Dictionary.
You can update your dictonary like so
Dictionary<string, int> list = new Dictionary<string, int>();
then you can set the value for the key "test" as follows
list["test"] = list["test"] + 1;
When you are ready to push the updated data to the external source. All you need to do is properly parse that data into the format the source is expecting and send away.
Like I said there are many different ways to do this, but this is a basic sample way to accomplishing it. You can use this example to build off and go from there.
I basically have created a class which when a user logs into a website it then queries the database and stores some settings in a List (So I have key/pair values).
The reason for this is because I want to always be able to access these settings without going to the database again.
I put these in a class and loop through the fields via a SQL query and add them to the list.
How can I then access these variables from another part of the application? or is there a better way to do this? I'm talking server side and not really client side.
Here is an example of what I had at the moment:
public static void createSystemMetaData()
{
string constring = ConfigurationManager.ConnectionStrings["Test"].ConnectionString;
SqlConnection sql = new SqlConnection(constring);
sql.Open();
SqlCommand systemMetaData = new SqlCommand("SELECT * FROM SD_TABLES", sql);
//Set Modules
using (SqlDataReader systemMetaDataReader = systemMetaData.ExecuteReader())
{
while (systemMetaDataReader.Read())
{
var name = systemMetaDataReader.GetOrdinal("Sequence").ToString();
var value = systemMetaDataReader.GetOrdinal("Property").ToString();
var Modules = new List<KeyValuePair<string, string>>();
Modules.Add(new KeyValuePair<string, string>(name, value));
}
}
}
Thanks
Any static properties of a class will be preserved for the lifetime of the application pool, assuming you're using ASP.NET under IIS.
So a very simple class might look like:
public static class MyConfigClass
{
public static Lazy<Something> MyConfig = new Lazy<Something>(() => GetSomethings());
public static Something GetSomethings()
{
// this will only be called once in your web application
}
}
You can then consume this by simply calling
MyConfigClass.MyConfig.Value
For less users you can go with the SessionState as Bob suggested,however with more users you might need to move to a state server or load it from Data Base each time.
As others have pointed out, the risk of holding these values in global memory is that the values might change. Also, global variables are a bad design decision as you can end up with various parts of your application reading and writing to these values, which makes debugging problems harder than it need be.
A commonly adopted solution is to wrap your database access inside a facade class. This class can then cache the values if you wish to avoid hitting the database for each request. In addition, as changes are routed through the facade too, it knows when the data has changed and can empty its cache (forcing a database re-read) when this occurs. As an added bonus, it becomes possible to mock the facade in order to test code without touching the database (database access is notoriously difficult to unit test).
From the looks of things you are using universal values irrespective of users so an SqlCacheDependency would be useful here:
Make sure you setup a database dependency in web.config for the name Test
public static class CacheData {
public static List<KeyValuePair<string,string>> GetData() {
var cache = System.Web.HttpContext.Current.Cache;
SqlCacheDependency SqlDep = null;
var modules = Cache["Modules"] as List<KeyValuePair<string,string>>;
if (modules == null) {
// Because of possible exceptions thrown when this
// code runs, use Try...Catch...Finally syntax.
try {
// Instantiate SqlDep using the SqlCacheDependency constructor.
SqlDep = new SqlCacheDependency("Test", "SD_TABLES");
}
// Handle the DatabaseNotEnabledForNotificationException with
// a call to the SqlCacheDependencyAdmin.EnableNotifications method.
catch (DatabaseNotEnabledForNotificationException exDBDis) {
SqlCacheDependencyAdmin.EnableNotifications("Test");
}
// Handle the TableNotEnabledForNotificationException with
// a call to the SqlCacheDependencyAdmin.EnableTableForNotifications method.
catch (TableNotEnabledForNotificationException exTabDis) {
SqlCacheDependencyAdmin.EnableTableForNotifications("Test", "SD_TABLES");
}
finally {
// Assign a value to modules here before calling the next line
Cache.Insert("Modules", modules, SqlDep);
}
}
return modules;
}
I am running a web site in ASP.NET/C#/SQL Server 2012 that needs to cache the result of some stored procedure queries. The result should have an absolute expiration. What options are there to do this?
Preferably setting command.ExpirationDateTime = DateTime.Now.AddMinutes(10) would be great, but as far as I know nothing like that is possible.
Edit:
The data will be returned from an API, so caching using pages or user controls is not possible.
Have a look at the Enterprise Library Caching Application Block. This has the exact functionality you are looking for
The Caching Application Block
cache.Add(listid.ToString(), list, CacheItemPriority.Normal, null,
new SlidingTime(TimeSpan.FromMinutes(60)));
I don't understand you restriction on where you can actually perform caching, but I assume you'll have access to HttpRuntime.Cache? If that's the case, I have written a series of utilities for caching service responses in a blog post (Caching Services - The Lazy Way).
The basics of this utility is you can do:
string cacheKey = GenerateCacheKey(myParam); //most likely a derivative of myParam
if (Cache.IsInCache<MyResultType>(cacheKey))
{
return Cache.GetFromCache<MyResultType>(cacheKey);
}
var result = GetMyRequestedResult(myParam);
if (result != null) //or whatever makes sense
{
Cache.InsertIntoCacheAbsoluteExpiration(cacheKey, result, DateTime.Now.AddMinutes(0));
}
return result;
If you have any services in between, the post shows a cute class for interacting/caching with those services.
I ended up creating a hash from the SqlCommand by merging the command text with the parameter names and values. That hash I used as a cache key when putting/getting stuff in/from the HttpContext.Current.Cache object. Works fine. Probably not super fast, but since some queries are somewhat much slower it is all ok.
You can also use System.Runtime.Caching.ObjectCache starting from .Net Framework 4 not only in web applications. Here is an example:
List<EmailData> result = null;
ObjectCache cache = MemoryCache.Default;
var key = string.Concat(title, ":", language);
var item = cache.GetCacheItem(key);
if (item != null)
return item.Value as List<EmailData>;
using (var connection = _connectionFactory.OpenConnection())
{
result = connection.Query<EmailData>(sql, new { title, language }).ToList();
}
var cachingPolicy = new CacheItemPolicy
{
AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(_cacheExpirationIntervalInMinutes)
};
cache.Set(new CacheItem(key, result), cachingPolicy);
return result;
You can read more: https://msdn.microsoft.com/en-us/library/system.runtime.caching.objectcache(v=vs.110).aspx