用C#代码使用Redis缓存

用C#代码使用Redis缓存

用C#代码使用Redis运行Manage NuGet包插件,找到ServiceStack.Redis包,并进行安装。

 

直接从实例化客户端使用Set/Get方法示例:
string host = "localhost";
string elementKey = "testKeyRedis";

using (RedisClient redisClient = new RedisClient(host))
{
      if (redisClient.Get<string>(elementKey) == null)
      {
           // adding delay to see the difference
           Thread.Sleep(5000); 
           // save value in cache
           redisClient.Set(elementKey, "some cached value");
      }
      // get value from the cache by key
      message = "Item value is: " + redisClient.Get<string>("some cached value");
 }

类型化实体集更有意思和更实用,这是因为它们操作的是确切类型的对象。在下面的代码示例中,有两个类分别定义为Phone和Person——phone的主人。每个phone实例引用它的主人。下面的代码演示我们如何通过标准添加、删除和发现缓存项:
public class Phone
{
   public int Id { get; set; }
   public string Model { get; set; }
   public string Manufacturer { get; set; }
   public Person Owner { get; set; }
}

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }
    public int Age { get; set; }
    public string Profession { get; set; }
}

using (RedisClient redisClient = new RedisClient(host))
{
     IRedisTypedClient<phone> phones = redisClient.As<phone>();
     Phone phoneFive = phones.GetValue("5");
     if (phoneFive == null)
     {
          // make a small delay
          Thread.Sleep(5000);
          // creating a new Phone entry
          phoneFive = new Phone
          {
               Id = 5,
               Manufacturer = "Motorolla",
               Model = "xxxxx",
               Owner = new Person
               {
                    Id = 1,
                    Age = 90,
                    Name = "OldOne",
                    Profession = "sportsmen",
                    Surname = "OldManSurname"
               }
          };
          // adding Entry to the typed entity set
          phones.SetEntry(phoneFive.Id.ToString(), phoneFive);
     }
     message = "Phone model is " + phoneFive.Manufacturer;
     message += "Phone Owner Name is: " + phoneFive.Owner.Name;
}

在上面的例子中,我们实例化了输入端IRedisTypedClient,它与缓存对象的特定类型——Phone类型一起工作。

Redis ASP.NET会话状态

要用Redis提供商配置ASP.NET会话状态,添加新文件到你的Web项目,命名为RedisSessionStateProvider.cs,可以从https://github.com/chadman/redis-service-provider/raw/master/RedisProvider/SessionProvider/RedisSessionProvider.cs复制代码,然后添加或更改配置文件中的以下部分(sessionState标签已经内置于system.web标签),或者你也可以下载附加来源和复制代码。
<sessionstate timeout="1" mode="Custom" 
customprovider="RedisSessionStateProvider" cookieless="false">
      <providers>
        <add name="RedisSessionStateProvider" writeexceptionstoeventlog="false" 
        type="RedisProvider.SessionProvider.CustomServiceProvider" 
        server="localhost" port="6379" password="pasword">
      </add> </providers>
</sessionstate>

注意,此密码是可以选择的,看服务器是否需要认证。它必须被真实的值替换或删除,如果Redis服务器不需要身份验证,那么服务器属性和端口得由具体的数值代替(默认端口为6379)。然后在项目中,你才可以使用会话状态:
// in the Global.asaxpublic class MvcApplication1 : System.Web.HttpApplication{    protected void Application_Start()    {        //....    }     protected void Session_Start()    {        Session["testRedisSession"] = "Message from the redis ression";    }}

在Home controller(主控制器):
public class HomeController : Controller
{
    public ActionResult Index()
    {
       //...
       ViewBag.Message = Session["testRedisSession"];
       return View();
    }
//...
}

结果:

 

ASP.Net输出缓存提供者,并且Redis可以用类似的方式进行配置。

Redis Set(集合)和List(列表)

主要要注意的是,Redis列表实现IList<T>,而Redis集合实现ICollection<T>。下面来说说如何使用它们。

