Thursday, 17 September 2015

Redis from C# using a generic linq-based approach

Redis is a powerful cache that acts as a remote in-memory data structure store. It stands for Remote Dictionary Server. Redis allows advanced scenarios and multiple cache servers organized in hierarchies as a master with multiple slaves and supporting persistence. The code below is a generic Redis provider (class that follows the provider pattern) written in C# that makes it easier to use Redis. You will need to add an app setting to your application configuration file (app.config) called "RedisServer" with the hostname of Redis. If a non-standard port is used, just type ":<PORT>" e.g. "someredisserver.cloudapp.net:6784". Of course you need access to a Redis server to make this work. I have tested the C# code below against a Redis Server installed in Ubuntu 14. The Redis version is 2.6.16. The RedisMemoryProvider can be expanded by moving some functionality down to a base class. At the same time, the RedisNativeClient client class gives good support for running low-level operations in Redis. The code you move up a bit to a higher-level code such as RedisMemoryProvider of type T should be concerned around generics, while a non-generic base class can use the Set and Get methods below and so on. The C# console below uses ServiceStack.Redis Nuget package. I have not verified how this technology works against load tests, which will be interesting to write some Load Tests for.

Sample application configuration file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="RedisServer" value="someredisserver.cloudapp.net" />
  </appSettings>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
</configuration>

RedisMemoryProvider source code:


using ServiceStack.Redis;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace RedisProvider
{
    
    //Based on: http://stackoverflow.com/questions/30818784/generic-object-cache
    public class RedisMemoryProvider<T> where T : class
    {

        private static readonly PooledRedisClientManager m = new PooledRedisClientManager(new string[] { 
            ConfigurationManager.AppSettings["RedisServer"] });

        readonly IDictionary<Type, List<object>> _cache = new ConcurrentDictionary<Type, List<object>>();

        public RedisMemoryProvider()
        {
            LoadIntoCache<T>(); 
        }

        /// <summary>
        /// Load {T} into object cache from Data Store.
        /// </summary>
        /// <typeparam name="T">class</typeparam>
        private void LoadIntoCache<T>() where T : class
        {
            _cache[typeof(T)] = GetAll<T>().Cast<object>().ToList();
        }

        /// <summary>
        /// Find Single {T} in object cache.
        /// </summary>
        /// <typeparam name="T">class</typeparam>
        /// <param name="predicate">linq statement</param>
        /// <returns></returns>
        public T Read(Func<T, bool> predicate)
        {
            List<object> list;
            if (_cache.TryGetValue(typeof(T), out list))
            {
                return list.Cast<T>().Where(predicate).FirstOrDefault(); 
            }
            return null; 
        }

        /// <summary>
        /// Find List<T>(predicate) in cache.
        /// </summary>
        /// <typeparam name="T">class</typeparam>
        /// <param name="predicate">linq statement</param>
        /// <returns></returns>
        public List<T> FindBy<T>(Func<T, bool> predicate) where T : class
        {
            List<object> list;
            if (_cache.TryGetValue(typeof(T), out list))
            {
                return list.Cast<T>().Where(predicate).ToList();
            }
            return new List<T>();
        }

        public T FindById<T>(long id)
        {
            using (var ctx = m.GetClient())
            {
                T foundItem = ctx.GetById<T>(id);
                return foundItem; 
            }
        }

        public IList<T> FindByIds<T>(long[] ids)
        {
            using (var ctx = m.GetClient())
            {
                IList<T> foundItems = ctx.GetByIds<T>(ids);
                return foundItems;
            }
        }

        public void Create<T>(T entity) where T : class
        {
            List<object> list;
            if (!_cache.TryGetValue(typeof(T), out list))
            {
                list = new List<object>(); 
            }
            list.Add(entity);
            _cache[typeof(T)] = list;
            Store<T>(entity); 
        }

        /// <summary>
        /// Delete single {T} from cache and Data Store.
        /// </summary>
        /// <typeparam name="T">class</typeparam>
        /// <param name="entity">class object</param>
        public void Delete<T>(T entity) where T : class
        {
            List<object> list;
            if (_cache.TryGetValue(typeof(T), out list))
            {
                list.Remove(entity);
                _cache[typeof(T)] = list;

                RedisDelete<T>(entity);
            } 
           
        }


        public long Next<T>() where T : class
        {
            long id = 1;

            using (var ctx = m.GetClient())
            {
                try
                {
                    id = ctx.As<T>().GetNextSequence(); 
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message); 
                }
            }
            return id; 
        }

        public IList<T> GetAll<T>() where T : class
        {
            using (var ctx = m.GetClient())
            {
                try
                {
                    return ctx.As<T>().GetAll();
                }
                catch (Exception err)
                {
                    Debug.WriteLine(err.Message);
                    return new List<T>();
                }
            }
        }

        public void Update<T>(Func<T, bool> predicate, T entity) where T : class
        {
            List<object> list;

            if (_cache.TryGetValue(typeof(T), out list))
            {
                var existing = list.Cast<T>().FirstOrDefault(predicate);
                if (existing != null)
                    list.Remove(existing);
                list.Add(entity);
                _cache[typeof(T)] = list;
                Store<T>(entity); 
            }
        }

        public bool ExpireAt(string keyName, int expireInSeconds)
        {
            using (var client = new RedisNativeClient(ConfigurationManager.AppSettings["RedisServer"]))
            {
                return client.Expire(keyName, expireInSeconds); 
            }
        }

        public long GetTtl(string keyName)
        {
            using (var client = new RedisNativeClient(ConfigurationManager.AppSettings["RedisServer"]))
            {
                return client.Ttl(keyName);
            }
        }

        public void Set(string keyName, string content)
        {
            using (var client = new RedisNativeClient(ConfigurationManager.AppSettings["RedisServer"]))
            {
                client.Set(keyName, Encoding.UTF8.GetBytes(content));
            }
        }

        public string Get(string keyName)
        {
            using (var client = new RedisNativeClient(ConfigurationManager.AppSettings["RedisServer"]))
            {
                return Encoding.UTF8.GetString(client.Get(keyName));
            }
        }

        public IDictionary<string, string> GetInfo()
        {
            using (var client = new RedisNativeClient(ConfigurationManager.AppSettings["RedisServer"]))
            {
                return client.Info;
            }
        }

        public bool Ping()
        {
            using (var client = new RedisNativeClient(ConfigurationManager.AppSettings["RedisServer"]))
            {
                return client.Ping();
            }
        }

        #region Private methods 

        private void Store<T>(T entity) where T : class
        {
            using (var ctx = m.GetClient())
            {
                ctx.Store<T>(entity);                   
            }
        }

        private void RedisDelete<T>(T entity) where T : class
        {
            using (var ctx = m.GetClient())
            {
                ctx.As<T>().Delete(entity);
            }
        }

        private T Find<T>(long id) where T : class
        {
            using (var ctx = m.GetClient())
            {
                return ctx.As<T>().GetById(id); 
            }
        }

        #endregion 


    }
}


