Memcached and Asp.net mvc
Today I was hanging around on Google and found a lot of useful stuff about Memcached and .net. Currently, I am using System.Web.Cache to cache objects for my webpage stuff. It's good, but not enough for scalable since it's often remove my object when run on low memory. So I go on with Memcached instead. I found a lot of library which could help us develop faster. Here are some library/solution that I found:
http://ayende.com/Blog/archive/2008/06/06/Scratching-an-itch-NMemcached.aspx
http://www.codeproject.com/KB/aspnet/memcached_aspnet.aspx
http://maxi326.wordpress.com/2009/11/02/use-memcached-in-net-application-with-linq/
http://zvolkov.com/blog/post/2009/06/18/Using-MemCached-with-NHibernate.aspx
http://latebound.blogspot.com/2008/10/using-memcached-from-c.html
http://www.codeplex.com/memcachedproviders
http://code.google.com/p/beitmemcached/
http://sourceforge.net/projects/memcacheddotnet/
http://www.codeplex.com/EnyimMemcached/
and the list go on...
What is MemCached? To put this in simple term, it's a cache store / hash table, where you can store almost everything, like objects, on it. Basically, it's very useful since there are parts where you don't want to cache your whole webpage but you just want to cache your object which you finished process or pulled out from database. It can save you hundreds of ms query time, server loads, and process time, which is very very very useful.
There are some downside if you run Memcached on Windows, the latest update for Memcached for windows that i can found is 1.2.5 I found a latest version here: 1.4.4, and the lates for linux is 1.4.4 (at the time I write this post). There are some solution for this problem. By using another computer/server installed as linux, you can solely run Memcached for cache purpose on multiple server. If you are not having more than 1 server, then just fall back to use System.Web.Cache since it's quite good for production already. You can also try out Velocity, which currently on it's CTP and I am not sure about whether Microsoft going to charge users for this.
I suggest you use MemCached Provider, it's the simplest to implement memcached in asp.net mvc, the following web.config instruction provided by MemCached Provider
Open Web.config file and add the following section to configSections tag
<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"/>
Add the following section to configure Enyimâs client to point to Memcached servers
<enyim .com> <memcached> <servers> <!-- put your own server(s) here--> <add address="127.0.0.1" port="11211" /> </servers> <socketpool minPoolSize="10" maxPoolSize="100" connectionTimeout="00:00:10" deadTimeout="00:02:00" /> </memcached> </enyim>
Add the following section to configure Memcached Cache Provider. keySuffix attribute allows for adding suffix to cache provider keys in order to simulate namespaces.
<cacheprovider defaultProvider="MemcachedCacheProvider"> <providers> <add name="MemcachedCacheProvider" type="MemcachedProviders.Cache.MemcachedCacheProvider, MemcachedProviders" keySuffix="_MySuffix_" defaultExpireTime="2000"/> </providers> </cacheprovider>
Add following section to configure log4net
<log4net> <!-- Define some output appenders --> <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender"> <layout type="log4net.Layout.PatternLayout"> <conversionpattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" /> </layout> </appender> <!--<threshold value="OFF" />--> <!-- Setup the root category, add the appenders and set the default priority --> <root> <priority value="WARN"/> <appender -ref ref="ConsoleAppender"> <filter type="log4net.Filter.LevelRangeFilter"> <levelmin value="WARN"/> <levelmax value="FATAL"/> </filter> </appender> </root> </log4net>
Provided you include all the reference libraries, for example, this is my category controller, which will cache the object if it's not existed in the MemCached's memory.
1 2 3 4 5 6 7 8 9 10 11 12 | public ActionResult Category() { var returnObj = DistCache.Get<list <CategoryObject>>("fullCatListObject"); if (returnObj == null) { P015.Modules.Categories.Categories _cat = new P015.Modules.Categories.Categories(); returnObj = _cat.getCatObject(); DistCache.Add("fullCatListObject", returnObj, new TimeSpan(29, 0, 0, 0)); } return View(returnObj); } </list> |
One thing you need to remember when using this library is to have all your objects that you want to save in memcached serialized. For example, this is my CategoryObject class use for the above example:
CategoryObject.cs
[Serializable] public class CategoryObject { public string CAT_NAME { get; set; } public int CAT_ID { get; set; } public int LEVEL { get; set; } }
In case you are using Linq, or more specific, linq to sql, you can open your DBML file in design mode, change the Serialize Mode option to Unidirectional.
This works for me, so it should works for you too. Enjoy
Cache individual object in asp.net mvc
Upon writing a controller for exporting image on the fly, I struggled with the problem that: "Output cache does not cache HttpHeader (in this case, "Location" header)". Which lead me to despair since each time i called the controller to output image, it will have to connect to database and do a couple of query, which is not practical in real life. So finally, i found out that i can easily cache objects into a cache provider in Asp.net Mvc. Very useful, all my queries go back to 0 (traced through Ling2sql profiler).
You can use this piece of code in anywhere of your code where you want to cache any object.
(Notice: this code was used in a controller, so I used HttpContext.Cache. But if you use else where that make this code not working, try HttpContext.Current.Cache)
1 2 3 4 5 6 7 8 9 10 | if (HttpContext.Cache["ObjName"] != null) { link = (string)HttpContext.Cache["ObjName"]; return link; } else { HttpContext.Cache.Add("ObjName", link, null, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(30, 0, 0, 0, 0), System.Web.Caching.CacheItemPriority.High, null); return link; } |
What it did was to check if the ObjName exist in the context, if not, create one and dump the data into it. Remember when you call back from HttpContext, it's an object so you always need a cast. If you don't know about argument, let the intellisense do the work for you.
Time to exand further about the topic: HttpContext.Cache.Add
Noob guide to Globalization in Asp.net MVC
Yea. Finally, I've done the Globalization after google and code for 4 hours, it's a pain because there are really (as of when I search) no specific post for this area. So basically in this post, i will gather everything I've done in my Globalization / Localization / Internationalization (whatever you called it is) and post here.
First step, we need to make a folder name: "App_GlobalResources", which you can create by right click your project, Add -> Add ASP.NET Folder -> App_GlobalResources
After you created the folder, we need to create Resource Files in this folder for the language. Basically, you can see the picture below have 3 files:
- Language.resx -> this is the default of the language, all the default translation must be in here.
- Language.en.resx -> this is the English file. It will override the default translations
- Language.vi.resx -> Vietnamese translation, used UTF-8 in here on an UTF-8 website
This is the Language.resx, which I just have to put the name because it actually doesn't matter, but if for precaution, you can always duplicate one of your language file and replace the Language.resx with it so you don't have to worry.