当需要区分相同类型的不同分类对象时,使用列表。例如,我们有“mostSelling(热销手机)”和“oldCollection(回收手机)”两个列表:
string host = "localhost";using (var redisClient = new RedisClient(host)){    //Create a 'strongly-typed' API that makes all Redis Value operations to apply against Phones    IRedisTypedClient<phone> redis = redisClient.As<phone>();     IRedisList<phone> mostSelling = redis.Lists["urn:phones:mostselling"];    IRedisList<phone> oldCollection = redis.Lists["urn:phones:oldcollection"];     Person phonesOwner = new Person        {            Id = 7,            Age = 90,            Name = "OldOne",            Profession = "sportsmen",            Surname = "OldManSurname"        };     // adding new items to the list    mostSelling.Add(new Phone            {                Id = 5,                Manufacturer = "Sony",                Model = "768564564566",                Owner = phonesOwner            });     oldCollection.Add(new Phone            {                Id = 8,                Manufacturer = "Motorolla",                Model = "324557546754",                Owner = phonesOwner            });     var upgradedPhone  = new Phone    {        Id = 3,        Manufacturer = "LG",        Model = "634563456",        Owner = phonesOwner    };     mostSelling.Add(upgradedPhone);     // remove item from the list    oldCollection.Remove(upgradedPhone);     // find objects in the cache    IEnumerable<phone> LGPhones = mostSelling.Where(ph => ph.Manufacturer == "LG");     // find specific    Phone singleElement = mostSelling.FirstOrDefault(ph => ph.Id == 8);     //reset sequence and delete all lists    redis.SetSequence(0);    redisClient.Remove("urn:phones:mostselling");    redisClient.Remove("urn:phones:oldcollection");}

当需要存储相关的数据集和收集统计信息,例如answer -> queustion给答案或问题投票时,Redis集合就非常好使。假设我们有很多的问题(queustion)和答案(answer ),需要将它们存储在缓存中。使用Redis,我们可以这么做:
/// <summary>
/// Gets or sets the Redis Manager. The built-in IoC used with ServiceStack autowires this property.
/// </summary>
IRedisClientsManager RedisManager { get; set; }
/// <summary>
/// Delete question by performing compensating actions to 
/// StoreQuestion() to keep the datastore in a consistent state
/// </summary>
/// <param name="questionId">
public void DeleteQuestion(long questionId)
{
    using (var redis = RedisManager.GetClient())
    {
        var redisQuestions = redis.As<question>();

        var question = redisQuestions.GetById(questionId);
        if (question == null) return;

        //decrement score in tags list
        question.Tags.ForEach(tag => redis.IncrementItemInSortedSet("urn:tags", tag, -1));

        //remove all related answers
        redisQuestions.DeleteRelatedEntities<answer>(questionId);

        //remove this question from user index
        redis.RemoveItemFromSet("urn:user>q:" + question.UserId, questionId.ToString());

        //remove tag => questions index for each tag
        question.Tags.ForEach("urn:tags>q:" + tag.ToLower(), questionId.ToString()));

        redisQuestions.DeleteById(questionId);
    }
}

public void StoreQuestion(Question question)
{
    using (var redis = RedisManager.GetClient())
    {
        var redisQuestions = redis.As<question>();

        if (question.Tags == null) question.Tags = new List<string>();
        if (question.Id == default(long))
        {
            question.Id = redisQuestions.GetNextSequence();
            question.CreatedDate = DateTime.UtcNow;

            //Increment the popularity for each new question tag
            question.Tags.ForEach(tag => redis.IncrementItemInSortedSet("urn:tags", tag, 1));
        }

        redisQuestions.Store(question);
        redisQuestions.AddToRecentsList(question);
        redis.AddItemToSet("urn:user>q:" + question.UserId, question.Id.ToString());

        //Usage of tags - Populate tag => questions index for each tag
        question.Tags.ForEach(tag => redis.AddItemToSet
        ("urn:tags>q:" + tag.ToLower(), question.Id.ToString()));
    }
}

/// <summary>
/// Delete Answer by performing compensating actions to 
/// StoreAnswer() to keep the datastore in a consistent state
/// </summary>
/// <param name="questionId">
/// <param name="answerId">
public void DeleteAnswer(long questionId, long answerId)
{
    using (var redis = RedisManager.GetClient())
    {
        var answer = redis.As<question>().GetRelatedEntities<answer>
        (questionId).FirstOrDefault(x => x.Id == answerId);
        if (answer == null) return;

        redis.As<question>().DeleteRelatedEntity<answer>(questionId, answerId);

        //remove user => answer index
        redis.RemoveItemFromSet("urn:user>a:" + answer.UserId, answerId.ToString());
    }
}

