麥克斯做個不宅的工程師

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

ASP.NET Membership/Role Model + Sitemap Security Trimming

The scenario is like this, I want to extend the ability of ASP Membership and role mode, also want to display content depends on user role.

So, I am going to use a SQL server to store and be able to extend. You can even implement you own provider class.

I will focus on how to setup and enable these features in this article.

We start with building tables in our database which we store our membership and role information.

Look under %WINDOWS%\Microsoft.NET\Framework\v2.0.50727\

There should be a ASPNET_REGSQL.exe, use it to create tables in your database.

ASPNET_REGSQL -S database_host -U username -P password -d database_name -A support_providers

for example : ASPNET_REGSQL -S 192.168.1.2 -U sa -P 1234 -d membershipDb -A All

And then, we need to configure the web.config to tell the web application that we are using membership and role.

Also, tell the provider where to get those information.

Setup a connection string to the database.

<connectionStrings>
 <remove name="LocalSqlServer"/>
 <add name="LocalSqlServer" connectionString="data source=.;Initial Catalog=MemRoleProvider;user id=sa;password=1234" providerName="System.Data.SqlClient" />
 </connectionStrings>

The [remove] delete the connect string whose name is LocalSqlServer. This seems to be the default connection string name.

You may use your own connection string name and do not remove the default one.

At last, we create the provider for the application in the [system.web] section.

<membership defaultProvider="AnaSystemSqlMembershipProvider" userIsOnlineTimeWindow="15">
 <providers>
 <clear/>
 <add name="AnaSystemSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider,System.Web, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="LocalSqlServer" enablePasswordRetrieval="false"
enablePasswordReset="true" requiresQuestionAndAnswer="true" applicationName="/" requiresUniqueEmail="false"
passwordFormat="Hashed" maxInvalidPasswordAttempts="5" passwordAttemptWindow="10"/>
 </providers>
 </membership>

 <roleManager enabled="true" defaultProvider="AnaSystemSqlRoleProvider">
 <providers>
 <add name="AnaSystemSqlRoleProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="LocalSqlServer" applicationName="/"/>
 </providers>
 </roleManager>

The [clear] remove all existing provider. The attributes reference can be found here.

ConnectionStringName should be the one that you previously configure. You should now able to leverage the power of ASP membership and role model.

The next step is to integrate with Security Trimming. I will use this feature to populate navigation menu depending on user role.

You have to have a  sitemap in your web application, then activeate the security trimming like this.

Inside [system.web].

<siteMap enabled="true" defaultProvider="XmlSiteMapProvider">
 <providers>
 <clear/>
 <add name="XmlSiteMapProvider"
 description="Default SiteMap provider."
 type="System.Web.XmlSiteMapProvider"
 siteMapFile="Web.sitemap"
 securityTrimmingEnabled="true" />
 </providers>
 </siteMap>

[clear] is to remove existing sitemap provider. Next we have to enable the authenticatin feature.

Inside [system.web]

<authentication mode="Forms">
 <forms name=".mycookie" path="/" loginUrl="Default.aspx" protection="All" timeout="90"/>
 </authentication>

The name attribute inside forms tag is the unique cookie name for the application.

Reference for other attribute can be found here.

The next step is to setup access rule for your directories. ASP access rule is directory base.

The root directory access rule is inside project’s web.config. Each directory will also have its

own web.config. Here is an example to give you an idea. inside [system.web]

<authorization>
 <allow users="admin" />
 <deny users="?" />
 </authorization>

This means the root directory is accessable by admin role and forbid anonymous users.

You can manage access rule with a UI, in VS2008->Project->ASP.NET administration tools->security.

The final step is to add some setting to your sitemap file, web.sitemap.

Add roles=”*” to your siteMapNode to enable security trimming base on access rules.

Here is an example.

<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
 <siteMapNode title="Home" description="Home"
 url="~/default.aspx" >

 <siteMapNode title="Guest Area" roles="*">
 <siteMapNode url="~/Guest/Guest.aspx" title="guest only"  description="" />
 </siteMapNode>
 <siteMapNode title="Power Area" roles="*">
 <siteMapNode url="~/PowerUser/Content.aspx" title="power user"  description="" />
 </siteMapNode>
 </siteMapNode>

</siteMap>

That’s it. You can also fine tune it to access rule base on page file by that is beyond the scope of this article.

You can find a little reference about that here.

中文版在這裡

November 2, 2009 , Monday Posted by maxi326 | Programming | , , , | 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

SQL server query for Concatenate column fail

You can query for combine columns using ‘+’ operator.

for example:
SELECT Col1 + Col2 AS CombineColumn

Question:
But this query will sometimes fail, how is that?

Answer:
This query will fail if either of the concatenating column contains ‘Null’ value.

Solution:
The isNull() Function can be used to check whether the column is Null.

Example With isNull() Function.
Select isnull(CAST(Customer.Contactid as nvarchar(10)),”) +’-'+ isnull(Customer.Firstname,”) +’-'+ isnull(Customer.LastName,”)
as [Full Name] from Customer

The first parameter is the column and the second parameter is what the method return if ‘Null’ was encountered.

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

C# Extension Method By Simple Example!

For Example, You want to add new methods to System.Web.UI.WebControls.CheckBoxList
Step One. Declare a static class with *any* name.
Step Two. Declare a static method just like any normal method. This method can have return value and arguments.
Remember, we need to have the first argument define like this *this ClassName object*. In our case :
public static void MethodName(this CheckBoxList checkBoxList)
{
...
}

Okay now, Let’s see an example. Let’s say we want to have all selected checkbox values.
We can do like this.
public static class CheckBoxListExtension
{
public static string[] GetSelectedValues(this CheckBoxList checkBoxList)
{
List l_selected = new List(5);
foreach (ListItem item in checkBoxList.Items)
{
if (item.Selected)
{
l_selected.Add(item.Value);
}
}return l_selected.ToArray();
}
}

Then, when you use it, .NET may tells you that it can’t find that method.
The auto add using won’t work in this situation.
That make sense since the extension methods is not in that extended class.
Just add using YourNameSpace manually and we are good.

July 30, 2009 , Thursday Posted by maxi326 | Programming | , , , | No Comments Yet