麥克斯做個不宅的工程師

Maxi’s idiotic programming.

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.

中文版

November 2, 2009 , Monday Posted by maxi326 | Programming, Server | , , , , | No Comments Yet

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.

September 28, 2009 , Monday Posted by maxi326 | Programming | , , , | No Comments Yet

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.

September 28, 2009 , Monday Posted by maxi326 | Programming | , , | No Comments Yet

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攻擊

September 28, 2009 , Monday Posted by maxi326 | Programming | , , , , | No Comments Yet

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.

September 2, 2009 , Wednesday Posted by maxi326 | Programming | , , | No Comments Yet

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.

September 2, 2009 , Wednesday Posted by maxi326 | Programming | , , , , | No Comments Yet

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.

September 2, 2009 , Wednesday Posted by maxi326 | Programming | , , | No Comments Yet

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.
line chart
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.

June 19, 2009 , Friday Posted by maxi326 | Programming | , , , | No Comments Yet

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.

June 19, 2009 , Friday Posted by maxi326 | Programming | , , | No Comments Yet

Remembering ListView page index and restoring it.

I am using ListView with DataPager to get a paging feature of my list.
I want to redirect my page when I click on an entry in the list view, show some detail information.
When I am done, I want to go back and view the exact same list of information.
That is, remembering the page index.

我是用DataPager幫我的ListView做分頁功能.我想要在使用者點選列表上某一條目後,
轉跳到別的頁面,顯示一些詳細資訊後,完成後回到剛才的列表頁面,要能看到剛才的那一個分頁.
就是說,要記住之前的頁碼.

Inside your ItemCommand handler method, use the following code snippet to find the correct startRowIndex
在你的ItemCommand處理程序裡面,用下面的程式片段去找出正確的startRowIndex

//index of the clicked item within the whole list
int l_dataIndex = ((ListViewDataItem)e.Item).DataItemIndex;
//minus the index of the item in that page = startRowIndex
int l_startRowIndex = l_dataIndex - ((ListViewDataItem)e.Item).DisplayIndex;
Store this index whatever you want, I store it by using GET method.
把索引值存在你喜歡的地方,我把它儲存在GET方法裡

Why do I call the above correct startRowIndex and not using the DataPager.startRowIndex?
Because we are going to use DataPager.SetPageProperties() in Page_Load() to restore the page we previously looking at.
為甚麼我會說上面的是正確的startRowIndex還有不去用DataPager.startRowIndex呢?
因為我們將會在Page_Load()裡用DataPager.SetPageProperties()去顯示之前我們在看的分頁

dataPager.SetPageProperties(l_startRowIndex, dpg_dataPager.MaximumRows, true);

This will bring you back to the page as we expect. But after this call.
The startRowIndex won’t update now even though you click next/previous to change your page.
That is why I said this is not the correct startRowIndex. You can try this youself ^^.
這個調用會把你帶到之前的分頁,正如我們預期.但在這個調用後,
startRowIndex就會停止更新,就算你點選上一頁/下一頁也不會改變.
這就是為甚麼我說上面的才是正確的startRowIndex.你可以自己試一試 ^^.

It turns out that I have done something stupid. The SetPageProperties() have to be inside IsPostBack to work correctly.
Result in something like this.

//in ItemCommand handler
url += "&startRowIndex=" + dataPager.startRowIndex;

//in Page_Load
if (!IsPostBack)
{
dataPager.SetPageProperties(startRowIndex, dataPager.MaximumRows, true);
}

June 16, 2009 , Tuesday Posted by maxi326 | Programming | , , , , | No Comments Yet