Sample console application using the RedisMemoryProvider:

using System;
using System.Linq;

namespace RedisProvider
{
    class Program
    {
        static void Main(string[] args)
        {

            RedisMemoryProvider<User> r = new RedisMemoryProvider<User>(); 

            // We do not touch sequence, by running example we can see that sequence will give Users new unique Id.

            // Empty data store.
            Console.WriteLine("Our User Data store should be empty.");
            Console.WriteLine("Users In \"Database\" : {0}\n", r.GetAll<User>().Count);

            // Add imaginary users.
            Console.WriteLine("Adding 30 imaginairy users.");
            for (int i = 0; i < 30; i++)
                r.Create<User>(new User { Id = r.Next<User>(), Name = "Joachim Nordvik" });

            // We should have 30 users in data store.
            Console.WriteLine("Users In \"Database\" : {0}\n", r.GetAll<User>().Count);

            // Lets print 10 users from data store.
            Console.WriteLine("Order by Id, Take (10) and print users.");
            foreach (var u in r.GetAll<User>().OrderBy(z => z.Id).Take(10))
            {
                Console.WriteLine("ID:{0}, Name: {1}", u.Id, u.Name);

                // Lets update an entity.
                u.Name = "My new Name";
                r.Update<User>(x => x.Id == u.Id, u);
            }

            // Lets print 20 users from data store, we already edited 10 users.
            Console.WriteLine("\nOrder by Id, Take (20) and print users, we previously edited the users that we printed lets see if it worked.");
            foreach (var u in r.GetAll<User>().OrderBy(z => z.Id).Take(20))
            {
                Console.WriteLine("ID:{0}, Name: {1}", u.Id, u.Name);
            }

            // Clean up data store.
            Console.WriteLine("\nCleaning up Data Store.\n");
            foreach (var u in r.GetAll<User>())
                r.Delete<User>(u);

            // Confirm that we no longer have any users.
            Console.WriteLine("Confirm that we no longer have User entities in Data Store.");
            Console.WriteLine("Users In \"Database\" : {0}\n\n", r.GetAll<User>().Count);

            //Do some misc additional test 
            r.Set("Dog", "Gomle");
            string dog = r.Get("Dog");
            Console.WriteLine("Key: Dog, Value: " + dog);
            r.ExpireAt("Dog", 11);
            long ttlDog = r.GetTtl("Dog");
            Console.WriteLine("Key: Dog, Expiration: " + ttlDog);

            var info = r.GetInfo();

            Console.WriteLine("INFO:"); 
            foreach (var x in info)
            {
                Console.WriteLine(x.Key + ": " + x.Value); 
            }

            Console.WriteLine("Hit return to exit!");
            Console.Read();

        }