public void StoreAnswer(Answer answer)
{
    using (var redis = RedisManager.GetClient())
    {
        if (answer.Id == default(long))
        {
            answer.Id = redis.As<answer>().GetNextSequence();
            answer.CreatedDate = DateTime.UtcNow;
        }

        //Store as a 'Related Answer' to the parent Question
        redis.As<question>().StoreRelatedEntities(answer.QuestionId, answer);
        //Populate user => answer index
        redis.AddItemToSet("urn:user>a:" + answer.UserId, answer.Id.ToString());
    }
}

public List<answer> GetAnswersForQuestion(long questionId)
{
    using (var redis = RedisManager.GetClient())
    {
        return redis.As<question>().GetRelatedEntities<answer>(questionId);
    }
}

public void VoteQuestionUp(long userId, long questionId)
{
    //Populate Question => User and User => Question set indexes in a single transaction
    RedisManager.ExecTrans(trans =>
    {
        //Register upvote against question and remove any downvotes if any
        trans.QueueCommand(redis => 
        redis.AddItemToSet("urn:q>user+:" + questionId, userId.ToString()));
        trans.QueueCommand(redis => 
        redis.RemoveItemFromSet("urn:q>user-:" + questionId, userId.ToString()));

        //Register upvote against user and remove any downvotes if any
        trans.QueueCommand(redis => 
        redis.AddItemToSet("urn:user>q+:" + userId, questionId.ToString()));
        trans.QueueCommand(redis => 
        redis.RemoveItemFromSet("urn:user>q-:" + userId, questionId.ToString()));
    });
}

public void VoteQuestionDown(long userId, long questionId)
{
    //Populate Question => User and User => Question set indexes in a single transaction
    RedisManager.ExecTrans(trans =>
    {
        //Register downvote against question and remove any upvotes if any
        trans.QueueCommand(redis => 
        redis.AddItemToSet("urn:q>user-:" + questionId, userId.ToString()));
        trans.QueueCommand(redis => 
        redis.RemoveItemFromSet("urn:q>user+:" + questionId, userId.ToString()));

        //Register downvote against user and remove any upvotes if any
        trans.QueueCommand(redis => 
        redis.AddItemToSet"urn:user>q-:" + userId, questionId.ToString()));
        trans.QueueCommand(redis => 
        redis.RemoveItemFromSet("urn:user>q+:" + userId, questionId.ToString()));
    });
}

public void VoteAnswerUp(long userId, long answerId)
{
    //Populate Question => User and User => Question set indexes in a single transaction
    RedisManager.ExecTrans(trans =>
    {
        //Register upvote against answer and remove any downvotes if any
        trans.QueueCommand(redis => 
        redis.AddItemToSet("urn:a>user+:" + answerId, userId.ToString()));
        trans.QueueCommand(redis => 
        redis.RemoveItemFromSet("urn:a>user-:" + answerId, userId.ToString()));

        //Register upvote against user and remove any downvotes if any
        trans.QueueCommand(redis => 
        redis.AddItemToSet("urn:user>a+:" + userId, answerId.ToString()));
        trans.QueueCommand(redis => 
        redis.RemoveItemFromSet("urn:user>a-:" + userId, answerId.ToString()));
    });
}

public void VoteAnswerDown(long userId, long answerId)
{
    //Populate Question => User and User => Question set indexes in a single transaction
    RedisManager.ExecTrans(trans =>
    {
        //Register downvote against answer and remove any upvotes if any
        trans.QueueCommand(redis => 
        redis.AddItemToSet("urn:a>user-:" + answerId, userId.ToString()));
        trans.QueueCommand(redis => 
        redis.RemoveItemFromSet("urn:a>user+:" + answerId, userId.ToString()));

        //Register downvote against user and remove any upvotes if any
        trans.QueueCommand(redis => 
        redis.AddItemToSet("urn:user>a-:" + userId, answerId.ToString()));
        trans.QueueCommand(redis => 
        redis.RemoveItemFromSet("urn:user>a+:" + userId, answerId.ToString()));
    });
}