And here is the picture showing the file Language.en.resx. Which I have the Name and the Value

As you notice the {0} and {1} above, I will explain in the later part.
2nd, we need now is to create a new folder to contain our HtmlExtension and use this to output text string.
In my case, I created a Folder name Helper in my Project root.
Notice: these files are not mine, they are code from, i have modified a little from this source: http://blog.eworldui.net/post/2008/10/ASPNET-MVC-Simplified-Localization-via-ViewEngines.aspx . If ever you need to explain further about WebFormViewEngine or something, you can always go to their website. In my case, to make it as simple as possible, i will cut down this part.
LocalizationWebFormView.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | using System.IO; using System.Web.Mvc; namespace Project.Helper.Localization { public class LocalizationWebFormView : WebFormView { internal const string ViewPathKey = "__ViewPath__"; public LocalizationWebFormView(string viewPath) : base(viewPath) { } public LocalizationWebFormView(string viewPath, string masterPath) : base(viewPath, masterPath) { } public override void Render(ViewContext viewContext, TextWriter writer) { // there seems to be a bug with RenderPartial tainting the page's view data // so we should capture the current view path, and revert back after rendering string originalViewPath = (string) viewContext.ViewData[ViewPathKey]; viewContext.ViewData[ViewPathKey] = ViewPath; base.Render(viewContext, writer); viewContext.ViewData[ViewPathKey] = originalViewPath; } } } |
LocalizationWebFormViewEngine.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | using System.Web.Mvc; namespace Project.Helper.Localization { public class LocalizationWebFormViewEngine : WebFormViewEngine { protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) { return new LocalizationWebFormView(viewPath, masterPath); } protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath) { return new LocalizationWebFormView(partialPath, null); } } } |
LocalizationWebFormViewEngine.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | using System.Web.Mvc; namespace Project.Helper.Localization { public class LocalizationWebFormViewEngine : WebFormViewEngine { protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) { return new LocalizationWebFormView(viewPath, masterPath); } protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath) { return new LocalizationWebFormView(partialPath, null); } } } |
ResourceExtensions.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | using System.Globalization; using System.Web; using System.Web.Compilation; using System.Web.Mvc; using System; namespace P014.Helper.Localization { public static class ResourceExtensions { public static string Resource(this Controller controller, string expression, params object[] args) { ResourceExpressionFields fields = GetResourceFields(expression, "~/"); return GetGlobalResource(fields, args); } public static string Resource(this HtmlHelper htmlHelper, string expression, params object[] args) { string path = (string)htmlHelper.ViewData[LocalizationWebFormView.ViewPathKey]; if (string.IsNullOrEmpty(path)) path = "~/"; ResourceExpressionFields fields = GetResourceFields(expression, path); if (!string.IsNullOrEmpty(fields.ClassKey)) return GetGlobalResource(fields, args); return GetLocalResource(path, fields, args); } public static string Language(this HtmlHelper htmlHelper, string key, params object[] args) { //Replace Language with a name of your choice, if you have lang.en.resx, you should change the value here to "lang" string expression = "Language, " + key; return Resource(htmlHelper, expression, args); } static string GetLocalResource(string path, ResourceExpressionFields fields, object[] args) { try{ return string.Format((string)HttpContext.GetLocalResourceObject(path, fields.ResourceKey, CultureInfo.CurrentUICulture), args); } catch { return (string)HttpContext.GetLocalResourceObject(path, fields.ResourceKey, CultureInfo.CurrentUICulture); } } static string GetGlobalResource(ResourceExpressionFields fields, object[] args) { try{ return string.Format((string)HttpContext.GetGlobalResourceObject(fields.ClassKey, fields.ResourceKey, CultureInfo.CurrentUICulture), args); } catch { return (string)HttpContext.GetGlobalResourceObject(fields.ClassKey, fields.ResourceKey, CultureInfo.CurrentUICulture); } } static ResourceExpressionFields GetResourceFields(string expression, string virtualPath) { var context = new ExpressionBuilderContext(virtualPath); var builder = new ResourceExpressionBuilder(); return (ResourceExpressionFields)builder.ParseExpression(expression, typeof(string), context); } } } |
What I've added in this code is in the file ResourceExtensions.cs. Which i add a language helper to make you called out from your view easier. Remember to replace Language with a name of your choice, if you have lang.en.resx, you should change the value here to "lang".
Now for the program to finally fire up the Globalization / Localization for the webpage, we need to declare the culture for the program, either you can declare in web.config or you can write a little code in Global.asax
web.config approach:
<system .web> <globalization uiCulture="vi" /> </system>
Global.asax
protected void Application_BeginRequest(Object sender, EventArgs e) { // Application Level language // VI here is the language you want to set it on default. Thread.CurrentThread.CurrentUICulture = new CultureInfo("vi"); }
3rd, Configure.... We need to config the view of WFViewEngine to be able to find the Localization classes.
Open web.config and search for
/* Find: */ <namespaces> /* Add this after, where "Project.Helper.Localization" is the class of the files above. */ <add namespace="Project.Helper.Localization" /> </namespaces>
Finally! We've done most of it. You can now use the tag below to insert the translation text into the program.
< %= Html.Language("name") %>For the {0} {1} argument I've mention earlier, I will put some example here:
// name: argumenttest // value: This is first argument {0}, 2nd {1}, 3rd <strong>{3} </strong> < %= Html.Language("argumenttest", new [] { "First", "duc", "strong" } %> // output: This is first argument First, 2nd duc, 3rd strong
Now you can have a fully functional globalization / localization without a sweat!
I hope you enjoy this post
!
Builtin Async Controller in ASP.net MVC 2
Well, it's Christmas Eve at the time I post this post in my place. I hope you enjoy your Christmas with your family and happy new year to all of you who read this post of mine.
------------------------
One thing that make me feel more powerful when using ASP.net MVC is the newly introduce Asynchronous Controller Action.
Imagine, you have a front page with multiple widget, like news, weather, personal information, new forum thread etc... You are going to run it from top to bottom, query, query... Your basic solution for this is to query news first since it's kinda important, then weather, etc...
Why it's not a good solution? Because if you going to query everything like that, your program need to wait for each query to finish before continue to work with the code behind...
Better Solution? I could suggest you using Async Controller in this case. Separate each query into different Data Context, query to the database, Close Current Data Context, and process the info with code behind, separately and process at the same time. This helps you bring down processing time twice or thrice as fast than the normal approach...
Asynchronous Process has been introduce to ASP.net long before, but now, apply it to asp.net MVC even easier than what you really expect.
- Inherit to "AsyncController" instead of "Controller" class
- Write async method
- Write Complete method
- Write functions to handle async request
First step:
Look out for the head of your controller, you will always see this:
public class MyController : Controller
change it to:
public class MyController : AsyncController
Don't worry because everything will work just as normal even if you don't use AsyncController.
2nd Step, write the MethodAsync and MethodCompleted. The important is the prefix in this case, Async and Completed.
- Method is the action name, like Create, Delete, Details
- Async is in Void type, and everything request first will come to this void method, you can get the input from form, url here in this method. Async will pass parameter to Completed using AsyncManager.Parameters that you can see later in this article
- Completed is an ActionResult method. just like your normal Create, Delete action. You can return a View or RedirectResult here. The param of this method corresponding to the AsyncManager.Parameter that was used in MethodAsync
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | // Http://local/Controller/Method/ // As you can see i am using the attribute that can be use in the normal Actions [AcceptVerbs(HttpVerbs.Post), ActionName("Method")] public void MethodAsync(int id, string hello) { // This Increment() use to identify how many async process are there, // once everything is back to 0, MethodCompleted is fire-up AsyncManager.OutstandingOperations.Increment(); // This used to handle functions that are not Async Ready // since they don't have any async event ready. // using this you can start with any method. // the first int, string is the parameter data type, the last bool is the return type. // DoTestAsync is the method name that we going to write later on Func<int , string, bool> doTestHandler = DoTestAsync; // Begin async // ID, Hello is the param passing to method DoTestAsync doTestHandler.BeginInvoke(id, hello, ar => { var handler = (Func</int><int , string, bool>)ar.AsyncState; // AsyncManager.Parameters["returned"] now cantaining the result of // method DoTestAsync after finish the process. The datatype is bool. AsyncManager.Parameters["returned"] = handler.EndInvoke(ar); AsyncManager.OutstandingOperations.Decrement(); }, doTagHandler); } // This will process after the Async finished. The param of // this method is what we have used with AsyncManager.Parameters, // in this case, returned with type bool public ActionResult MethodCompleted(bool returned) { // You can actually make use of the param here to make some ViewData or something returned = !returned; // Since it's a ActionResult, i must return a View or something related. // return View(); -> will use the default view (Controller\Method.aspx) // i want to show some Content: return Content(returned.ToString()); } // Now we will write the method for our async controller public bool DoTestAsync(int a, string b) { // Everything you need to do with the param in here, // include query to your database or anything you can // posibly think of. For me, i will just return something. return true; } </int> |
The result of this is:
"False"
Isn't it simple, what you need is just to follow this and there you go, you can now have a Web application that run twice or thrice as fast.
Good luck...



