I'm new to the concept of caching data and I wanted to ask if there are any issues I"m overlooking with the strategy I"m building for caching data in my MVC web application from my database. I want to stress that basically the data I'm looking at caching is practically read only, and would only be updated very very infrequently (coinciding with code upgrades where the app would be being refreshed).
From what I have been able to research I was planning on using a static class to serve as a helper for building and maintaining the cache. The below code shows how I would build the cache from a database table which would contain the different baseball card condition grades (Mint, Near Mint, etc)
Static CacheHelper class:
public static class CacheHelpers
{
private static HashSet<baseballcardcondition> _CardConditionsCache = null;
public static HashSet<baseballcardcondition> CardConditionsCache
{
get
{
if (_CardConditionsCache == null)
{
_CardConditionsCache = (HttpContext.Current.Cache["CardConditionsCache"] as HashSet<baseballcardconditions>);
if (_CardConditionsCache == null)
{
mydbEntities db = new mydbEntities();
_CardConditionsCache = new HashSet<baseballcardconditions>(db.baseballcardconditions);
HttpContext.Current.Cache.Insert("CardConditionsCache", _CardConditionsCache);
}
}
return _CardConditionsCache;
}
set
{
HttpContext.Current.Cache.Insert("CardConditionsCache", _CardConditionsCache);
}
}//public static HashSet<baseballcardconditions> CardConditionsCache
}//public static class CacheHelpers
Later I would be using this data to build a formatted string for a jqGrid grid (a jQuery javascript plugin for displaying tabular data) and would access it via:
//cardConditionSelectListHelperString
string tempString = "";
foreach (var keyPair in CacheHelpers.CardConditionsCache)
{
tempString += keyPair.IdBaseballCardCondition + ":" + keyPair.Condition + ";";
}
Does this look like a solid, robust, and thread safe way to manage my cache for my MVC web application? I would probably expand my CacheHelpers class to have other cached Hashsets so I don't have to hit the database for this read only, basically static data.
Thank you in advance.
"Does this look like a solid, robust, and thread safe way to manage my cache for my MVC web application? "
No, it doesn't. For example how do you keep the size of your data low? You should flush the unused data from the cache (check this: http://msdn.microsoft.com/en-us/library/dd632018.aspx). In addition it is not thread safe.
I think create an own cache solution is the typical "re-inviting the wheel" scenario, there are a lot of cache (even distributed cache) on the market - in addition some of them are free. A good cache solution is much more complex than just start using some static field.
Related
I have a project that is already developed using dataset in Visual Studio, and I want to use the same datasets for CRUD transactions for my newly developed web app.
When two users try to add a new record exactly at the same time, it happens that the record is inserted into another database. For instance instead of saving that record in table x of DB1, it is saved in table x of db2.
Here is the code for instantiating the dataset:
[System.Web.Http.HttpOptions]
[System.Web.Http.HttpPost]
[System.Web.Http.Route("api/PayAndReceive/AddForm")]
public string AddForm([FromBody] FormList MdlObject)
{
clsConnectionString clnn = new clsConnectionString(MdlObject.BaseParams[0].ToString().Trim(), MdlObject.BaseParams[1].ToString());
Z.DAL.DataSetAlls.D1DataTable datatable1 = new Zeus.DAL.DataSetAlls.D1DataTable();
Z.DAL.DataSetAllsTableAdapters.D1TableAdapter adapter1 = new Z.DAL.DataSetAllsTableAdapters.D1TableAdapter();
Here is the class:
public class clsConnectionString
{
public string ConnectionStringIS = "";
private void SetConnectionString(string CorporationID, string FisCalYear)
{
string ServerName = System.Configuration.ConfigurationManager.AppSettings["ServerName"].ToString();
string ISDbName = "DB" + "" + CorporationID + "" + FisCalYear;
ConnectionStringIS = "Data Source=" + "" + ServerName + ";" + "Initial Catalog =" + "" + ISDbName + ";" + "Integrated Security = True" + ";";
Z.DAL.SetConnectionString cls = new Z.DAL.SetConnectionString();
cls.setISDBConnectionString =
}
}
There are other places in the code that same table and adapter is created and I have added setting connection string before all of them, in order to make sure they are using correct database for table creation.
Is it a correct way of creating a datatable in an api?
Is it ok to have ConnectionStringIS as a public variable or it may make a problem when it is used in an api?
Does dataTable1 and Adapter1 will be connected to new db when another api call happens and that causes the issue?
Any advice would be appreciated.
any chance you using a static class for this?
You can't make/create/set/have/use a static class for persisting this data set.
You can certainly have a whole bunch of static helper routines, and a libary of code as static and that is fine.
but, you certainly don't want to have nor use a static class for connections, or the datasets.
So, you posted this:
Z.DAL.DataSetAlls.D1DataTable datatable = new
Zeus.DAL.DataSetAlls.D1DataTable();
Z.DAL.DataSetAllsTableAdapters.D1TableAdapter adapter = new
Z.DAL.DataSetAllsTableAdapters.D1TableAdapter();
The above looks fine, as LONG as you not attempting to persit the dataset in a static class. Static classes are shared among all users of the web site.
So, say in a page on load, if the above code is say in that page class, then you are fine.
However, if the above code is global - in some static class, then you can't do that, since static class(s) if global persisted are shared amoung all users.
So, say I want to fill a grid view? (and I not using the pre Entitfy Framework dataset designer, or the newer EF data designer)?
Then I am free to do this:
Say, I want to load a gridview, then this is fine:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadGrid();
}
void LoadGrid()
{
string strSQL = "SELECT * FROM People ORDER BY Family_ID, FamilyType DESC";
SqlCommand cmdSQL = new SqlCommand(strSQL);
GridView1.DataSource = General.MyRstP(cmdSQL);
GridView1.DataBind();
}
so, in above, note how I don't crete a "instance" of class General. "Genreal" is my shared libary code - all kinds of helper routines in that static class.
So, MyRstP is this:
public static DataTable MyRstP(SqlCommand cmdSQL)
{
DataTable rstData = new DataTable();
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
using (cmdSQL)
{
cmdSQL.Connection = conn;
conn.Open();
rstData.Load(cmdSQL.ExecuteReader());
}
}
return rstData;
}
Again, note how that routine creates a connection object. But, as such, it not global to the class "general", and thus it is multi-user safe.
So, your posted code looks fine, but only if that posted code is not part of a static class that attempts to persist the values global to that class.
And it not all that clear why you have more then one connection to the database, and that such connections are "different" for each user. That's not a normal approach here, and especially when using dataset designer, or the newer Entity Frameworks. They REALLY do not work if you design assuming is to have multiple connections for that EF or dataset designer active at the same time.
In other words?
You really can't on the fly change the active connection for the dataset designer, or the EF designer since such connections are going to be persisted, and you can't have multiple active connections at the same time.
as a FYI:
The term and use of "dataset" in above has ZERO ZERO to do with the type object called datatable, or dataset (which is a collection of tables).
While it is VERY but VERY rare to have multiple active connections for the SAME tables in a given applcation? You can certainly have multiple database connections active, but not for a single given EF and not for the DS designer.
You can get away with such a less than ideal design for desktop applications, since each desktop has their own copy of running code. On a web site, you have ONE copy of the code, and all logged on users share that code. A new instance of running code is not created for each new user who logs into the site.
So any public vars to a static class are in fact shared among all users. But, of course as my above General class example shows, each routine is free to create instances of objects - even connection objects. but, you certainly cannot have public vars global to the static class, since as noted, you NOT creating an instance of that class when it is used by all logged on users.
So all functions (methods) of the class are fine for that static class, but you can't adopt nor use public vars in that class, since you never creating a instance of the class.
It not clear if you are creating a connection object or those datasets inside of a static class. If you are then simply don't make that class static, and thus in your code you can simple create a instance of that class, including the public connection object and even the public datasets in that class. But, that class can't be static.
So, if you need multiple different connections? then don't use a static class to hold that connection and datasets information, and you should be fine.
Edit
In your sample code you don't show how and where you are setting the connection- we need to see that code.
We are implementing an Client / Server application. Data are sent throughout the LAN. Where LAN means company network with several sites / locations.
We are using WCF and NetTcpBinding (EDIT: VS2010 .net 4.0).
I know that [DataMember(EmitDefaultValue = false)] is not recommended by Microsoft. But as mentionend above, data might be sent from one site to another. Therefore: size really matters!
Not sending the default value works most of the time fine. I have just an issue with collections of any kind. I do not want to transfer empty collections!
So I usually end up with to members of the same type (one for work, one work network) and I need to implement the methods OnSerializing and OnDeserialized.
[DataMember(EmitDefaultValue = false)]
private List<someType> data = new List<someType>();
[NonSerialized]
private List<someType> network = new List<someType>();
[OnDeserialized]
private void OnDeserialized(StreamingContext c)
{
if (network == null)
data = new List<someType>();
else
data = network;
}
[OnSerializing]
private void OnSerializing(StreamingContext c)
{
if (data.Count > 0)
network = data;
else
network = null;
}
Is there any elegant way to do that?
Or maybe even a completely different approach?
Remark: for simplicity I did not care about possible multi-threading issues.
But as mentionend above, data might be sent from one site to another.
Therfore: size really matters!
Do you really think that a few Bytes will make a big difference using NetTcpBinding in a LAN ? Did you made a load test to show that.
I know that [DataMember(EmitDefaultValue = false)] is not recommended
by Microsoft
It's not recommanded because it's not interoperable. This recomandation does not apply to your case as you have only WCF Clients/Server on a NetTcpBinding. The config already does not support interop (through java or php).
The WCF binary encoder (uned in NetTcpBinding) supports Gzip/Deflate compression since .net 4.5. You will gain more Bytes with this feature than removing empty collections.
Read more here.
What approach do you recommend for persisting user settings in a WPF windows (desktop) application? Note that the idea is that the user can change their settings at run time, and then can close down the application, then when starting up the application later the application will use the current settings. Effectively then it will appear as if the application settings do not change.
Q1 - Database or other approach? I do have a sqlite database that I will be using anyway hence using a table in the database would be as good as any approach?
Q2 - If Database: What database table design? One table with columns for different data types that one might have (e.g. string, long, DateTime etc) OR just a table with a string for the value upon which you have to serialize and de-serialize the values? I'm thinking the first would be easier, and if there aren't many settings the overhead isn't much?
Q3 - Could Application Settings be used for this? If so are there any special tasks required to enable the persistence here? Also what would happen regarding usage of the "default" value in the Application Settings designer in this case? Would the default override any settings that were saved between running the application? (or would you need to NOT use the default value)
You can use Application Settings for this, using database is not the best option considering the time consumed to read and write the settings(specially if you use web services).
Here are few links which explains how to achieve this and use them in WPF -
User Settings in WPF
Quick WPF Tip: How to bind to WPF application resources and settings?
A Configurable Window for WPF
Update: Nowadays I would use JSON.
I also prefer to go with serialization to file. XML files fits mostly all requirements. You can use the ApplicationSettings build in but those have some restrictions and a defined but (for me) very strange behavior where they stored. I used them a lot and they work. But if you want to have full control how and where they stored I use another approach.
Make a class Somewhere with all your settings. I named it MySettings
Implement Save and Read for persistence
Use them in you application-code
Advantages:
Very Simple approach.
One Class for Settings. Load. Save.
All your Settings are type safe.
You can simplify or extend the logic to your needs (Versioning, many Profiles per User, etc.)
It works very well in any case (Database, WinForms, WPF, Service, etc...)
You can define where to store the XML files.
You can find them and manipulate them either by code or manual
It works for any deployment method I can imagine.
Disadvantages:
- You have to think about where to store your settings files. (But you can just use your installation folder)
Here is a simple example (not tested)-
public class MySettings
{
public string Setting1 { get; set; }
public List<string> Setting2 { get; set; }
public void Save(string filename)
{
using (StreamWriter sw = new StreamWriter(filename))
{
XmlSerializer xmls = new XmlSerializer(typeof(MySettings));
xmls.Serialize(sw, this);
}
}
public MySettings Read(string filename)
{
using (StreamReader sw = new StreamReader(filename))
{
XmlSerializer xmls = new XmlSerializer(typeof(MySettings));
return xmls.Deserialize(sw) as MySettings;
}
}
}
And here is how to use it. It's possible to load default values or override them with the user's settings by just checking if user settings exist:
public class MyApplicationLogic
{
public const string UserSettingsFilename = "settings.xml";
public string _DefaultSettingspath =
Assembly.GetEntryAssembly().Location +
"\\Settings\\" + UserSettingsFilename;
public string _UserSettingsPath =
Assembly.GetEntryAssembly().Location +
"\\Settings\\UserSettings\\" +
UserSettingsFilename;
public MyApplicationLogic()
{
// if default settings exist
if (File.Exists(_UserSettingsPath))
this.Settings = Settings.Read(_UserSettingsPath);
else
this.Settings = Settings.Read(_DefaultSettingspath);
}
public MySettings Settings { get; private set; }
public void SaveUserSettings()
{
Settings.Save(_UserSettingsPath);
}
}
maybe someone get's inspired by this approach. This is how I do it now for many years and I'm quite happy with that.
You can store your settings info as Strings of XML in the Settings.Default. Create some classes to store your configuration data and make sure they are [Serializable]. Then, with the following helpers, you can serialize instances of these objects--or List<T> (or arrays T[], etc.) of them--to String. Store each of these various strings in its own respective Settings.Default slot in your WPF application's Settings.
To recover the objects the next time the app starts, read the Settings string of interest and Deserialize to the expected type T (which this time must be explcitly specified as a type argument to Deserialize<T>).
public static String Serialize<T>(T t)
{
using (StringWriter sw = new StringWriter())
using (XmlWriter xw = XmlWriter.Create(sw))
{
new XmlSerializer(typeof(T)).Serialize(xw, t);
return sw.GetStringBuilder().ToString();
}
}
public static T Deserialize<T>(String s_xml)
{
using (XmlReader xw = XmlReader.Create(new StringReader(s_xml)))
return (T)new XmlSerializer(typeof(T)).Deserialize(xw);
}
The long running most typical approach to this question is: Isolated Storage.
Serialize your control state to XML or some other format (especially easily if you're saving Dependency Properties with WPF), then save the file to the user's isolated storage.
If you do want to go the app setting route, I tried something similar at one point myself...though the below approach could easily be adapted to use Isolated Storage:
class SettingsManager
{
public static void LoadSettings(FrameworkElement sender, Dictionary<FrameworkElement, DependencyProperty> savedElements)
{
EnsureProperties(sender, savedElements);
foreach (FrameworkElement element in savedElements.Keys)
{
try
{
element.SetValue(savedElements[element], Properties.Settings.Default[sender.Name + "." + element.Name]);
}
catch (Exception ex) { }
}
}
public static void SaveSettings(FrameworkElement sender, Dictionary<FrameworkElement, DependencyProperty> savedElements)
{
EnsureProperties(sender, savedElements);
foreach (FrameworkElement element in savedElements.Keys)
{
Properties.Settings.Default[sender.Name + "." + element.Name] = element.GetValue(savedElements[element]);
}
Properties.Settings.Default.Save();
}
public static void EnsureProperties(FrameworkElement sender, Dictionary<FrameworkElement, DependencyProperty> savedElements)
{
foreach (FrameworkElement element in savedElements.Keys)
{
bool hasProperty =
Properties.Settings.Default.Properties[sender.Name + "." + element.Name] != null;
if (!hasProperty)
{
SettingsAttributeDictionary attributes = new SettingsAttributeDictionary();
UserScopedSettingAttribute attribute = new UserScopedSettingAttribute();
attributes.Add(attribute.GetType(), attribute);
SettingsProperty property = new SettingsProperty(sender.Name + "." + element.Name,
savedElements[element].DefaultMetadata.DefaultValue.GetType(), Properties.Settings.Default.Providers["LocalFileSettingsProvider"], false, null, SettingsSerializeAs.String, attributes, true, true);
Properties.Settings.Default.Properties.Add(property);
}
}
Properties.Settings.Default.Reload();
}
}
.....and....
Dictionary<FrameworkElement, DependencyProperty> savedElements = new Dictionary<FrameworkElement, DependencyProperty>();
public Window_Load(object sender, EventArgs e) {
savedElements.Add(firstNameText, TextBox.TextProperty);
savedElements.Add(lastNameText, TextBox.TextProperty);
SettingsManager.LoadSettings(this, savedElements);
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
SettingsManager.SaveSettings(this, savedElements);
}
Apart from a database, you can also have following options to save user related settings
registry under HKEY_CURRENT_USER
in a file in AppData folder
using Settings file in WPF and by setting its scope as User
In my experience storing all the settings in a database table is the best solution. Don't even worry about performance. Today's databases are fast and can easily store thousands columns in a table. I learned this the hard way - before I was serilizing/deserializing - nightmare. Storing it in local file or registry has one big problem - if you have to support your app and computer is off - user is not in front of it - there is nothing you can do.... if setings are in DB - you can changed them and viola not to mention that you can compare the settings....
You can use SQLite, a small, fast, self-contained, full-featured, SQL database engine. I personally recommend it after trying settings file and XML file approach.
Install NuGet package System.Data.SQLite
which is an ADO.NET provider for SQLite.
The package includes support for LINQ and Entity Framework
Overall you can do many things with such supporting features to your settings window.
1.Install SQLite
2.Create your database file
3.Create tables to save your settings
4.Access database file in your application to read and edit settings.
I felt this approach very much helpful for application settings, since i can do adjustments to database and also take advantage of ADO.Net and LINQ features
I typically do this sort of thing by defining a custom [Serializable] settings class and simply serializing it to disk. In your case you could just as easily store it as a string blob in your SQLite database.
In all the places I've worked, database has been mandatory because of application support. As Adam said, the user might not be at his desk or the machine might be off, or you might want to quickly change someone's configuration or assign a new-joiner a default (or team member's) config.
If the settings are likely to grow as new versions of the application are released, you might want to store the data as blobs which can then be deserialized by the application. This is especially useful if you use something like Prism which discovers modules, as you can't know what settings a module will return.
The blobs could be keyed by username/machine composite key. That way you can have different settings for every machine.
I've not used the in-built Settings class much so I'll abstain from commenting. :)
I wanted to use an xml control file based on a class for my VB.net desktop WPF application. The above code to do this all in one is excellent and set me in the right direction. In case anyone is searching for a VB.net solution here is the class I built:
Imports System.IO
Imports System.Xml.Serialization
Public Class XControl
Private _person_ID As Integer
Private _person_UID As Guid
'load from file
Public Function XCRead(filename As String) As XControl
Using sr As StreamReader = New StreamReader(filename)
Dim xmls As New XmlSerializer(GetType(XControl))
Return CType(xmls.Deserialize(sr), XControl)
End Using
End Function
'save to file
Public Sub XCSave(filename As String)
Using sw As StreamWriter = New StreamWriter(filename)
Dim xmls As New XmlSerializer(GetType(XControl))
xmls.Serialize(sw, Me)
End Using
End Sub
'all the get/set is below here
Public Property Person_ID() As Integer
Get
Return _person_ID
End Get
Set(value As Integer)
_person_ID = value
End Set
End Property
Public Property Person_UID As Guid
Get
Return _person_UID
End Get
Set(value As Guid)
_person_UID = value
End Set
End Property
End Class
I've developed a sample software in c# windows Appliation. How to make it a multilingual supporting software.
For Example: One of the message boxes display " Welcome to sample application"
i installed the software in a chinees os , but it displays the message in english only.
i'm using "string table" (Resource File) for this problem.
In string table i need to create entry for each messages in english and Chinees.
its a timely process. is there any other way to do this?
Create Resources files for each language you want to give support for mentioned below.
alt text http://geekswithblogs.net/images/geekswithblogs_net/dotNETPlayground/resx.gif
Based on the language/currentculture of the user, read values from respective Language Resource file and display in label or MessageBox. Here's some sample code:
public static class Translate
{
public static string GetLanguage()
{
return HttpContext.Current.Request.UserLanguages[0];
}
public static string Message(string key)
{
ResourceManager resMan = null;
if (HttpContext.Current.Cache["resMan" + Global.GetLanguage()] == null)
{
resMan = Language.GetResourceManager(Global.GetLanguage());
if (resMan != null) HttpContext.Current.Cache["resMan" + Global.GetLanguage()] = resMan;
}
else
resMan = (ResourceManager)HttpContext.Current.Cache["resMan" + Global.GetLanguage()];
if (resMan == null) return key;
string originalKey = key;
key = Regex.Replace(key, "[ ./]", "_");
try
{
string value = resMan.GetString(key);
if (value != null) return value;
return originalKey;
}
catch (MissingManifestResourceException)
{
try
{
return HttpContext.GetGlobalResourceObject("en_au", key).ToString();
}
catch (MissingManifestResourceException mmre)
{
throw new System.IO.FileNotFoundException("Could not locate the en_au.resx resource file. This is the default language pack, and needs to exist within the Resources project.", mmre);
}
catch (NullReferenceException)
{
return originalKey;
}
}
catch (NullReferenceException)
{
return originalKey;
}
}
}
In asn asp.net application, you'd use it as following:
<span class="label">User:</span>
You now would put:
<span class="label"><%=Translate.Message("User") %>:</span>
If you were going to use resource files as Ram suggested, there is a good blog post about localisation
here: ASP.NET MVC 2 Localization complete guide. (I should have mentioned that this is for Asp.net mvc 2, it may or may not be useful) You still have to spend time making tables for each language. I have not used any other approach for this before, hope you find something useful
You can do it using resource files. You need to create resource file for each language and you can use the appropriate one while running the application.
Resharper 5.0 can greatly improve the time you spend on localization. It has features that allows easy move to resource and it underlines (if chosen so) all strings that are localizable so it's harder to miss them.
Given that it has 30 days trial (full version) you can simply install it, do your job and uninstall if you can't afford it, but i would suggest to keep it :-) It's really worth it's price.
Software localization and globalization have always been tough and at times unwanted tasks for developers. ReSharper 5 greatly simplifies working with resources by providing a full stack of features for resx files and resource usages in C# and VB.NET code, as well as in ASP.NET and XAML markup.
Dedicated features include Move string to resource, Find usages of resource and other navigation actions. Combined with refactoring support, inspections and fixes, you get a convenient localization environment.
I'm trying to standardise some data access code with my colleagues. One of the aforementioned colleagues asserts that the EntLib Data Access Block trys to cache parameters on stored proc calls.
I've had a look in reflector and there is some evidence that it could be caching them. But I don't think it does in the following situation.
public Dictionary<long, string> GetQueue(int maxItems)
{
var sq = new SqlDatabase(_connString.ConnectionString);
var result = new Dictionary<long, string>();
using (var cmd = (SqlCommand)sq.GetStoredProcCommand("dbo.GetQueue"))
{
sq.AddInParameter(cmd, "maxItems", DbType.Int32, maxItems);
var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
while (reader.Read())
{
long id = reader.GetInt64(reader.GetOrdinal("id"));
string fileName = reader.GetString(reader.GetOrdinal("meta_data_filename"));
result.Add(id, fileName);
}
}
return result;
}
Can anyone confirm or deny this?
I'm using EntLib 4.1
It definetly used to, I ripped the code out and threw in in my library.
it used sp_help and parsed the output to determine the data types.
These days, I ripped the code out, .Net is much much better about adding parameters.
cmd.Parameters.AddWithValue("#name",somevalue)
in your example of you keep reflectoring ... you will find it being done down this path GetStoredProcCommand()
You will get a Command object back, already populated with parameters
The ent lib code is copyrighted, but the code is almost identical to this
http://code.google.com/p/dbdotnet/source/browse/trunk/ParameterCache.cs
As far as I can tell it doesn't cache the parameters. Using the same instance of a Database object I called DiscoverParameters multiple times while running a trace. Each time I call DiscoverParameters I can see a [sys].[sp_procedure_params_100_managed] so it looks like it's making the round trip every time.
Here's an example of how to do it yourself that's seems like it might be alright:
http://davidhayden.com/blog/dave/archive/2006/11/03/CachingStoredProcedureParameters.aspx