        public class User
        {
            public long Id { get; set; }
            public string Name { get; set; }
        }


    }
}


So by using the code above, we can get started with using Redis.io in our C# based solutions much easier. The provider works on types, so your business entities will be divided into Sets in Redis.io according to their given type. Hopefully, this will cover many different uses. In addition, a local ConcurrentCache is kept to get quicker execution. Make note if you use this Redis provider in multi-tier environments such as load balanced clusters, you would want to refresh the cache now and then. The syncing of the content is of course not being kept if there are several writers to the cache. In that case, we might want to pump out events to reload the cache. Redis.io support both publish and subscribe, such that informing your consumers that the Redis cache is updated is a possibility. Redis is primarily being used for performance enhancement, but getting the cache to remain synced with a local ConcurrentCache above accross multiple tiers (nodes) will be a challenge.

88 comments:

  1. Thank you for the kind comment murali! I will look into using an Atom RSS feed or similar to provide a newsstream here.

    ReplyDelete
  2. Install-Package ServiceStack.Redis.Signed can be used.

    ReplyDelete
  3. Thank you for the kind comment murali! I will look into using an Atom RSS feed or similar to provide a newsstream here.
    base-sas training in chennai

    ReplyDelete
  4. In case you are looking into making money from your visitors by popunder ads - you should embed one of the most reputable networks - Clickadu.

    ReplyDelete
  5. I recommended your blog post very helpful to us.The amount of information in here is stunning like you practically wrote the book on the subject.
    Selenium Training in Chennai
    Selenium Automation Testing Training in Chennai

    ReplyDelete
  6. I have read your blog.Your step by step coding gives a clear explanation. Keep sharing more like this.
    SAS Training in Chennai | SAS Institutes in Chennai

    ReplyDelete
  7. Thanks for sharing informative article… Know about How to Change BSNL WiFi Password from techfizy.

    ReplyDelete
  8. Appreciating the persistence you put into your blog and detailed information you provide.
    Hadoop Training Institute In chennai

    amazon-web-services-training-in-bangalore

    ReplyDelete
  9. I believe there are many more pleasurable opportunities ahead for individuals that looked at your site.

    Final Year Iot Project Centers Chennai | ME Projects Chennai

    ReplyDelete
  10. Nice and good article. It is very useful for me to learn and understand easily.

    IEEE Electrical Projects in Chennai | IEEE Php Projects in Chennai.

    ReplyDelete
  11. I have read your blog its very attractive and impressive.I like it your blog.

    Wcf Training in Chennai | Software Testing Training in Chennai.

    ReplyDelete
  12. I simply wanted to write down a quick word to say thanks to you for those wonderful tips and hints you are showing on this site.

    ccna training in chennai



    ccna training in bangalore


    ccna training in pune

    ReplyDelete
  13. I just needed to record a speedy word to express profound gratitude to you for those magnificent tips and clues you are appearing on this site.
    health and safety courses in chennai

    ReplyDelete
  14. It’s always so sweet and also full of a lot of fun for me personally and my office colleagues to search you blog a minimum of thrice in a week to see the new guidance you have got.
    safety course in chennai

    ReplyDelete
  15. Thanks you for sharing this unique useful information content with us. Really awesome work. keep on blogging
    Selenium Training in Chennai | Selenium Training in Bangalore | Selenium Training in Pune | Selenium online Training

    ReplyDelete
  16. Wonderful blog!!! Thanks for your information… Waiting for your upcoming data.

    atstartups
    Technology

    ReplyDelete
  17. Really you have enclosed very good information's. it will educates lot of young students and please furnish more information's in future.
    devops training near me
    devops training in chennai
    devops Training in anna nagar
    devops Training in Chennai Anna Nagar

    ReplyDelete
  18. Awesome article. It is so detailed and well formatted that i enjoyed reading it as well as get some new information too.
    python course in pune
    python course in chennai
    python Training in Bangalore

    ReplyDelete
  19. After seeing your article I want to say that the presentation is very good and also a well-written article with some very good information which is very useful for the readers....thanks for sharing it and do share more posts like this.
    python Training institute in Pune
    python Training institute in Chennai
    python Training institute in Bangalore

    ReplyDelete
  20. I love the information you provide here and can’t wait to take a look when I get home. I’m surprised at how fast your blog loaded on my cell phone. I’m not even using WIFI, just 3G. Anyways, awesome blog!
    safety course in chennai

    ReplyDelete
  21. Thank you for taking the time to provide us with your valuable information. We strive to provide our candidates with excellent care and we take your comments to heart.As always, we appreciate your confidence and trust in us
    Data Science Training in Chennai
    Data Science course in anna nagar
    Data Science course in chennai
    Data science course in Bangalore
    Data Science course in marathahalli

    ReplyDelete
  22. I wish to show thanks to you just for bailing me out of this particular trouble.As a result of checking through the net and meeting techniques that were not productive, I thought my life was done.
    UI Path training in Chennai | UI Path training Chennai

    ReplyDelete
  23. I simply wanted to write down a quick word to say thanks to you for those wonderful tips and hints you are showing on this site.
    Dotnet training in Chennai | Dotnet training in Chennai

    ReplyDelete
  24. Really great information. Thanks for sharing. For latest jobs in India please visit TapResume
    jobs

    ReplyDelete
  25. 360DigiTMG, Indore is a leading solutions provider of Training and Consulting to assist students, professionals by delivering top-notch, world-class classroom and online training. It offers courses in artificial intelligence course in indore.

    ReplyDelete
  26. Great post i must say and thanks for the information. Education is definitely a sticky subject. However, is still among the leading topics of our time. I appreciate your post and look forward to more.
    iot course training in coimbatore

    ReplyDelete
  27. Really nice and interesting post. I was looking for this kind of information and enjoyed reading this one. Keep posting. Thanks for sharing.
    ethical hacking course training in guduvanchery

    ReplyDelete
  28. You completed a number of nice points there. I did a search on the issue and found nearly all people will have the same opinion with your blog.
    Data Science Training

    ReplyDelete
  29. I am a new user of this site so here i saw multiple articles and posts posted by this site,I curious more interest in some of them hope you will give more information on this topics in your next articles. Primavera Training in Chennai | Primavera online course

    ReplyDelete
  30. Great Article, Thanks for the nice information. Here I have a suggestion for the best learning of Digital Marketing Course. 99 Digital Academy is the best digital marketing training institute for advanced digital marketing course in Gurgaon.

    ReplyDelete
  31. Great Article, Thanks for the nice information. Here I have a suggestion for the Best Free Guest Posting Site List. These Guest Posting sites will help you in improving SEO of your website.

    ReplyDelete
  32. Thanks a lot for sharing kind of information. Your article provides such great information with good knowledge. Digital Marketing Courses in Pune with Placement

    ReplyDelete
  33. I curious more interest in some of them hope you will give more information on this topics in your next articles.
    Best Data Science courses in Hyderabad

    ReplyDelete
  34. Thanks for posting the best information and the blog is very importantdata science institutes in hyderabad

    ReplyDelete
  35. This blog have good information and it full concentrated in the topic. Thanks for sharing this kind of blog. Looking forward for more content.

    AWS Training in Hyderabad

    ReplyDelete
  36. I read this post,Keep sharing this information.

    SQL training in Pune

    ReplyDelete
  37. Ucuz, kaliteli ve organik sosyal medya hizmetleri satın almak için Ravje Medyayı tercih edebilir ve sosyal medya hesaplarını hızla büyütebilirsin. Ravje Medya ile sosyal medya hesaplarını organik ve gerçek kişiler ile geliştirebilir, kişisel ya da ticari hesapların için Ravje Medyayı tercih edebilirsin. Ravje Medya internet sitesine giriş yapmak için hemen tıkla: https://www.ravje.com

    İnstagram takipçi satın almak için Ravje Medya hizmetlerini tercih edebilir, güvenilir ve gerçek takipçilere Ravje Medya ile ulaşabilirsin. İnstagram takipçi satın almak artık Ravje Medya ile oldukça güvenilir. Hemen instagram takipçi satın almak için Ravje Medyanın ilgili sayfasını ziyaret et: instagram takipçi satın al

    Tiktok takipçi satın al istiyorsan tercihini Ravje Medya yap! Ravje Medya uzman kadrosu ve profesyonel ekibi ile sizlere Tiktok takipçi satın alma hizmetide sunmaktadır. Tiktok takipçi satın almak için hemen tıkla: tiktok takipçi satın al

    ReplyDelete
  38. I was reading your article and wondered if you had considered creating an ebook on this subject. Your writing would sell it fast. You have a lot of writing talent. Generic for viagra

    ReplyDelete
  39. This is actually the kind of information I have been trying to find. Thank you for writing this information. จีคลับ

    ReplyDelete
  40. I got too much interesting stuff on your blog. I guess I am not the only one having all the enjoyment here! Keep up the good work. ZHEWITRA 20 MG

    ReplyDelete
  41. Truly known as "the week's end pill", Tadalista 20 mg experiences its obvious quality. While the impact of different fixes if all else fails disappears in around 4 hours, a solitary piece of Tadalista 20 mg is persuading for as long as a day and a half. Tadalista 20 mg is according to a general point of view used to treat male Erectile Dysfunction (ED) and works by giving up the improvement of blood to the penis to concrete and save an erection for extra. Quantifiably, 6 out of 10 men who took a piece of Tadalista 20 mg said that they given out the impact as indicated by an overall perspective fundamentally more constantly speedier and got longer erections.
    https://www.theusameds.com/product/tadalista-20-mg/

    ReplyDelete
  42. This is such a great resource that you are providing and you give it away for free. I love seeing blog that understand the value. Im glad to have found this post as its such an interesting one! I am always on the lookout for quality posts and articles so i suppose im lucky to have found this! I hope you will be adding more in the future... Buy Vidalista

    ReplyDelete
  43. Positive site, where did u come up with the information on this posting?I have read a few of the articles on your website now, and I really like your style. Thanks a million and please keep up the effective work. https://www.hansfund.org

    ReplyDelete
  44. Thanks for sharing such great information. I also write about casino& sports betting online on my personal blog. kindly check out my page and learn more about online gambling! Thanks a lot admin! 파워볼 하는법

    ReplyDelete
  45. Great information, thanks for sharing it with us 슬롯머신

    ReplyDelete


  46. This post is so interactive and informative.keep update more information...
    Advantages of Machine Learning
    Benefits of Machine Learning

    ReplyDelete
  47. This post is so interactive and informative.keep update more information...
    AWS Training in Anna Nagar
    AWS Training in Chennai

    ReplyDelete
  48. Through this post, I realize that your great information in playing with all the pieces was exceptionally useful. I advise this is the primary spot where I discover issues I've been scanning for. You have a smart yet alluring method of composing.

    ReplyDelete
  49. Pleasant data, important and incredible structure, as offer great stuff with smart thoughts and ideas, loads of extraordinary data and motivation, the two of which I need, because of offer such an accommodating data here.

    ReplyDelete
  50. This is a great inspiring article.I am pretty pleased with your good work 온라인경마

    ReplyDelete
  51. Here are some links to web pages that we link to simply because we feel they’re really worth visiting. 릴게임

    ReplyDelete
  52. 360DigiTMG, the top-rated organisation among the most prestigious industries around the world, is an educational destination for those looking to pursue their dreams around the globe. The company is changing careers of many people through constant improvement, 360DigiTMG provides an outstanding learning experience and distinguishes itself from the pack. 360DigiTMG is a prominent global presence by offering world-class training. Its main office is in India and subsidiaries across Malaysia, USA, East Asia, Australia, Uk, Netherlands, and the Middle East.

    ReplyDelete
  53. İnsan böyle şeyler görünce mutlu oluyor

    ReplyDelete
  54. Fast forward your career with the best Data Analyst Course offered by 360DigiTMG.Gettrained by expert trainers with placement assistance.

    <a href="https://360digitmg.com/india/business-analytics-course-in-gorakhpur''>business analytics course in gorakhpur</a>

    ReplyDelete
  55. Fildena Professional 100 has been a game-changer for me. I've struggled with erectile dysfunction for years, and this medication has truly made a difference in my life. The effects are long-lasting and the medication is easy to take. It has improved my sexual performance and boosted my confidence in the bedroom. However, it's important to consult with a healthcare professional before starting any medication, as they can provide personalized advice and ensure it's safe for you. Overall, I highly recommend Fildena Professional 100 for anyone dealing with ED.

    ReplyDelete