.NET中使用Redis

一个简单的博客系统
假设我们要设计一个简单的博客系统,用户可以注册一个博客(Blog),然后可以在上面写文章(Post),文章可以分类(Category)以及添加标签(Tag),用户可以对文章进行评论(Comment)。

在该系统中,我们需要实现,如下基本功能:

首页:显示所有人的博客
首页:显示最近的所有发表的文章
首页:显示所有的最近的评论
首页:显示博客的标签云
首页:显示所有的分类
文章页面:显示文章以及所有的评论
文章页面:添加评论
标签页面:显示所有标签以及标签对应的文章
分类页面:显示所有分类以及分类对应的文章
如果在SQLServer中,相信很简单就可以设计出这样一个DB了。

Blog database in SQLServer

在NoSQL环境中,我们不能直接将上面的结构搬进来,所以需要根据需求重新设计我们的模型。

定义实体
在NoSQL环境下,所有的数据其实都是以key和value的形式存储在内存中的,value通常是序列化为字符串保存的。我们使用redis客户端的时候,可以直接将对象存储,这些客户端在内部实现上帮助我们进行了序列化。所以第一步就是需要定义实体模型:

首先来看User实体:

public class User
{
public User()
{
this.BlogIds = new List<long>();
}

public long Id { get; set; }
public string Name { get; set; }
public List<long> BlogIds { get; set; }

}
User实体中,包含了用户的Id,Name以及博客的Id。

然后Blog实体:

public class Blog
{
public Blog()
{
this.Tags = new List<string>();
this.BlogPostIds = new List<long>();
}

public long Id { get; set; }
public long UserId { get; set; }
public string UserName { get; set; }
public List<string> Tags { get; set; }
public List<long> BlogPostIds { get; set; }

}
包含了标签Tag,以及文章Id列表。

文章BolgPost实体:

public class BlogPost
{
public BlogPost()
{
this.Categories = new List<string>();
this.Tags = new List<string>();
this.Comments = new List<BlogPostComment>();
}

public long Id { get; set; }
public long BlogId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public List<string> Categories { get; set; }
public List<string> Tags { get; set; }
public List<BlogPostComment> Comments { get; set; }

}
包含了一篇文章的基本信息,如文章分类,文章标签,文章的评论。

最后看评论BlogPostComment实体:

public class BlogPostComment
{
public string Content { get; set; }
public DateTime CreatedDate { get; set; }
}
class diagrame in Redis

具体实现
实体定义好了之后,我们就可以开始具体实现了。为了演示,这里通过单元测试的方式实现具体功能:

首先要把Redis的服务端启动起来,然后在工程中新建一个Redis客户端,之后的所有操作都通过这个客户端进行。

[TestFixture, Explicit, Category("Integration")]
public class BlogPostExample
{
readonly RedisClient redis = new RedisClient("localhost");

[SetUp]
public void OnBeforeEachTest()
{
    redis.FlushAll();
    InsertTestData();
}

}
在单元测试的SetUp中,我们插入一些模拟数据,插入数据的方法为InsetTestData方法:

public void InsertTestData()
{
var redisUsers = redis.As<User>();
var redisBlogs = redis.As<Blog>();
var redisBlogPosts = redis.As<BlogPost>();

var yangUser = new User { Id = redisUsers.GetNextSequence(), Name = "Eric Yang" };
var zhangUser = new User { Id = redisUsers.GetNextSequence(), Name = "Fish Zhang" };

var yangBlog = new Blog
{
    Id = redisBlogs.GetNextSequence(),
    UserId = yangUser.Id,
    UserName = yangUser.Name,
    Tags = new List<string> { "Architecture", ".NET", "Databases" },
};

var zhangBlog = new Blog
{
    Id = redisBlogs.GetNextSequence(),
    UserId = zhangUser.Id,
    UserName = zhangUser.Name,
    Tags = new List<string> { "Architecture", ".NET", "Databases" },
};

var blogPosts = new List<BlogPost>
{
    new BlogPost
    {
        Id = redisBlogPosts.GetNextSequence(),
        BlogId = yangBlog.Id,
        Title = "Memcache",
        Categories = new List<string> { "NoSQL", "DocumentDB" },
        Tags = new List<string> {"Memcache", "NoSQL", "JSON", ".NET"} ,
        Comments = new List<BlogPostComment>
        {
            new BlogPostComment { Content = "First Comment!", CreatedDate = DateTime.UtcNow,},
            new BlogPostComment { Content = "Second Comment!", CreatedDate = DateTime.UtcNow,},
        }
    },
    new BlogPost
    {
        Id = redisBlogPosts.GetNextSequence(),
        BlogId = zhangBlog.Id,
        Title = "Redis",
        Categories = new List<string> { "NoSQL", "Cache" },
        Tags = new List<string> {"Redis", "NoSQL", "Scalability", "Performance"},
        Comments = new List<BlogPostComment>
        {
            new BlogPostComment { Content = "First Comment!", CreatedDate = DateTime.UtcNow,}
        }
    },
    new BlogPost
    {
        Id = redisBlogPosts.GetNextSequence(),
        BlogId = yangBlog.Id,
        Title = "Cassandra",
        Categories = new List<string> { "NoSQL", "Cluster" },
        Tags = new List<string> {"Cassandra", "NoSQL", "Scalability", "Hashing"},
        Comments = new List<BlogPostComment>
        {
            new BlogPostComment { Content = "First Comment!", CreatedDate = DateTime.UtcNow,}
        }
    },
    new BlogPost
    {
        Id = redisBlogPosts.GetNextSequence(),
        BlogId = zhangBlog.Id,
        Title = "Couch Db",
        Categories = new List<string> { "NoSQL", "DocumentDB" },
        Tags = new List<string> {"CouchDb", "NoSQL", "JSON"},
        Comments = new List<BlogPostComment>
        {
            new BlogPostComment {Content = "First Comment!", CreatedDate = DateTime.UtcNow,}
        }
    },
};

yangUser.BlogIds.Add(yangBlog.Id);
yangBlog.BlogPostIds.AddRange(blogPosts.Where(x => x.BlogId == yangBlog.Id).Map(x => x.Id));

zhangUser.BlogIds.Add(zhangBlog.Id);
zhangBlog.BlogPostIds.AddRange(blogPosts.Where(x => x.BlogId == zhangBlog.Id).Map(x => x.Id));

redisUsers.Store(yangUser);
redisUsers.Store(zhangUser);
redisBlogs.StoreAll(new[] { yangBlog, zhangBlog });
redisBlogPosts.StoreAll(blogPosts);

}
在方法中,首先在Redis中创建了三个强类型的IRedisTypedClient类型的对象redisUsers,redisBlogs,redisBlogPosts来保存用户信息,博客信息,和文字信息。

var yangUser = new User { Id = redisUsers.GetNextSequence(), Name = "Eric Yang" };
在新建用户的时候,因为Id是自增字段,所以直接调用redisUsers这个client的GetNextSequence()方法就可以获得一个自增的Id。

创建完用户之后,接着创建博客信息:

var yangBlog = new Blog
{
Id = redisBlogs.GetNextSequence(),
UserId = yangUser.Id,
UserName = yangUser.Name,
Tags = new List<string> { "Architecture", ".NET", "Databases" },
};
该博客有几个标签。

在接着创建该博客上发表的若干篇文章:

var blogPosts = new List<BlogPost>
{
new BlogPost
{
Id = redisBlogPosts.GetNextSequence(),
BlogId = yangBlog.Id,
Title = "Memcache",
Categories = new List<string> { "NoSQL", "DocumentDB" },
Tags = new List<string> {"Memcache", "NoSQL", "JSON", ".NET"} ,
Comments = new List<BlogPostComment>
{
new BlogPostComment { Content = "First Comment!", CreatedDate = DateTime.UtcNow,},
new BlogPostComment { Content = "Second Comment!", CreatedDate = DateTime.UtcNow,},
}
}
}
每一篇文章都有分类和标签,以及评论。

然后需要给user的BlogsIds和blog的BlogPostIds赋值

yangUser.BlogIds.Add(yangBlog.Id);
yangBlog.BlogPostIds.AddRange(blogPosts.Where(x => x.BlogId == yangBlog.Id).Map(x => x.Id));
最后需要把这些信息保存到redis中。

//保存用户信息
redisUsers.Store(yangUser);
redisUsers.Store(zhangUser);
//保存博客信息
redisBlogs.StoreAll(new[] { yangBlog, zhangBlog });
//保存所有的文章信息
redisBlogPosts.StoreAll(blogPosts);
现在,利用Redis Desktop Manager,可以查看Reidis中存储的数据:

PostSystemDB

数据准备好了之后,可以实现前面列出的一系列方法了:

显示所有博客
该方法在GetAllBlogs中,实现如下:

[Test]
public void Show_a_list_of_blogs()
{
var redisBlogs = redis.As<Blog>();
var blogs = redisBlogs.GetAll();
blogs.PrintDump();
}
只需要调用GetAll方法即可获取内存中的所有指定类型的对象。

输出结果为:

[
{

    Id: 1,
    UserId: 1,
    UserName: Eric Yang,
    Tags: 
    [
        Architecture,
        .NET,
        Databases
    ],
    BlogPostIds: 
    [
        1,
        3
    ]
},
{
    Id: 2,
    UserId: 2,
    UserName: Fish Zhang,
    Tags: 
    [
        Architecture,
        .NET,
        Databases
    ],
    BlogPostIds: 
    [
        2,
        4
    ]
}

]
显示最近发表的文章和评论
实现如下:

