Use Memcached in .NET application with Linq
First of all, get the memcached, I user win32 version. http://jehiah.cz/projects/memcached-win32/ version 1.2.1
Secondly, get a client for memcached, I use this one http://memcachedproviders.codeplex.com/ version 1.2
Get the document for the client too, I start without it and I think you should read it in advance.
I will share some of my experience too.
The official documentation is a bit poor, I am going to give an introduction on the main points.
1. Memcached is a universal distributed cache.
i.e. it can run multiple instances and appears to the programmer as one cache server which is programming language independent. A really large hashtable.
2. It stores no object, you’ll have to serialze every object that you want to cache.
3. It won’t throw any exception no matter what happens.
I will focus on how to use the memcached server, not how to setup since you should be run it without a problem.
Setup the client in your application. Unarchive and add reference to all three .dll in your project.
Then configure your web.config or app.config like this, inside <configSections> tag, add the following:
<section name="cacheProvider" type="MemcachedProviders.Cache.CacheProviderSection, MemcachedProviders" allowDefinition="MachineToApplication" restartOnExternalChanges="true"/> <sectionGroup name="enyim.com"> <section name="memcached" type="Enyim.Caching.Configuration.MemcachedClientSection, Enyim.Caching"/> </sectionGroup> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"/>
This tells the application that there will be a section with tag <cacheprovider> and <log4net>. Also will have a section with tag <enyim.com> who has a child tag <memcached> inside. You can just copy and paste the above, it should work.
Then add the detail settings in <configuration> tag.
<cacheProvider defaultProvider="MemcachedCacheProvider">
<providers>
<add name="MemcachedCacheProvider" type="MemcachedProviders.Cache.MemcachedCacheProvider, MemcachedProviders"
keySuffix="_MySuffix_" defaultExpireTime="2000"/>
</providers>
</cacheProvider>
<enyim.com>
<memcached>
<servers>
<add address="192.168.1.2" port="11211" />
</servers>
<socketPool minPoolSize="10" maxPoolSize="100" connectionTimeout="00:00:10" deadTimeout="00:02:00" />
</memcached>
</enyim.com>
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}]- %message%newline" />
</layout>
</appender>
<root>
<priority value="WARN"/>
<appender-ref ref="ConsoleAppender">
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="WARN"/>
<levelMax value="FATAL"/>
</filter>
</appender-ref>
</root>
</log4net>
You can change the default expire time. Server address is the host which you run memcached and 11211 is the default port.
Caching string and serialized object should be something you already know. For simple class, just add [Serializable] to the class.
Let’s see how it works with LINQ.
To serialize LINQ object, open .dbml design view, select serialize mode to unidirectional.
Then you will need to use DataContractSerializer to actually serialize your object. For more information.
There are a few serializer, XML, binary, JSON, etc. I am using DataContractJsonSerializer since it should be faster than XML. The following code snippet serialize and cache the object array to memcached.
//create a serialize provider with specify object type
DataContractJsonSerializer serializeProvider = new DataContractJsonSerializer(typeof(myClass[]));
//a memory stream as buffer since the provider do not directly generate string
using (MemoryStream ms = new MemoryStream())
{
serializeProvider.WriteObject(ms, myClassArrayObject);
byte[] l_buffer = new byte[ms.Length];
ms.Position = 0;
//If the string is out of integer range, skip it, you can do whatever you need to handle this
if (ms.Length < int.MaxValue)
{
ms.Read(l_buffer, 0, (int)ms.Length);
string l_serialized = Encoding.Unicode.GetString(l_buffer);
//remove existing cache with the same key
DistCache.Remove("keyString");
DistCache.Add("keyString", l_serialized);
}
else
{
throw new Exception("object data large.");
}
}
Then you can check whether you succussfully cache it with telnet.
Telnet to your server and type the command ’stats’, it will then shows the status of your memcached.
curr_items is the number of items inside memcached.
There is some common practices in choosing the hash key but that is another topic.
I am using Application:Method:Parameters:Salt and then hash it with SHA256, convert it to base64.
This should be enough for testing purpose, you can come up with your own hash decision.
Then we will get the cache and deserialize it to our object.
string l_serialized = (string)DistCache.Get("keyString");
DataContractJsonSerializer serializeProvider = new DataContractJsonSerializer(typeof(myClass[]));
using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(l_serialized)))
{
myClass[] members = (myClass[])serializeProvider.ReadObject(ms);
}
You can do this with your object, object array, object collection, etc.
But remember you are in a different data context if you get you data from cache.
This will need some workaround to update or delete data with Linq.
I will cover this part later in another article.
C# do not inherit parent class constructor
C# inheritance do not inherit parent class constructor.
If you want to invoke the parent constructor before doing anything in child constructor.
Define the child constructor as the following
ChildClassName(child parameter list) : base (child parameter list)
Two parameter lists do not need to be the same.
Request.Params vs Request.QueryString
Request.Params will get the set of keys/values including queryString, form, cookies and server variables.
whereas Request.QueryString only get those http [GET] parameters which means more efficient.
Encoding Error When Passing Value in Url
Title: 使用URL傳參數出現亂碼
Use the following to solve the problem.
HttpUtility.UrlEncode()/UrlDecode()
And this method save you from some XSS attacks too ^_^
用下列方法就編碼可以
HttpUtility.UrlEncode()/UrlDecode()
這會同時避開某部份XSS攻擊
Extension is static method, beware of its namespace
If you are using extension method within same namespace, carefully choose your method names.
Because extension methods is actually treat as static methods.
So, if you have extension method isEmpty() for let’s say Guid and TextBox.
And both of them are in namespace Your.Extension. The compile will complete without a problem.
But you will encounter error at runtime since both static methods have the same name.
You can either choose another name or separate them in different namespace.
Export using ReportViewer without viewing
ReportViewer is a great control. You can make drill down report and preview the report in the webpage.
Export as Excel or Pdf file easily, etc. Great features.
But, sometimes the user may not want to preview the report, the just want to get the file.
You can probably do that with Crystal report, but ReportViewer can just do the same, there is no point to shift.
Here is how to export as a byte stream. Use the LocalReport.Render()
Warning[] warnings;
string[] streamids;
string mimeType;
string encoding;
string extension;
//export to excel, use "PDF" is you would like pdf format
byte[] l_reportBytes = rv_waterLicenseReport.LocalReport.Render("Excel", null, out mimeType, out encoding, out extension, out streamids, out warnings);
Response.ClearContent();
//setup the file name
Response.AddHeader("Content-Disposition", "attachment; filename=" + "ApplicationForm" + tb_LicenseNo.Text.Trim() + ".xls");
Response.AddHeader("Accept-Header", l_reportBytes.Length.ToString());
//define mime type
Response.ContentType = "application/vnd.ms-excel";
Response.OutputStream.Write(l_reportBytes, 0, l_reportBytes.Length);
Response.Flush();
Response.Close();
Strangely, the one page report of mine will export out as two page with the second page blank.
I will look into that later. For how to create a report, see my other post.
Data Struct in HashTable is ReadOnly
I once use a HashTable to work as a fast data lookup for some struct.
And I use a delegate to implement the Find() method to lookup.
Everything works fine until I try to modify values of the struct that my Find() gives me.
I later find out that because struct is a value type variable.
That means, my Find() return a copy of that struct, modifying it is just useless.
So, HashTable with struct AS-IS readonly.
If you want to have a HashTable with object that is changable,
simply use Class instead of struct.
Well, actually, struct is recommended to use for read only data in MS press book.
Generate Statistic Chart with ZedGraph in ASP.NET
中文翻譯請稍候.
The chart generation work is handled by my former colleague Peter. But he left.
He did the job with his own little chart library which I don’t know how to use it.
So I look for an open source and/or free solution. I find ZedGraph and it looks pretty much 90% what I need.
After experiencing ZedGraph, it is very amazing how easy to use ZedGraph and its features are so complete!
Download ZedGraph first.
If you are not going to make your own version or dive into the source, just download dll only.
Create a Web Application, add both ZedGraph.dll and ZedGraph.Web.dll to the reference.
And then right click on the toolbox, add new server control by adding ZedGraph.Web.dll.
You can now drag the ZedGraph control to your design view.
Remember to *REMOVE* all other tags, just leave
and the ZedGraphWeb control behind.
Result in something like this :
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="GenerateLineChart.aspx.cs" Inherits="TaoYuan.HistoryData.ByItemGroup.GenerateLineChart" %>
<%@ Register Assembly="ZedGraph.Web" Namespace="ZedGraph.Web" TagPrefix="zgw" %>
As you can read in their document, ZedGraph works in two modes.
I am not going to explain it since their have nice document on that.
I will show how to use the stream mode to expose a Graph as a url.
Back to the page which we have a ZedGraph control.
We assign an event handler to RenderGraph event which will define how the graph is rendered. Assign like the following :
zedGraphControl.RenderGraph += new ZedGraph.Web.ZedGraphWebControlEventHandler(zedGraphControl_RenderGraph);
public void zedGraphControl_RenderGraph(ZedGraphWeb zgw, Graphics g, MasterPane masterPane){...}
Inside the handler method, you will be given a default master pane.
I maybe talk about master pane later, let’s just use one graph in this post.
What you need to do here is setup the graph style, bind data and done.
I will just show a demo snippet with a little explaination.
//get a graph pane
GraphPane masterPaneDefault = masterPane[0];
//style the title and axises
masterPaneDefault.Title.Text = m_selectedStation.Name;
masterPaneDefault.XAxis.Title.IsVisible = false;
masterPaneDefault.YAxis.Title.Text = m_selectedItemInfo.Name+'('+ m_selectedItemInfo.Unit +')';
//we are going to use line chart here
//use data to make a curve line
PointPairList theLine = new PointPairList();
foreach (DataRow dr in m_DataFound.Rows)
{
DateTime l_date = (DateTime)dr[0];
double x = (double)new XDate(l_date);
double y = 0;
if (!DBNull.Value.Equals(dr[1]))
{
y = (Single)dr[1];
theLine.Add(x, y);
}
}
//set angle of xaxis label
masterPaneDefault.XAxis.Scale.FontSpec.Angle = 45;
//add the line to our graph pane
LineItem theCurve = masterPaneDefault.AddCurve(m_selectedItemInfo.Name, theLine, Color.Red, SymbolType.XCross);
masterPaneDefault.XAxis.Type = AxisType.DateAsOrdinal;
masterPaneDefault.XAxis.Scale.Format = "yyyy/MM/dd HH:mm";
//smooth the curve
//theCurve.Line.IsSmooth = true;
// Make curves thicker
//theCurve.Line.Width = 3.0F;
// Add gridlines to the plot, and make them gray
masterPaneDefault.XAxis.MajorGrid.IsVisible = true;
masterPaneDefault.YAxis.MajorGrid.IsVisible = true;
masterPaneDefault.XAxis.MajorGrid.Color = Color.Gray;
masterPaneDefault.YAxis.MajorGrid.Color = Color.LightGray;
masterPaneDefault.XAxis.MinorGrid.IsVisible = true;
masterPaneDefault.XAxis.MinorGrid.Color = Color.Khaki;
// Move the legend location
masterPaneDefault.Legend.Position = LegendPos.Right;
// Add a background gradient fill to the axis frame
masterPaneDefault.Chart.Fill = new Fill(Color.White, Color.FromArgb(255, 255, 210), -45F);
masterPaneDefault.Fill = new Fill(Color.FromArgb(250, 250, 255));
//masterPaneDefault.XAxis.Scale.MajorStep = 2.5d;
masterPaneDefault.Legend.IsVisible = false;
//calculate the axis scale ranges
masterPaneDefault.AxisChange();
The result will look something like this.

That’s it. There are other great charting tools avaiable.
The MS Chart control and Flot are both great. Check them out if you like.
How To Cast ExecuteScalar Return Value
SqlCommand.ExecuteScalar(). I believe many of you use this handy method quite often.
But it is what it returns causing the trouble. This method returns an object.
If you try to cast that with (Type), this will almost always return you an invalid cast exception.
It is okay to use (Int32) or (string). But for (double), it needs to be done in another way.
Always use Convert.ToInt32() and Convert.ToDouble() for these situation.
This will save you a day.