小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

.NET中使用Redis (二)

 weijianian 2016-08-07

很久以前寫了一篇文章 .NET中使用Redis 介紹了如何安裝Redis服務(wù)端,以及如何在.NET中調(diào)用Redis讀取數(shù)據(jù)。本文簡單介紹如何設(shè)計(jì)NoSQL數(shù)據(jù)庫,以及如何使用Redis來存儲對象。

和傳統(tǒng)的關(guān)系型數(shù)據(jù)庫不同,NoSQL大部分都是以鍵值對存儲在內(nèi)存中的,我們不能直接把RDBMS里面的一些做法直接移植到NoSQL中來,一個(gè)最主要的原因是,在NoSQL中缺少RDBMS中的一些諸如join ,union以及一些在關(guān)系型數(shù)據(jù)庫中效率很高的執(zhí)行語句,這些在NoSQL不能很好的支持,或者說效率低。

下文首先通過例子介紹在SQLServer中設(shè)計(jì)一個(gè)DB系統(tǒng)以及與NoSQL環(huán)境中設(shè)計(jì)一個(gè)DB的區(qū)別,最后演示如何在Redis中對數(shù)據(jù)進(jìn)行讀寫操作。


一個(gè)簡單的博客系統(tǒng)

假設(shè)我們要設(shè)計(jì)一個(gè)簡單的博客系統(tǒng),用戶可以注冊一個(gè)博客(Blog),然后可以在上面寫文章(Post),文章可以分類(Category)以及添加標(biāo)簽(Tag),用戶可以對文章進(jìn)行評論(Comment)。

在該系統(tǒng)中,我們需要實(shí)現(xiàn),如下基本功能:


  • 首頁:顯示所有人的博客

  • 首頁:顯示最近的所有發(fā)表的文章

  • 首頁:顯示所有的最近的評論

  • 首頁:顯示博客的標(biāo)簽云

  • 首頁:顯示所有的分類

  • 文章頁面:顯示文章以及所有的評論

  • 文章頁面:添加評論

  • 標(biāo)簽頁面:顯示所有標(biāo)簽以及標(biāo)簽對應(yīng)的文章

  • 分類頁面:顯示所有分類以及分類對應(yīng)的文章


如果在SQLServer中,相信很簡單就可以設(shè)計(jì)出這樣一個(gè)DB了。




在NoSQL環(huán)境中,我們不能直接將上面的結(jié)構(gòu)搬進(jìn)來,所以需要根據(jù)需求重新設(shè)計(jì)我們的模型。


定義實(shí)體


在NoSQL環(huán)境下,所有的數(shù)據(jù)其實(shí)都是以key和value的形式存儲在內(nèi)存中的,value通常是序列化為字符串保存的。我們使用redis客戶端的時(shí)候,可以直接將對象存儲,這些客戶端在內(nèi)部實(shí)現(xiàn)上幫助我們進(jìn)行了序列化。所以第一步就是需要定義實(shí)體模型:

首先來看User實(shí)體:


public class User

{
public User()
{
this.BlogIds = new List();
}

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

}


User實(shí)體中,包含了用戶的Id,Name以及博客的Id。


然后Blog實(shí)體:


public class Blog

{
public Blog()
{
this.Tags = new List();
this.BlogPostIds = new List();
}

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

}


包含了標(biāo)簽Tag,以及文章Id列表。


文章BolgPost實(shí)體:


public class BlogPost

{
public BlogPost()
{
this.Categories = new List();
this.Tags = new List();
this.Comments = new List();
}

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

}


包含了一篇文章的基本信息,如文章分類,文章標(biāo)簽,文章的評論。


最后看評論BlogPostComment實(shí)體:


public class BlogPostComment

{
public string Content { get; set; }
public DateTime CreatedDate { get; set; }

}



具體實(shí)現(xiàn)


