您好,欢迎访问一九零五行业门户网

RavenDB在传统C/S应用下的一点实践

ravendb介绍 ravendb是一个基于.net开发的nosql数据库。下面是官方介绍的一个简单翻译: ravendb is a transactional, open-source document database written in .net, offering a flexible data model designed to address requirements coming from real-
ravendb介绍
ravendb是一个基于.net开发的nosql数据库。下面是官方介绍的一个简单翻译:
ravendb is a transactional, open-source document database written in .net, offering a flexible data model designed to address requirements coming from real-world systems.
ravendb allows you to build high-performance, low-latency applications quickly and efficiently.
ravendb是一个用.net编写的事务性开源文档数据库,提供灵活的数据模型,设计用于解决来自真实世界系统的需求。
ravendb允许你快速而高效地构建高性能、低延迟的应用程序。
更多介绍可以浏览官方网站的介绍: 场景介绍
由于nosql一般是用于web场景,比如web应用程序(尤其mvc web应用程序),或者web服务(包括rest服务等)。最近,香港服务器租用,需要实现一个简单的数据编辑工具,不过由于某些原因,这个工具必须和一个桌面的windows forms应用程序集成在一起,且也要满足多个用户同时操作数据的需求。对于这种标准的c/s模式的应用,能否使用ravendb这样的nosql来作为server端的数据库呢?
答案当然是可以的。毕竟ravendb本身就支持两种运行模式:嵌入模式(embedded)和服务器模式(server)。对于c/s的应用,很自然就是把ravendb部署在一个服务器上,运行于server模式,然后在客户端通过.net client api来访问。
遇到问题 在这个c/s应用程序中使用ravendb的过程中,遇到的最大的问题,还是ravendb本身的一些特性所带来的限制,分别为:
每次获取的数据量有限制。ravendb规定每次获取的数据量默认为128条,最多可配置为1024条。对于我这个工具的数据量,就是5000条左右,其实如果使用其他数据库技术的话(比如entity framework),且也在局域网内,完全可以一次性载入到内存中。然而使用ravendb就必须考虑分页处理。 每个session能够调用的次数有限制。ravendb规定每个session调用服务端的最大次数是30,并且推荐最好控制在1次左右。由于有这样的规定,就无法在整个客户端应用程序的生存期内保持一个共享的session。 对于ef也不存在这样的限制。 搜索是基于lucene的。对于字符串进行contain操作会出错,这是由于对于类似的全文搜索,ravendb都是依赖于lucene的。因而需要预先定义搜索的索引,并使用单独的search方法。 ravendb内置的lucene分词器对于中文的支持有问题。就需要单独使用其他中文分词器。 解决方式 针对以上的限制,并结合我这个c/s小工具的一些特点,使用了如下解决方式:
结合bindingnavigator和bindingsource,编写了一个自动分页的工具类(ravendbdatasource),可以让bindingnavigator的前后导航按钮实现分页导航,还可以支持条件过滤(where)和全文搜索(search)后的分页。具体用法见下“ravendbdatasource解析和用法”。 虽然不能保持一个共享的session,但是可以保持一个共享的store对象,在每次需要获取数据或更新数据的时候,创建单独的session。不过需要注意的是,由于没有共享session,会导致之前取回的数据丢失变更跟踪,需要自己进行跟踪与提交。见下面的“如何保存数据变更”。 我从lucene.net的网站下载了contri包,直接使用了里面的“lucene.net.analysis.cn.chineseanalyzer”,即把lucene.net.contrib.analyzers.dll文件放到ravendb\server\analyzers目录里面。把当然有兴趣的同学也可以使用ictclas的lucene实现。 预定义全文搜索索引的话,我的方式是在连接数据库后,检查是否存在所需索引,不存在就用代码创建。当然也可以通过studio来创建。见下”创建索引”。
ravendbdatasource解析和用法 代码见:https://github.com/heavenwing/redmoon/blob/master/ravendbdatasource.cs
这个类提供了一个构造器public ravendbdatasource(idocumentstore store, bindingnavigator bn, bindingsource bs),可以接受idocumentstore 、bindingnavigator 和bindingsource 作为参数。其中会对bn进行一些初始化处理。
提供了一个重载的load方法,可以无参数,或者接受func, iravenqueryable> criteria, string indexname = 两个参数。criteria用来对查询进行构造,indexname顾名思义,在进行search操作的时候就需要传入预先定义的索引的名称。在load方法中,会对调用代码构造好的查询进行执行,根据pagesize的设置进行分页查询,把查询结果赋值给bindingsource来提示和bindingsource绑定的控件(如datagridview)进行刷新。在进行分页查询的同时,也会更新当前的页码。
其中bindingnavigator 对象的positionitem的textchanged事件处理,会触发load事件。为了避免频率过高的执行,我使用了一个自定义的事件延迟器(见:https://github.com/heavenwing/redmoon/blob/master/delayevent.cs),当然也可以使用rx来进行延迟。
具体用法就很简单:实例化一个用于具体实体类的ravendbdatasource,然后调用load方法,在load方法中构造查询。如:
private void loadprocessdata(){if (_dsprocess == null)_dsprocess = new ravendbdatasource(_store, bnprocess, bsprocess);var txt = tstbsearchforprocess.text.tolower();if (string.isnullorempty(txt)){if (tscbsource.selectedindex == 0){if (tscbrelatedcount.selectedindex query.where(o => o.relatedcount == tscbrelatedcount.selectedindex).orderby(o => o.productname));else_dsprocess.load(query => query.where(o => o.relatedcount >= 5).orderby(o => o.productname));}else{if (tscbrelatedcount.selectedindex query.where(o => o.source == tscbsource.text&& o.relatedcount==tscbrelatedcount.selectedindex).orderby(o => o.productname));else{_dsprocess.load(query => query.where(o => o.source == tscbsource.text&& o.relatedcount >= 5).orderby(o => o.productname));}}}else{_dsprocess.load(query => query.search(o => o.productname, txt).orderby(o => o.productname),index1name);}}上述代码中,可以同时对多个属性进行过滤(where),也可通过设定索引名称(index1name)对一个或多个属性进行搜索(search)。
其它类似信息

推荐信息