10月PDC10 关于Silverlight前景的风波过后. 很多做相关Silverlight开发的DVP 转而投向微软的Windows phone 7平台移动.上周五Silverlight官方首页也打算在在12月2日做了一个Action Event.:Future of Silverlight Starts Now. 我不知微软是否在为PDC 10大会后 为Silverlight战略上表现做其他补充功课. 届时Scott Guthrie 可能会介绍Silverlight的下一版本Silverlight 5相关内容.
回到主题,本篇是关于Windows Phone 7本地数据库访问的第四篇. 在前三篇中我分别作了关于Effiproz For Windows Phone 7, SQlite For Windows Phone 7, Windows Phone DB, DB4O For Windows Phone,等嵌入式数据库的在Windows phone 7本地数据访问的相关尝试.当然也遇到很多问题.具体细节请参考前面几篇文章. 本篇将继续验证Rapid Repository在Windows Phone 7 本地数据访问.
<1>关于Repid Repository
在进入开发前 我们先来OverView一下这个Repid repository.
10月份发布的Windows Phone 7 支持访问数据几种方式为: XML、Isolated Storage[独立存储]、Cloud[云存储],微软官方并没有给广大WP7开发者提供相关的本地数据库访问的API. 这曾经让很多习惯嵌入式开发的DVp 很是头疼.
Rapid Repository是一个基于Apache 2.0协议开源的项目,本月11月7日刚刚发布了第一个1.0正式版本[10月31日发布第一个Beta版本],Rapid Repository在存储数据本质还是是基于Windows Phone 7 独立存储的一个NoSQL数据库[即文档数据库]. 这里我稍微说一下关于NoSQL和传统Relation DataBase[关系型数据库]特点和区别.
NoSql数据库和Relation DataBase相比的优势是不需要创建Schema,表或者是存储过程等关系数据库的对象。只需要根据你的需要定义你的实体然后把它保存到数据库就可以了[其实就是说白了就像DB4O具备的面向对象数据库.],同时支持LINQ to Object方式查询数据库.
关于Repid Repository更多细节 请参考:Repid Repository Codeplex Link
<2>Repid Repository:How it Works
对于这个目前尚是第一个版本Repid Repositroy, 我可以在第一个版本总能省去更多细枝末节看到Repid Repository内部基于WP7独立存储实现数据处理机制如何构建的.
对于Repid Repostitory 的CUD[添加/更新/删除]数据操作 实际上CRU操作的是存储在WP7 独立存储空间中已经把实体序列化的JSon格式的文件.
在存储空间中存储一个实体类,则需要通过Add()和Update()方法同时把Entity序列化成Json格式. 这意味着你可以在WP7中村复制的对象和属性. List<>基本信息等.
当你通过GetById()和GetAll()方法实现Linq 同Object查询时 Repid 则通过发序列化方式把存储在WP7空间的Json文件转化成Entity Class.而RapidContext.Current.SaveChanges() 方法则保持所有当前关于Repid数据库操作改变
.看来Repid 采用对象可序列化 把序列化JSon格式文件存在独立存储空间进行操作. 所有操作都是在内存中进行的.
<3>进入开发
如上可以看出Repid第一个版本只是实现WP7中数据基本操作, 只能说基本满足需求.如下我会演示基于Repid数据库基本的CRUD操作.在进入开发前需要在Rapid Repository - Windows 7 Phone Database上下载引用RepidRepository.DLL.
新建一个Windows Phone 7 Application 命名RepidDBLocalDemo: 添加RepidRepository.DLL引用:
建立测试页面MainPage.Xaml:
MainPage页面保存一个实体类EntityClass 货物Product信息到Repid数据库中,定义好页面后,如下需要定义 Product实体:
1: using RapidRepository;
2:
3: namespace RepidDBLocalDemo.EntityModel
4: {
5: /// <summary>
6: /// Define the Product Model to Store
7: /// Author:chenkai Date:2010年11月16日18:10:47
8: /// </summary>
9: public class Product :IRapidEntity
10: {
11: public Guid Id { get; set; }
12: public string ProductName { get; set; }
13: public string ProductCatory { get; set; }
14: public ProductPackage ProductSpec { get; set; }
15: public DateTime CreateDate { get; set; }
16:
17: }
18:
19: public class ProductPackage
20: {
21: public string PId { get; set; }
22: public string PackageSpec { get; set; }
23: }
24: }
实体类Product定义中发现继承了一个接口IRapidEntity, 这个IRapidEntity我们查看一下它的定义:
1: namespace RapidRepository
2: {
3: public interface IRapidEntity
4: {
5: Guid Id { get; set; }
6: }
7: }
you see! 其实继承IRapidEntity接口目的就是为了给实体类强制加了一个Guid属性.这也不难理解, Guid[全球唯一标识符]值就是这个唯一的标识码来标识实体类. 这主要是为了当我们Repid数据库中存储多个实体通过Guid来唯一标识实体类. 在Product用属性Id来实现用户自定义Guid.
Product实体类把ProductPackage规格类作为ProductSpec属性. 这就是初步复杂类型实体
定义完实体类Product后,对于实体类存储操作我们需要定义一个对应的ProductRepository操作类:
1: using RapidRepository;
2: using RapidRepository.Context;
3: using RepidDBLocalDemo.EntityModel;
4:
5: namespace RepidDBLocalDemo.EntityModel
6: {
7: /// <summary>
8: /// Define the ProductRepository
9: /// Author:chenkai Date:2010年11月16日18:45:19
10: /// </summary>
11: public class ProductRepository :RapidRepository<Product>
12: {
14: /// <summary>
15: /// Override Base Add new Product EntityModel Method
16: /// Author:chenkai Date:2010年11月17日10:21:51
17: /// </summary>
18: public override Product Add(Product entity)
19: {
20: var newproduct = base.Add(entity);
21: RapidContext.CurrentContext.SaveChanges();
22: return newproduct;
23: }
24:
26: /// <summary>
27: /// use GUID Delete EntityModel Class
28: /// </summary>
29: /// <param name="id">Entity Guid</param>
30: public override void Delete(Guid id)
31: {
32: base.Delete(id);
33: RapidContext.CurrentContext.SaveChanges();
34: }
36:
37: /// <summary>
38: /// Update the EntityModel
39: /// </summary>
40: public override Product Update(Product entity)
41: {
42: var updateproduct = base.Update(entity);
43: RapidContext.CurrentContext.SaveChanges();
44: return updateproduct;
45: }
46:
47: }
48: }
ProductRepository类是对实体Product进行数据库操作时使用类.ProductRepository 继承了RapidRepository类,
其实RepidRespository是定义目前Repid数据所有CRUD操作:
1: public class RapidRepository<TEntity> where TEntity : global::RapidRepository.IRapidEntity
2: {
3: public RapidRepository();
4:
5: public virtual TEntity Add(TEntity entity);
6: public virtual void Delete(Guid id);
7: public virtual void Delete(TEntity entity);
8: public virtual IList<TEntity> GetAll();
9: public virtual TEntity GetById(Guid id);
10: public virtual TEntity Update(TEntity entity);
11: }
对于Repid数据库所有对源数据发生变动需要使用repidContent.CurrentContent.SaveChange()方法强制保存.才能生效.
MainPage添加对Repid数据库相关引用:
1: //Add References
2: using RapidRepository;
3: using RapidRepository.Database;
4: using RapidRepository.Context;
5: using RepidDBLocalDemo.EntityModel;
点击Save按钮触发保存一个Product实体到Repid数据库中并确认是否保存成功:
1: private void Saveinfor_Click(object sender, RoutedEventArgs e)
2: {
3: //Save Product Entity
4: try
5: {
6: if (!string.IsNullOrEmpty(this.Product_Id.Text) && !string.IsNullOrEmpty(this.Product_Name.Text)
7: && !string.IsNullOrEmpty(this.Product_catory.Text))
8: {
9: Product newproduct = new Product
10: {
11: Id=new Guid(),
12: ProductName=this.Product_Name.Text,
13: ProductCatory=this.Product_catory.Text,
14: ProductSpec=new ProductPackage
15: { PId=this.Product_Id.Text, PackageSpec="ProductSpec_Value"},
16: CreateDate=DateTime.Now
17: };
18:
19: //Save The Product Entity
20: ProductRepository productrep = new ProductRepository();
21: Product saveproduct=productrep.Add(newproduct);
22:
24: //query the Entity Confirm Entity is Save?
25: Product queryproduct = productrep.GetById(saveproduct.Id);
26:
27: if (queryproduct != null)
28: MessageBox.Show("Save Sucess!\r\n产品编号:"
29: +queryproduct.ProductSpec.PId+"\r\n产品名称:"
30: +queryproduct.ProductName+"\r\n产品分类:"
31: +queryproduct.ProductCatory+"\r\n创建时间:"
32: +queryproduct.CreateDate.ToString());
33: else
34: MessageBox.Show("Save Fail!");
35: }
36: }
37: catch (Exception se)
38: {
39: MessageBox.Show("sorry you get Exception:" + se.Message);
40: }
41: }
输入数据后保存效果:
上面保存验证是先把产品信息保存Repid数据库中,然后通过查询方式获取到已经保存的Product信息,以确认信息正确保存,查询方式目前在Repid1.0中只有两种方式GetById()和GetAll():
1: /// <summary>
2: /// query a Entity by Guid
3: /// </summary>
4: public override Product GetById(Guid id)
5: {
6: return base.GetById(id);
7: }
9: /// <summary>
10: /// Get All Entity Model
11: /// </summary>
12: /// <returns>List of Sotre Model</returns>
13: public override IList<Product> GetAll()
14: {
15: return base.GetAll();
16: }
很明显GetById通过Guid方式唯一查询一个Entity实体.这里你明白了为何要在Product中继承接口的目的. GetAll方式则获取repid数据库目前所有实体数据.返回的是一个实现接口IList<Entity>集合.
其他简单的CRUD操作基本雷同, 不在赘述.注意有一点就是当对Repid数据库发生一定操作改变数据源是一定不要忘了采用PepidContent.CurrentContent.SaveChanges();方法保存修改.
<4>Repid 相关质疑
相信用过Nhibernate对数据ORM[Obeject-Relation-Mapping]映射来替代关系型数据库这种解决方案. 我从开始用Repid一直对它数据性能保持质疑态度. 虽然作者说能够实现WP7中数据处理基本要求. 但还是对它性能上没有相关测试案例和官方的关于这个问题比较有说服力资料公布.
另外昨天在Group中有人质疑问到Repid数据既然通过实体序列化成Json文件格式然后存储到独立存储空间中. 那么这个把实体序列化Json格式这个工作在哪里做呢? 我们理清一下Repid存储数据过程:
Repid总结归纳一下存储数据过程: Define Entity[定义实体]——Add Entity[添加实体]——Entity Serializable Json File[把实体序列化成JSon文件]——Store JSon File Isolate Space[把序列化JSon文件存储在WP7独立存储空间中].
其中把Entity实体序列化成一个Json文件这个操作被Repid数据库给封装在底层.开源数据库的作者并没有把相关方法暴露给用户来操作. 所有有人说看不到Entity实体转化成JSon文件这个过程 其实是被作者封装在底层隐藏起来.这样一来用户只需要关心他们与实体操作相关方法二无需关注具体这个实体时如何序列化的并存储起来过程. 更加简化用户关注地方 简便 实用.
关于数据性能质疑 目前上没有任何资料能够初步验证Repid,由于本篇幅有限.我会在下一篇用实际处理数据方式演示Repid数据库在Windows Phone 7上性能体验.下一篇幅 会相关介绍repid缓存机制和延迟加载相关功能.如上市Repid数据在Windows phone 7数据访问.如有任何建议和意见请在留言中提出..
本实例的源码下载:/Files/chenkai/RepidDBLocalDemo.rar