Sofire v1.0 开源——快速数据库访问模式 Sofire.Data(2)

Sofire Suite 是一套个人从 2009 年 08 月开始着手研发的套件。历经几年的不断优化改进,从最初的 V 套件到 Sofire2011 到目前的 Sofire.v1.0,Sofire 已经经历了许多项目的考验,并且出色的完成它的使命。现在,我将这套组件再次重构,尝试让它成为任意平台、框架、套件的的底层首选。秉着开源精神,希望这套组件在博友的讨论中不断成长、成熟。

那么,Sofire.v1.0包含什么内容?

下载地址:Sofire.v1.0-120604

1、数据库访问(Sofire.Data)

2、快速动态反射(Sofire.Dynamic)

3、高效简短的二进制序列化(Sofire.Serialization.BinarySuite)

4、远程对象模式(Sofire.DataComm.Remote)

5、安全高效Socket(Sofire.DataComm.Net)

6、面向切面(Sofire.AOP)

7、等。由于Sofire v1.0 第一期移植,故而功能暂时尚未全部移植完成。

前文

紧接上篇内容《Sofire v1.0 开源——WinForm/SL/WebForm 的 Remoting(1)》。

本文主要讲述 SOFIRE 框架在底层开发中,采用 Sofire.Data 对各种数据库的操作进行统一使用。它仿佛就是对 ADO.NET 的全面封装。

当然,Sofire.Data 支持哪些数据库,关键是在于实现,而不是在于“支持不支持”,理论上所有基于 ADO.NET 的数据库全部支持,而其他数据库,也有部分支持。

当前所支持的数据库包括:MSSQL、Oracle、Oracle、DDTekOracle、OleDb 和 SQLite。至于其他的数据库(如 MySql),由于本人实际项目中并没使用这些数据库,所有暂时不支持,如果你想支持其他数据库,请继承 Sofire.Data.QueryEngineBase。

框架约定
任何函数具有返回内容
在框架的开发过程中,在所有公共函数,都应具有一个返回描述。例如,在处理某业务的函数,可能有人会这样写:
        public DataTable GetTotalReport(string username, DateTime begin, DateTime end)
        {
            //处理过程...
            return new DataTable();
        }

当然,这并不是指责这样的写法的好坏,而是建议对函数的返回值进行适当的封装描述。例如,我可以这样:

        public Result<DataTable> GetTotalReport(string username, DateTime begin, DateTime end)
        {
            try
            {
                //处理过程...
                return new DataTable();
            }
            catch(Exception ex)
            {
                return ex;
            }
        }

通过这样的约定,可以明确的告诉函数调用者,这个函数返回一个值,但这个操作函数也可能会返回一个错误的内容。

返回描述,是为了更好的处理

当遇到具有返回内容的操作函数时,可以这样的处理返回结果

        public void Test()
        {
            var r = this.GetTotalReport("a", DateTime.Now, DateTime.Now);
            if(r.IsSucceed)
            {
                DataTable table = r.Value;
            }
            else
            {
                Exception ex = r.Exception;
            }
        }

简单的代码,表述了返回值具备了函数操作结果的“正确性”,同时也提供了错误的详细信息。以下是返回结果的接口(IResult):

    /// <summary>
    /// 表示一个结果(接口)。
    /// </summary>
    public interface IResult
    {
        #region Properties

        /// <summary>
        /// 获取执行时发生的错误。
        /// </summary>
        Exception Exception
        {
            get;
        }

        /// <summary>
        /// 获取一个值,表示执行结果是否为失败。
        /// </summary>
        bool IsFailed
        {
            get;
        }

        /// <summary>
        /// 获取一个值,表示执行结果是否为忽略。
        /// </summary>
        bool IsIgnored
        {
            get;
        }

        /// <summary>
        /// 获取一个值,表示执行结果是否为成功。
        /// </summary>
        bool IsSucceed
        {
            get;
        }

        /// <summary>
        /// 获取执行的状态。
        /// </summary>
        ResultState State
        {
            get;
        }

        #endregion Properties

        #region Methods

        /// <summary>
        /// 指定错误信息,将当前结果切换到失败的状态。
        /// </summary>
        void ToFailded(Exception exception);

        /// <summary>
        /// 指定错误信息,将当前结果切换到失败的状态。
        /// </summary>
        void ToFailded(string exceptionMessage);

        /// <summary>
        /// 将当前结果切换到忽略的状态。
        /// </summary>
        void ToIgnored();

        /// <summary>
        /// 将当前结果切换到成功的状态,并且清除结果的错误信息。
        /// </summary>
        void ToSuccessed();

        /// <summary>
        /// 指示一个结果,与当前结果进行校验。如果 <paramref name="result"/> 是一个错误结果,那么当前的结果将会转换为错误状态。否则将会转换 <paramref name="result"/> 的状态。
        /// </summary>
        /// <param name="result">比较的结果。</param>
        /// <returns>返回一个值,表示结果是否为成功状态。</returns>
        bool Checking(Result result);

        #endregion Methods
    }


    /// <summary>
    /// 表示包含一个返回值的结果(接口)。
    /// </summary>
    /// <typeparam name="TValue">返回值的类型。</typeparam>
    public interface IResult<TValue> : IResult
    {
        #region Properties

        /// <summary>
        /// 设置或获取结果的返回值。若结果不处于成功的状态,将返回默认值。
        /// </summary>
        TValue Value
        {
            get; set;
        }

        #endregion Properties

        #region Methods

        /// <summary>
        /// 将当前结果切换到成功的状态,并且清除结果的错误信息。
        /// </summary>
        /// <param name="value">结果返回的值。</param>
        void ToSuccessed(TValue value);

        /// <summary>
        /// 指示一个结果,与当前结果进行校验。如果 <paramref name="result"/> 是一个错误结果,那么当前的结果将会转换为错误状态。否则将会转换 <paramref name="result"/> 的状态。
        /// </summary>
        /// <param name="result">比较的结果。</param>
        /// <param name="value">状态为成功时的返回值。</param>
        /// <returns>返回一个值,表示结果是否为成功状态。</returns>
        bool Checking(Result result, TValue value);

        #endregion Methods
    }

    /// <summary>
    /// 表示包含两个返回值的结果(接口)。
    /// </summary>
    /// <typeparam name="TValue1">第一个返回值的类型。</typeparam>
    /// <typeparam name="TValue2">第二个返回值的类型。</typeparam>
    public interface IResult<TValue1, TValue2> : IResult
    {
        #region Properties

        /// <summary>
        /// 设置或获取结果的第一个返回值。若结果不处于成功的状态,将返回默认值。
        /// </summary>
        TValue1 Value1
        {
            get; set;
        }

        /// <summary>
        /// 设置或获取结果的第二个返回值。若结果不处于成功的状态,将返回默认值。
        /// </summary>
        TValue2 Value2
        {
            get; set;
        }

        #endregion Properties

        #region Methods

        /// <summary>
        /// 将当前结果切换到成功的状态,并且清除结果的错误信息。
        /// </summary>
        /// <param name="value1">结果的第一个返回值。</param>
        /// <param name="value2">结果的第二个返回值。</param>
        void ToSuccessed(TValue1 value1, TValue2 value2);

        /// <summary>
        /// 指示一个结果,与当前结果进行校验。如果 <paramref name="result"/> 是一个错误结果,那么当前的结果将会转换为错误状态。否则将会转换 <paramref name="result"/> 的状态。
        /// </summary>
        /// <param name="result">比较的结果。</param>
        /// <param name="value1">状态为成功时的第一个返回值。</param>
        /// <param name="value2">状态为成功时的第二个返回值。</param>
        /// <returns>返回一个值,表示结果是否为成功状态。</returns>
        bool Checking(Result result, TValue1 value1, TValue2 value2);

        #endregion Methods
    }

 

