Wednesday, November 20, 2013

Memcached in MVC projects

1. Install Memcached server in Windows

The latest Memcached version is 1.4.5, but the application doesn't support "-d install" parameter to install itself as Windows service.

So right now the 1.4.4 is still the option to go under Windows. Get a package from memcached 1.4.4 for Windows 32-bit from http://downloads.northscale.com/memcached-win32-1.4.4-14.zip.

Unzipped to a folder, then run the following command to install as win services
memcached.exe -d install

Start the server from the Microsoft Management Console or by running one of the following commands:
memcached.exe -d start

2. Check Memcached installation

You can telnet to the default port 11211 to check the status:
telnel localhost 11211

Then use the command stats to check status
stats
flush_all

3. Enyim Memcached client

EnyimMemcached is a .NET client library for memcached written in C#. You can get the source code from https://github.com/enyim/EnyimMemcached

Then it's simple to use the cache:

private static IMemcachedClient _cache = new MemcachedClient();

_cache.Store(StoreMode.Set, key, value);

object r = _cache.Get(key);
Of course you need some setting in the app/web.config:


  
    
      
You don't need log section if you are not doing the detail logging. Also the transcoder section is added here because I'm using the DataContractSerializer for my entities.

4. Some notes

On Azure, window only support the default protocol (Text).
 
The Enyim source code getting from Github is using log4net 1.2.10.0 targeting at .Net framework 3.5. If you are using log4net 1.2.12.0 (targeting at .Net framework 4.0) then there are some dll binding issue.

In order to re-compile the project to reference log4net 1.2.12.0 and targeting .Net framework 4.0, I end up disable the sign and removing the strong name sign public key file public_key.snk from the project, also remove the reference part in the CommonProperties.targets file under build folder.



Friday, October 18, 2013

Send HTML confirmation email with image part

CreateMail function:
        

        private MailMessage CreateMail(EmailArchive emailArchive, bool useCCAddress, bool isBodyHtml)
        {
            MailMessage mailMsg = new MailMessage();

            // From
            MailAddress mailAddress = new MailAddress(emailArchive.FromAddress, emailArchive.FromName);
            mailMsg.From = mailAddress;

            // Subject and Body
            mailMsg.Subject = emailArchive.Subject;

            //mailMsg.Body = emailArchive.Body;
            EmbedImages(mailMsg, emailArchive.Body);

            mailMsg.IsBodyHtml = isBodyHtml;

            // To

            if (useCCAddress)
            {
                if (string.IsNullOrEmpty(emailArchive.CCAddress))
                    mailMsg = null;
                else
                    mailMsg.To.Add(new MailAddress(emailArchive.CCAddress, emailArchive.CCName));

            }
            else
            {
                mailMsg.To.Add(new MailAddress(emailArchive.ToAddress, emailArchive.ToName));
            }

            return mailMsg;
        }

The following pattern will match all img tag in the html source code:
string pattern = "<img.+?src=\"(.+?)\".+?/?>";
EmbedImages function:

        /// 
        /// The function only deal with src like "/image/file.jpg"
        /// 
        /// 
        /// 
        public static void EmbedImages(MailMessage mailMessage, string body)
        {
            // Check config file
            string path = ConfigurationManager.AppSettings["WebSiteRootFolder"];
            AlternateView av;
            if (string.IsNullOrEmpty(path))
            {
                ShopLogging.Warn("Can not find [WebSiteRootFolder] setting in config file!!!");

                //path = "D:\\User\\up\\";
                av = AlternateView.CreateAlternateViewFromString(string.Format("{0}", body), null, MediaTypeNames.Text.Html);
                mailMessage.AlternateViews.Add(av);

                return;
            }


            if (path.Right(1) == "\\")
                path = path.Left(path.Length - 1);

            List links = new List();
            LinkedResource res;
            string file, id, ext, cid;
            int i = 1;

            // Find all image tag
            // 
            string pattern = "";

            body = Regex.Replace(body, pattern, delegate(Match tag)
            {
                string v = tag.Value;
                file = tag.Groups[1].Value;

                // The function only deal with src like "/image/file.jpg"
                // If the src is not absolute path to "/", skip
                if (file.Left(1) != "/")
                    return v;

                // Replace virtual path to physical path
                file = file.Replace('/', '\\');
                ext = Path.GetExtension(file);
                if (ext.Left(1) == ".")
                    ext = ext.Substring(1);

                // Generate mail path ID
                id = Path.GetFileName(file);
                id = i.ToString() + "_" + id.UrlClean().Replace('.', '_');

                // Add root folder
                if (file.Left(1) == "\\")
                    file = file.Substring(1);
                file = path + "\\" + file;
                cid = string.Format("cid:{0}", id);

                // Add Linked Recource
                res = new LinkedResource(file, string.Format("image/{0}", ext));
                res.ContentId = id;
                links.Add(res);

                //body = body.Replace(tag.Groups[1].Value, string.Format("cid:{0}", id));
                i++;
                v = v.Replace(tag.Groups[1].Value, cid);
                return v;
            });

            av = AlternateView.CreateAlternateViewFromString(string.Format("{0}", body), null, MediaTypeNames.Text.Html);
            links.ForEach(l => av.LinkedResources.Add(l));
            mailMessage.AlternateViews.Add(av);

        }

Monday, September 16, 2013

SessionState using SQL Server

1. Install database scheme

C:\Windows\Microsoft.NET\Framework\v4.0.30319>

aspnet_regsql.exe -sstype c -ssadd -d DatabaseName -U username -P password  -S xx.xx.xx.xxx



2. Config

    < sessionState mode="SQLServer" sqlConnectionString="data source=DatabaseName ;user id=username ;password=password" cookieless="UseCookies" timeout="20"/>

Remember to give the user dbowner permission.

3.  ASP.NET Universal Providers

 ASP.NET Universal Providers that will extend Session, Membership, Roles and Profile support to SQL Compact Edition and SQL Azure. Other than supporting additional storage options, the providers work like the existing SQL-based providers.

    < sessionState mode="Custom" customProvider="DefaultSessionProvider" allowCustomSqlDatabase="true">
      < providers>
        < add name="DefaultSessionProvider" type="System.Web.Providers.DefaultSessionStateProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" />
      < /providers>
    < /sessionState>

See here for detail about Azure config.