實(shí)體定義好了之后,我們就可以開始具體實(shí)現(xiàn)了。為了演示,這里通過單元測試的方式實(shí)現(xiàn)具體功能:

首先要把Redis的服務(wù)端啟動起來,然后在工程中新建一個(gè)Redis客戶端,之后的所有操作都通過這個(gè)客戶端進(jìn)行。


[TestFixture, Explicit, Category('Integration')]

public class BlogPostExample
{
readonly RedisClient redis = new RedisClient('localhost');

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

}


在單元測試的SetUp中,我們插入一些模擬數(shù)據(jù),插入數(shù)據(jù)的方法為InsetTestData方法:


public void InsertTestData()

{
var redisUsers = redis.As();
var redisBlogs = redis.As();
var redisBlogPosts = redis.As();

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 { 'Architecture', '.NET', 'Databases' },
};

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

var blogPosts = new List
{
new BlogPost
{
Id = redisBlogPosts.GetNextSequence(),
BlogId = yangBlog.Id,
Title = 'Memcache',
Categories = new List { 'NoSQL', 'DocumentDB' },
Tags = new List {'Memcache', 'NoSQL', 'JSON', '.NET'} ,
Comments = new List
{
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 { 'NoSQL', 'Cache' },
Tags = new List {'Redis', 'NoSQL', 'Scalability', 'Performance'},
Comments = new List
{
new BlogPostComment { Content = 'First Comment!', CreatedDate = DateTime.UtcNow,}
}
},
new BlogPost
{
Id = redisBlogPosts.GetNextSequence(),
BlogId = yangBlog.Id,
Title = 'Cassandra',
Categories = new List { 'NoSQL', 'Cluster' },
Tags = new List {'Cassandra', 'NoSQL', 'Scalability', 'Hashing'},
Comments = new List
{
new BlogPostComment { Content = 'First Comment!', CreatedDate = DateTime.UtcNow,}
}
},
new BlogPost
{
Id = redisBlogPosts.GetNextSequence(),
BlogId = zhangBlog.Id,
Title = 'Couch Db',
Categories = new List { 'NoSQL', 'DocumentDB' },
Tags = new List {'CouchDb', 'NoSQL', 'JSON'},
Comments = new List
{
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中創(chuàng)建了三個(gè)強(qiáng)類型的IRedisTypedClient類型的對象redisUsers,redisBlogs,redisBlogPosts來保存用戶信息,博客信息,和文字信息。


var yangUser = new User { Id = redisUsers.GetNextSequence(), Name = 'Eric Yang' };


在新建用戶的時(shí)候,因?yàn)镮d是自增字段,所以直接調(diào)用redisUsers這個(gè)client的GetNextSequence()方法就可以獲得一個(gè)自增的Id。


創(chuàng)建完用戶之后,接著創(chuàng)建博客信息:


var yangBlog = new Blog

{
Id = redisBlogs.GetNextSequence(),
UserId = yangUser.Id,
UserName = yangUser.Name,
Tags = new List { 'Architecture', '.NET', 'Databases' },

};


該博客有幾個(gè)標(biāo)簽。


在接著創(chuàng)建該博客上發(fā)表的若干篇文章:


var blogPosts = new List

{
new BlogPost
{
Id = redisBlogPosts.GetNextSequence(),
BlogId = yangBlog.Id,
Title = 'Memcache',
Categories = new List { 'NoSQL', 'DocumentDB' },
Tags = new List {'Memcache', 'NoSQL', 'JSON', '.NET'} ,
Comments = new List
{
new BlogPostComment { Content = 'First Comment!', CreatedDate = DateTime.UtcNow,},
new BlogPostComment { Content = 'Second Comment!', CreatedDate = DateTime.UtcNow,},
}
}

}


每一篇文章都有分類和標(biāo)簽,以及評論。


然后需要給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);


現(xiàn)在,利用Redis Desktop Manager,可以查看Reidis中存儲的數(shù)據(jù):




數(shù)據(jù)準(zhǔn)備好了之后,可以實(shí)現(xiàn)前面列出的一系列方法了:


顯示所有博客

該方法在GetAllBlogs中,實(shí)現(xiàn)如下:


[Test]

public void Show_a_list_of_blogs()
{
var redisBlogs = redis.As();
var blogs = redisBlogs.GetAll();
blogs.PrintDump();

}


只需要調(diào)用GetAll方法即可獲取內(nèi)存中的所有指定類型的對象。


輸出結(jié)果為:


[

{

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
]
}

]


顯示最近發(fā)表的文章和評論


實(shí)現(xiàn)如下:


[Test]

public void Show_a_list_of_recent_posts_and_comments()
{
//Get strongly-typed clients
var redisBlogPosts = redis.As();
var redisComments = redis.As();
{
//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 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);

//Print out the last 3 posts:
recentPosts.GetAll().PrintDump();
recentComments.GetAll().PrintDump();
}

}


方法中定義了兩個(gè)key為urn:BlogPost:RecentPosts 和 urn:BlogPostComment:RecentComments的 List對象來保存最近發(fā)表的文章和評論:recentPosts.Prepend(newBlogPost)方法表示將新創(chuàng)建的文章插到recentPosts列表的最前面。


Trim方法表示僅保留n個(gè)在集合中。


顯示博客的標(biāo)簽云

顯示博客的標(biāo)簽云方法如下:


[Test]

public void Show_a_TagCloud()
{
//Get strongly-typed clients
var redisBlogPosts = redis.As();
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();

}


顯示標(biāo)簽云的實(shí)現(xiàn),用到了redis中的SortedSet,IncrementItemInSortedSet表示如果有相同的話,值加一,GetRangeWithScoresFromSortedSetDesc方法,獲取某一key的前5個(gè)對象。


顯示所有的分類

顯示所有的分類用到了Set對象。


[Test]

public void Show_all_Categories()
{
var redisBlogPosts = redis.As();
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();

}


顯示文章以及其評論


實(shí)現(xiàn)如下:


[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();
var selectedBlogPost = redisBlogPosts.GetById(postId.ToString());

selectedBlogPost.PrintDump();

}


只需要把postId傳進(jìn)去就可以通過GetById的方法獲取內(nèi)存中的對象.


添加評論


首先根據(jù)PostId獲取BlogPost,然后在Comment屬性中添加一個(gè)BlogPostComment對象,然后在保存改BlogPost.


[Test]

public void Add_comment_to_existing_post()
{
var postId = 1;
var redisBlogPosts = redis.As();
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();

}


顯示分類以及分類對應(yīng)的文章


[Test]

public void Show_all_Posts_for_the_DocumentDB_Category()
{
var redisBlogPosts = redis.As();
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();

}


這里首先把所有的文章按照標(biāo)簽新建Set,把相同的分類的文章放到一個(gè)Set中,最后根據(jù)key即可查找到相應(yīng)的集合。


總結(jié)


本文利用一個(gè)簡單的博客系統(tǒng),簡要介紹了如何利用Redis存儲和獲取復(fù)雜的數(shù)據(jù)。由于本文主要為了演示如何與Redis進(jìn)行交互,所以實(shí)體設(shè)計(jì)的很簡陋,沒有按照DDD的思想進(jìn)行設(shè)計(jì),在某些設(shè)計(jì)方面沒有遵循前文淺談依賴注入中使用的原理和方法,后面會寫文章對該系統(tǒng)進(jìn)行重構(gòu)以使之更加完善。

希望本文對您了解如何利用Redis存儲復(fù)雜對象有所幫助。

參考資料

Designing NoSql Database
Migrations Using Schemaless NoSql
That No SQL Thing: The relational modeling anti pattern in document databases


原文出處:寒江獨(dú)釣

原文鏈接:http://www.cnblogs.com/yangecnu/p/Introduct-Redis-in-DotNET-Part2.html



    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多