天天看点

多样化实现Windows Phone 7本地数据库访问<2>

<1>初识Windows Phone DB

WPDB是利用Silverlight的独立存储[IsolatedStorage]机制为WP7访问数据库加以支持. 目前的版本只是针对开发人员. 使用简单. 开源. 其实它内部存储数据的实质就是利用IsolatedStorage. Silverlight的IsolatedStorage是一种类似Cookie的静态存储机制.可以将一些基本类型(String,Int)的信息甚至是自定义类型序列化后的静态存储于客户端文件中.

独立存储[IsolatedStorage]是一个局部信任机制. 什么叫局部? 当你创建一个Silverlight应用程序时会在硬盘上创建相应独立的存储区域.  这里面独立是相对于不同Silverlight Project而言的. 当然如果应用程序中存在多个程序集[Project],那么存储空间在这多个程序集之间是共享的.

<a target="_blank" href="http://blog.51cto.com/attachment/201201/121253668.jpg"></a>

&lt;2&gt;Windows Phone DB给我们带来什么?

先不着急回答这个问题.WPDB是开源的  你在可以在CodePlex上下载它相关源码:

下载完源码用VS工具打开.预览整个Solutions:

<a target="_blank" href="http://blog.51cto.com/attachment/201201/121259431.jpg"></a>

Solutions中包含两个项目: 第一个为WPDB的源码项目 下面是对WPDB测试项目. 二者关系是测试项目对源码项目采取了引用. 先不管那么多运行起来看看效果:

<a target="_blank" href="http://blog.51cto.com/attachment/201201/121304206.jpg"></a>

页面只有一个Run tests按钮. 点击后运行提示Test Completed 测试完成 我们来看Button按钮下事件下代码的调用:[注释是自己添加的]

private void RunTests_Click(object sender, RoutedEventArgs e)     

{     

    //获取测试项     

         foreach (var item in ResultPanel.Children)     

           {    

               if (item is TextBlock)     

            {     

            ((TextBlock)item).Foreground = new SolidColorBrush(Colors.White);     

       }    

    }    

     CreateDBTest();//创建DataBase    

CreateTableTest();//Create Table    

SaveTest();//Save Config    

  SaveSingleTableTest();//保存单表    

   OpenTest();//打开数据库    

    AddRangeTest();//添加一个范围数据[20条]    

     RemoveRangeTest();    

   RemoveRangeConditionTest();    

     SaveFailsTest();//保存记录    

  SaveWithEncryptionTest();//保存加密后数据-[看来还考虑加密]    

      OpenWithEncryptionTest();//打开机密数据    

        SelectConditionTest();   

  LazyLoadingTest();//还有延迟加载-[很意外啊]    

       AddRowToExistingTableTest();//添加一行数据库    

      AddRowToExistingTableTestLazyLoad();    

      DatabaseExists();//关闭数据库链接    

       //测试完成提示    

     MessageBox.Show("Test completed", "Silverlight Phone Database", MessageBoxButton.OK);   

    } 

由上面代码很明显能够看出, 方法包含操作也就是我们对数据库基本日常操作. WPDB完全创建一套自己的API[其实内部封装就是一个Silverlight 类库],这点和Effproze 在WP7访问方式完全不同. Effproze的API完全参考ADO.NET复制一个版本. SQlite则也是自己创建一套API.幸运的是这次我们能够看到WPDB的源码. 先不管大体方法中操作实现. 我们在回过头看看WPDB源码结构 分析如下:

<a target="_blank" href="http://blog.51cto.com/attachment/201201/121310902.jpg"></a>

如上分析可见.WPDB底层数据存储的实现 主要涉及到: DataBase/TAble的CRUD操作, 存储数据的加密和解密, 操作Exception异常自定义封装, IsolatedStorage数据存储以及文件流之间格式转换,Linq操作数据格式的支持,数据延迟加载等各个方面.从上源码分析来看. 这个WPDB实现总体来说还是比较简单的.不难理解.如果你觉得这些功能不能满足你的需求. 完全可以自己在如上代码添加更多的功能.

下面来看看对DataBase和Table表基本操作 我现在要创建一个PersionDB数据库 库中新建一个Persion表并添加 10条数据 如何实现: 创建DataBase:

public static Database CreateDatabase(string databaseName, string password)    

    {   

         //如果存在抛出异常    

        if (DoesDatabaseExists(databaseName))     

       {    

             throw new DatabaseExistsException(string.Format(DatabaseResources.DatabaseExistsExceptionText, databaseName));    

     //new一个DataBase新实例.    

        //参数为:DataBaseName[数据库名称]  password-[访问密码]   false-[默认不采用延迟加载]   

         return new Database(databaseName, password, false);    

     } 

创建先判断数据库是否存在, 然后new 一个Database实例看一下DataBase构造函数:

private Database(string databaseName, string password, bool useLazyLoading)    

        //如下全部类DataBase封装属性     

_databaseName = databaseName;//数据库名称     

          _password = password;//数据存储加密的密码-[注明:加密和解密都需要密码]     

      _useLazyLoading = useLazyLoading;//是否启用延迟加载     

           //封装了一个Collection 来存储当前DataBase下所有的Table表    

       _tables = new ReadOnlyCollection&lt;ITable&gt;(new List&lt;ITable&gt;());   

        _loadedTables = new Dictionary&lt;Type, bool&gt;();    

构造函数中封装DAtabase基本属性,  其中有必要说一下ReadOnlyCollection&lt;ITable&gt; 它其实目的是在Database对象创建一个Collection集合来存储表结构.  里面表结构实现是父类接口ITable.有了DataBase我们创建一个Persion表:

//创建库 [调用代码]    

Database.DeleteDatabase("test");    

Database db = Database.CreateDatabase("test");     

//创建相应表     

db.CreateTable&lt;Person&gt;();    

创建表CreateTable方法定义:

public void CreateTable&lt;T&gt;()     

    {     

     //判断表是否存在    

  if (DoesTableExists(typeof(T)))    

         throw new DatabaseExistsException(string.Format(DatabaseResources.TableExistsExceptionText, typeof(T).FullName));    

      }    

     else    

     {   

           //奥 尽然这种写法 已经利用定义Collection定义好List大小Size 难道也直接考虑到List性能    

            List&lt;ITable&gt; tables = new List&lt;ITable&gt;(_tables);     

          //创建表 其实就是New 一个Table实例 指定Table的名称和访问密码    

            tables.Add(SilverlightPhoneDatabase.Table&lt;T&gt;.CreateTable(_databaseName, _password));    

         //创建成功后 把这个新建的表添加指定的数据库中    

       _tables = new ReadOnlyCollection&lt;ITable&gt;(tables);    

        }    

      } 

创建表同时也与表实例类型关联. 这是我们需要定义一个Persion实体类[源码附有下载].  把新建的TAble表添加到Database表存储集合中, 实现与数据库的关联.  有了表和数据库 快速插入10条记录: 数据插入操作:

private void SaveTest()    

{    

//创建库和表     

     Database.DeleteDatabase("test");    

         Database db = Database.CreateDatabase("test");   

         db.CreateTable&lt;Person&gt;();    

     //数据库表存在情况下     

     if (db.Table&lt;Person&gt;() != null)    

     {    

           for (int i = 0; i &lt; 10; i++)    

               {   

             //添加数据  NewRandomPerson返回一个随机的实体类Persion    

               db.Table&lt;Person&gt;().Add(NewRandomPerson());   

             }    

         //保存数据    

         db.Save();    

          this.SaveDBLabel.Foreground = new SolidColorBrush(Colors.Green);    

         }    

          else   

        {    

       this.SaveDBLabel.Foreground = new SolidColorBrush(Colors.Red);   

     }    

数据添加到DAtaBase对象下属性ReadOnlyCollection&lt;ITable&gt; Tables集合中,db.Save数据保存在源码重写成两个方法: 下面保存所有的数据库和所有表到独立存储空间文件上:

public void Save()    

      {    

       try    

           //创建应用程序类型独立存储    

           using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())     

             {   

                  //删除存储文件 一个数据库对应一个存储文件     

            if (store.FileExists(_databaseName))    

                {   

              store.DeleteFile(_databaseName);    

               }   

                   //创建存储文件    

             using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(_databaseName, FileMode.OpenOrCreate, store))    

             {    

               //把数据库内容Database 写成文件流方式保存     

                  WriteDatabaseToStream(stream);    

                       stream.Close();    

                 }   

               }    

           foreach (var item in Tables)    

            //延迟加载方式    

              if (_useLazyLoading)    

                 {    

                  if (_loadedTables[item.RowType])    

                     {     item.Save();     }    

                   }   

                else   

               {  item.Save();  }    

       catch (Exception ex)   

    {    

         throw new SaveException(ex);    

 } 