public QuestionResult GetQuestion(long questionId)
{
    var question = RedisManager.ExecAs<question>
    (redisQuestions => redisQuestions.GetById(questionId));
    if (question == null) return null;

    var result = ToQuestionResults(new[] { question })[0];
    var answers = GetAnswersForQuestion(questionId);
    var uniqueUserIds = answers.ConvertAll(x => x.UserId).ToHashSet();
    var usersMap = GetUsersByIds(uniqueUserIds).ToDictionary(x => x.Id);

    result.Answers = answers.ConvertAll(answer =>
        new AnswerResult { Answer = answer, User = usersMap[answer.UserId] });

    return result;
}

public List<user> GetUsersByIds(IEnumerable<long> userIds)
{
    return RedisManager.ExecAs<user>(redisUsers => redisUsers.GetByIds(userIds)).ToList();
}

public QuestionStat GetQuestionStats(long questionId)
{
    using (var redis = RedisManager.GetReadOnlyClient())
    {
        var result = new QuestionStat
        {
            VotesUpCount = redis.GetSetCount("urn:q>user+:" +questionId),
            VotesDownCount = redis.GetSetCount("urn:q>user-:" + questionId)
        };
        result.VotesTotal = result.VotesUpCount - result.VotesDownCount;
        return result;
    }
}

public List<tag> GetTagsByPopularity(int skip, int take)
{
    using (var redis = RedisManager.GetReadOnlyClient())
    {
        var tagEntries = redis.GetRangeWithScoresFromSortedSetDesc("urn:tags", skip, take);
        var tags = tagEntries.ConvertAll(kvp => new Tag { Name = kvp.Key, Score = (int)kvp.Value });
        return tags;
    }
}

public SiteStats GetSiteStats()
{
    using (var redis = RedisManager.GetClient())
    {
        return new SiteStats
        {
            QuestionsCount = redis.As<question>().TypeIdsSet.Count,
            AnswersCount = redis.As<answer>().TypeIdsSet.Count,
            TopTags = GetTagsByPopularity(0, 10)
        };
    }
}

附加资源说明

项目中引用的一些包在packages.config文件中配置。

Funq IoC的相关配置,以及注册类型和当前控制器目录,在Global.asax文件中配置。

基于IoC的缓存使用以及Global.asax可以打开以下URL:http://localhost:37447/Question/GetQuestions?tag=test 查看。

你可以将tag字段设置成test3,test1,test2等。

Redis缓存配置——在web config文件(<system.web><sessionState>节点)以及RedisSessionStateProvider.cs文件中。

在MVC项目中有很多待办事项,因此,如果你想改进/继续,请更新,并上传。

如果有人能提供使用Redis(以及Funq IOC)缓存的MVC应用程序示例,本人将不胜感激。Funq IOC已经配置,使用示例已经在Question controller中。

注:部分取样于“ServiceStack.Examples-master”解决方案。

结论。优化应用程序缓存以及快速本地缓存

由于Redis并不在本地存储(也不在本地复制)数据,那么通过在本地缓存区存储一些轻量级或用户依赖的对象(跳过序列化字符串和客户端—服务端数据转换)来优化性能是有意义的。例如,在Web应用中,对于轻量级的对象使用’System.Runtime.Caching.ObjectCache‘
 会更好——用户依赖,并且应用程序时常要用。否则,当经常性地需要使用该对象时,就必须在分布式Redis缓存中存储大量容积的内容。用户依赖的对象举例——个人资料信息,个性化信息 。常用对象——本地化数据,不同用户之间的共享信息,等等。

下载源代码(Redis Funq LoC MVC 4版本)

链接

如何运行Redis服务:

https://github.com/kcherenkov/redis-windows-service

文档:

http://redis.io/documentation

.NET / C#示例:

https://github.com/ServiceStack/ServiceStack.Examples

关于如何用C#在Windows上使用Redis的好建议:

http://maxivak.com/getting-started-with-redis-and-asp-net-mvc-under-windows/:

http://www.piotrwalat.net/using-redis-with-asp-net-web-api/

关于Redis:

https://github.com/ServiceStack/ServiceStack.Redis

Azure缓存

http://kotugoroshko.blogspot.ae/2013/07/windows-azure-caching-integration.html
--------------------- 
作者:knight0513 
来源:CSDN 
原文:https://blog.csdn.net/knight0513/article/details/64922532 
版权声明:本文为博主原创文章,转载请附上博文链接!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值