Sofire.Data
数据库是如何连接的?

Sofire.Data 的所有数据库实现,都派生于 Sofire.Data.QueryEngineBase,通过简单的几个抽象实现,从而达到对数据库的快速支持。

        public void DataConnect()
        {
            string connectionString = "";
            OracleQuery oracleQuery = new OracleQuery(connectionString); // 微软已经不建议使用这种方式连接 Oracle
            DDTekOracleQuery oleDbQuery = new DDTekOracleQuery(connectionString);
            MsSqlQuery mssqlQuery = new MsSqlQuery(connectionString);
            OleDbQuery oleDbQuery = new OleDbQuery(connectionString);
            SQLiteQuery sqliteQuery = new SQLiteQuery(connectionString);
            // 派生基类 QueryEngineBase 扩展,可以对更多的数据库提供支持。
        }
数据库是如何查询的?

数据库的查询

        public void Execute()
        {
            string connectionString = "";
            int uid = 1;
            DDTekOracleQuery oracleQuery = new DDTekOracleQuery(connectionString);
            TableResult r1 = oracleQuery.ExecuteTable("SELECT * FROM Users WHERE UID=:uid", "uid", uid);
            
            if(r1.IsSucceed)
            {
                DataTable table = r1.Value;
            }

            SqlQuery mssqlQuery = new SqlQuery(connectionString);
            TableResult r2 = mssqlQuery.ExecuteTable("SELECT * FROM Users WHERE UID=@uid", "@uid", uid);
            
            if(r2.IsSucceed)
            {
                DataTable table = r2.Value;
            }
        }

当然,上面的演示代码仅仅是返回一张表,更多的支持请参考以下图片

由于今年的工作关系,我对 Oracle 的接触频繁,Sofire.Data 中对于 Oracle 的支持,也逐渐成熟中,例如支持多行 ExecuteNoQuery,支持游标参数。

        private Result Test1()
        {
            //“>”符号表示这是一个存储过程,或程序包
            // PKG_FLOW_NAME.UP_GetFlowNameById 的返回值在于一个游标参数。
            var tableResult = query.ExecuteTable(">PKG_FLOW_NAME.UP_GetFlowNameById"
                , new ExecuteParameter("v_FID", 111, DbType.VarNumeric)
                , query.CreateCursor("c"));

            if(tableResult.IsSucceed)
            {
                Console.WriteLine(tableResult.Value.Rows.Count);
            }
            return tableResult;
        }
        private Result Test2()
        {
            var noQueryResult = query.ExecuteNoQuery(@"begin
            insert into table1 select * from XXX;
            insert into table2 select * from XXX;
            insert into table3 select * from XXX;
            insert into table4 select * from XXX;
            end;");

            if(tableResult.IsSucceed)
            {
                Console.WriteLine(tableResult.Value.Rows.Count);
            }
            return tableResult;
        }
结束语

由于时间的关系(最近工作岗位变动),最重要,也是这套框架元老级组件——数据库部分,介绍的并不详细。接下来,可能对 Sofire.Data 的高级部分进行讲解,比如对查询前后的事件支持,查询结果自动转换为对象/集合。不过这种比较代码性的东西,的确比较难阅读,也让我异常纠结。

额,好像很多人想觉得我这款博客皮肤不错?其实这一款博客皮肤是参考 李宝亨达人 的博客风格进行改版的,很感谢他的皮肤(他是绿色版,我是蓝色版)。

我很懒,但如果您在使用这套组件中遇见任何问题或者有任何建议意见,可以在博客留言,我将会及时回复。源码已更新。稍后上传。

posted @ 2012-06-04 10:21  Treenew Lyn  阅读(3336)  评论(34编辑  收藏  举报