看到了吧这就是数据真正存储到独立存储空间上文件里方法.  注意独立存储Isolated Storage根据应用程序作用域不同分为应用程序和站点两种类型. 使用时分别用不同对象创建,当前采用应用程序方式.

存储时利用数据库名称作为文件名,  对应关系为 一个数据库对一个独立存储文件. 数据库库保存细节是把数据库内容及Collection写成Stream字节流方式存储到文件中 WriteDatabaseToStream编码如下:

public void WriteDatabaseToStream(Stream stream)     

     {     

            string serilizedInfo = string.Empty;     

        serilizedInfo = _databaseName;    

          //获取数据库中表数据    

         foreach (var item in _tables)    

         serilizedInfo = string.Concat(    

                  serilizedInfo,    

                  Environment.NewLine,    

                    CreateFormattedTableType(item.RowType));  

           }   

        if (!string.IsNullOrEmpty(_password))    

            {   

              //如果有采用了加密方式 则把数据进行加密  返回加密的字符串 .在存储到文件中    

              serilizedInfo = Cryptography.Encrypt(serilizedInfo, _password);    

        }   

       using (StreamWriter writer = new StreamWriter(stream))    

          //把数据字符串写入字节流中 方式写到独立存储空间硬盘文件上    

      writer.Write(serilizedInfo);    

            writer.Flush();   

           writer.Close();   

获取字节流格式后, 获取数据库对应每个表, 利用string.Concat方法拼接字符串, 如果在创建表时设置需要加密则功过加密方法返回加密后字符串, 最后把字符串写入存储流中 进行保存独立存储硬盘空间上 实现了数据的存储.

如上实现了Windows Phone DB 从创建数据库-创建数据表结构-插入数据-保存数据到独立存储空间上,整个流程. 当然更多操作请下载源码参考.

到了这儿我在回到这个小节的主题,Windows Phone DB 给我们带来了什么?

Windows Phone DB给我们带来利用独立存储方式现在WP7对本地数据访问支持最完整解决方案.它把Silverlight的独立存储机制运用在数据库存储上最大化了. 它利用Silverlight类库模拟了一个小型的数据库存储系统[虽然很多东西不支持]. 你可以看出数据库和表结构 完全可T-Sql没有任何关联, 利用类于类之间关系进行约束的.

很多人又不禁要问. 这样的形式是不完整的. 我要它支持View. 存储过程Proc. Transaction事务操作等. 那么剩下工作就是采用类于类之间关联进行约束创建,  它开辟了在WP7利用独立存储方式模拟数据库存储功能一种独特视角[虽然不是最好方式] 开阔我们解决问题更广的视野. 这一点是我个人为Windows Phone DB 做的最成功的地方. 但从这点于Effproz和SQlite来说  WPDB是具有创造性的思维的.

当然如果你认为它不能满足你的工作, 太过简单, 对于一个难度不大 但视角独特 而且开源项目来说, 你完全可以在这个基础之上加上更多的功能 模拟出更好数据库支持. 如果你好的建议 或疑问请在留言中提出.

&lt;3&gt;Windows Phone DB小节

当然短短一篇文章也许无法更加详细阐述WPDB所具有的各种特点. 我也是作为一个初学者利用短短一种时间对源码进行摸索. 虽然WPDB性能和实用性不及Effproz和SQlite 但作者的创造性思维的方式 给我的映像深刻.

由此WPDB和T-SQl没有任何关联. 所以就没有QueryTool查询工具可言了.另外对于Silverlight异步通信而言, 本次源码中并没有实现对类实例化进行远程传输JSon格式的实现, 其实这个功能完全可以再WPDB基础之上模拟出来.

至于说性能和其他功能完善. WPDB和其他数据库没有任何可比性. 那是因为没有共同的基础条件. 但是WPDB在创造性可以说独树一帜的. 以上均为我个人详细分析源码后获得一点感受.如果你有更好意见和建议可以再留言中提出.

本文转自chenkaiunion 51CTO博客,原文链接:http://blog.51cto.com/chenkai/764668