[Test]
public void Show_a_list_of_recent_posts_and_comments()
{
//Get strongly-typed clients
var redisBlogPosts = redis.As<BlogPost>();
var redisComments = redis.As<BlogPostComment>();
{
//To keep this example let's pretend this is a new list of blog posts
var newIncomingBlogPosts = redisBlogPosts.GetAll();

    //Let's get back an IList<BlogPost> wrapper around a Redis server-side List.
    var recentPosts = redisBlogPosts.Lists["urn:BlogPost:RecentPosts"];
    var recentComments = redisComments.Lists["urn:BlogPostComment:RecentComments"];

    foreach (var newBlogPost in newIncomingBlogPosts)
    {
        //Prepend the new blog posts to the start of the 'RecentPosts' list
        recentPosts.Prepend(newBlogPost);

        //Prepend all the new blog post comments to the start of the 'RecentComments' list
        newBlogPost.Comments.ForEach(recentComments.Prepend);
    }

    //Make this a Rolling list by only keep the latest 3 posts and comments
    recentPosts.Trim(0, 2);
    recentComments.Trim(0, 2);

www.81rz.com/bmsj31/
//Print out the last 3 posts:
recentPosts.GetAll().PrintDump();
recentComments.GetAll().PrintDump();
}
}
方法中定义了两个key为urn:BlogPost:RecentPosts 和 urn:BlogPostComment:RecentComments的 List对象来保存最近发表的文章和评论:recentPosts.Prepend(newBlogPost)方法表示将新创建的文章插到recentPosts列表的最前面。

Trim方法表示仅保留n个在集合中。

显示博客的标签云
显示博客的标签云方法如下:

[Test]
public void Show_a_TagCloud()
{
//Get strongly-typed clients
var redisBlogPosts = redis.As<BlogPost>();
var newIncomingBlogPosts = redisBlogPosts.GetAll();

foreach (var newBlogPost in newIncomingBlogPosts)
{
    //For every tag in each new blog post, increment the number of times each Tag has occurred 
    newBlogPost.Tags.ForEach(x =>
        redis.IncrementItemInSortedSet("urn:TagCloud", x, 1));
}

//Show top 5 most popular tags with their scores
var tagCloud = redis.GetRangeWithScoresFromSortedSetDesc("urn:TagCloud", 0, 4);
tagCloud.PrintDump();

}
显示标签云的实现,用到了redis中的SortedSet,IncrementItemInSortedSet表示如果有相同的话,值加一,GetRangeWithScoresFromSortedSetDesc方法,获取某一key的前5个对象。

显示所有的分类
显示所有的分类用到了Set对象。

[Test]
public void Show_all_Categories()
{
var redisBlogPosts = redis.As<BlogPost>();
var blogPosts = redisBlogPosts.GetAll();

foreach (var blogPost in blogPosts)
{
    blogPost.Categories.ForEach(x =>
            redis.AddItemToSet("urn:Categories", x));
}

var uniqueCategories = redis.GetAllItemsFromSet("urn:Categories");
uniqueCategories.PrintDump();

}
显示文章以及其评论
实现如下:

[Test]
public void Show_post_and_all_comments()
{
//There is nothing special required here as since comments are Key Value Objects
//they are stored and retrieved with the post
var postId = 1;
var redisBlogPosts = redis.As<BlogPost>();
var selectedBlogPost = redisBlogPosts.GetById(postId.ToString());

selectedBlogPost.PrintDump();

}
只需要把postId传进去就可以通过GetById的方法获取内存中的对象.

添加评论
首先根据PostId获取BlogPost,然后在Comment属性中添加一个BlogPostComment对象,然后在保存改BlogPost.

[Test]
public void Add_comment_to_existing_post()
{
var postId = 1;
var redisBlogPosts = redis.As<BlogPost>();
var blogPost = redisBlogPosts.GetById(postId.ToString());
blogPost.Comments.Add(
new BlogPostComment { Content = "Third Post!", CreatedDate = DateTime.UtcNow });
redisBlogPosts.Store(blogPost);

var refreshBlogPost = redisBlogPosts.GetById(postId.ToString());
refreshBlogPost.PrintDump();

}
显示分类以及分类对应的文章
[Test]
public void Show_all_Posts_for_the_DocumentDB_Category()
{
var redisBlogPosts = redis.As<BlogPost>();
var newIncomingBlogPosts = redisBlogPosts.GetAll();

foreach (var newBlogPost in newIncomingBlogPosts)
{
    //For each post add it's Id into each of it's 'Cateogry > Posts' index
    newBlogPost.Categories.ForEach(x =>
            redis.AddItemToSet("urn:Category:" + x, newBlogPost.Id.ToString()));
}

//Retrieve all the post ids for the category you want to view
var documentDbPostIds = redis.GetAllItemsFromSet("urn:Category:DocumentDB");

//Make a batch call to retrieve all the posts containing the matching ids 
//(i.e. the DocumentDB Category posts)
var documentDbPosts = redisBlogPosts.GetByIds(documentDbPostIds);

documentDbPosts.PrintDump();

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值