WebForm —— 页面状态自动加载和保存(下)

很久之前写完了上、中两篇,因为各种原因吧,到现在也没有完成下篇,心里一直有些愧疚。好了,废话不说了,把下篇补上,也是我用到现在的代码。

第一步,新建一个类,并且让类从 BasePage 继承。

第二步,重写 BasePage 类的两个虚方法:GetCacheData 和 SaveCacheData ,分别处理数据的 Load 和 Save 。

第三步,保存这个类,并让页面的后台类(系统默认继承自 Page)继承自这个新类就可以了。

好了,步骤理解之后,原理在上中两篇说的差不多了,剩下的看代码就行了,有问题给我留言就 OK 。

首先是中篇中提到的的 AutoSaveAttribute 特性类:

using System;
using System.Diagnostics;

namespace Lenic.Web
{
    /// <summary>
    /// 自动保存属性,配合 BasePage 能够实现 Web 页面后台代码类字段或属性的自动保存和加载。
    /// </summary>
    [DebuggerStepThrough]
    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
    public class AutoSaveAttribute : Attribute
    {
        /// <summary>
        /// 初始化创建一个 <see cref="AutoSaveAttribute"/> 类的实例,使得具有该属性的类的属性或字段具有自动保存的特性。
        /// </summary>
        public AutoSaveAttribute() { }
    }
}

然后是核心处理逻辑 BasePage 虚基类:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Web.UI;

namespace Lenic.Web
{
    /// <summary>
    /// 提供了 Web 页面自动保存属性处理的基类
    /// </summary>
    [DebuggerStepThrough]
    public abstract class BasePage : Page
    {
        #region Reload Fields And Properties
        /// <summary>
        /// 引发 <see cref="E:System.Web.UI.Control.Load"/> 事件。
        /// </summary>
        /// <param name="e">包含事件数据的 <see cref="T:System.EventArgs"/> 对象。</param>
        protected override void OnLoad(EventArgs e)
        {
            // 初始化当前用户控件的缓冲字典
            InitCacheDic();

            if (Page.IsPostBack)
            {
                // 获得缓冲数据列表
                var list = GetCacheData();

                // 自动加载 AutoSave 属性保存的值
                int index = 0;
                foreach (MemberInfo info in CacheDic[CurrType])
                {
                    if (info.MemberType == MemberTypes.Property)
                    {
                        PropertyInfo pi = info as PropertyInfo;
                        object value = list[index];
                        if (value != null)
                            pi.SetValue(this, value, null);
                    }
                    else if (info.MemberType == MemberTypes.Field)
                    {
                        FieldInfo fi = info as FieldInfo;
                        object value = list[index];
                        fi.SetValue(this, value);
                    }
                    index++;
                }
            }
            base.OnLoad(e);
        }
        #endregion

        #region Save Fields And Properties
        /// <summary>
        /// 在这里实现属性的自动保存。
        /// </summary>
        protected override object SaveViewState()
        {
            // 初始化当前用户控件的缓冲字典
            InitCacheDic();

            // 初始化要保存的属性值列表
            List<object> list = new List<object>();
            foreach (MemberInfo info in CacheDic[CurrType])
            {
                if (info.MemberType == MemberTypes.Property)
                {
                    PropertyInfo pi = info as PropertyInfo;
                    list.Add(pi.GetValue(this, null));
                }
                else if (info.MemberType == MemberTypes.Field)
                {
                    FieldInfo fi = info as FieldInfo;
                    list.Add(fi.GetValue(this));
                }
            }

            // 保存更改
            SaveCacheData(list);

            return base.SaveViewState();
        }
        #endregion

        #region Business Properties
        /// <summary>
        /// 用户控件类型及自动保存属性成员缓冲字典
        /// </summary>
        protected static Dictionary<Type, MemberInfo[]> CacheDic = null;

        /// <summary>
        /// 当前页面的类型
        /// </summary>
        protected Type CurrType = null;

        /// <summary>
        /// 获得成员列表的绑定标识.
        /// </summary>
        private static readonly BindingFlags Flag;

        /// <summary>
        /// 初始化 <see cref="BasePage"/> 类.
        /// </summary>
        static BasePage()
        {
            CacheDic = new Dictionary<Type, MemberInfo[]>();

            Flag = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.FlattenHierarchy;
        }

        /// <summary>
        /// 初始化当前页面的缓冲字典
        /// </summary>
        private void InitCacheDic()
        {
            // 获得当前实例类型
            CurrType = GetType();

            MemberInfo[] mems = null;
            if (!CacheDic.TryGetValue(CurrType, out mems))
            {
                var list = CurrType.GetMembers(Flag)
                    .Where(p => Attribute.IsDefined(p, typeof(AutoSaveAttribute), false))
                    .ToArray();
                CacheDic[CurrType] = list;
            }
        }
        #endregion

        #region Data Fetch
        /// <summary>
        /// 获得缓存的数据。
        /// </summary>
        /// <returns>重获的数据。</returns>
        protected abstract List<object> GetCacheData();

        /// <summary>
        /// 保存需要缓存的数据。
        /// </summary>
        /// <param name="data">需要保存的数据数组。</param>
        protected abstract void SaveCacheData(List<object> data);
        #endregion
    }
}

其次是对 GetCacheData 方法和 SaveCacheData 的实现:

private ICacheList _currCache = null;
private ICacheList CurrCache
{
    get
    {
        if (_currCache == null)
        {
            _currCache = SqliteCacheList.NewInstance(CurrUser.SessionID, CurrUser.ID, CurrType.FullName);
            _currCache.LoadPageData();
        }
        return _currCache;
    }
}

protected override List<object> GetCacheData()
{
    var data = CurrCache.Where(p => p.PageID == CurrType.FullName)
        .Select(p => p.Data == null ? null : p.Data.DeserializeFromByte<object>())
        .ToList();

    return data;
}

protected override void SaveCacheData(List<object> data)
{
    data.ForEach((p, i) => CurrCache[i].Data = p == null ? null : p.SerializeToByte());

    CurrCache.Save();
}

这里用到了一些自定义的扩展方法:

List<T> 类的 ForEach 方法添加了第二个参数 i 表示当前索引值。

DeserializeFromByte<T> 是 byte[] 字节数组反序列化为 T 类型对象的扩展。

SerializeToByte 是将当前对象序列化为 byte[] 字节数组的扩展。

这三个方法应该问题都不大,百度一下就能找到类似的实现,这里就不再贴代码了。

再次是缓存的具体实现代码。下面的代码是我个人实现的方法,每个人的想法可能都不同,权当抛砖引玉了:

观察仔细的童鞋,可以看到 ICacheList 接口,这就是我自定义的一个接口:

using System.Collections.Generic;

namespace Lenic.Web.Caches
{
    /// <summary>
    /// 缓存列表
    /// </summary>
    public interface ICacheList : IEnumerable<CacheItem>
    {
        /// <summary>
        /// 【自动新建】获得或设置缓存项。
        /// </summary>
        /// <param name="i">项的索引</param>
        /// <returns>缓存项</returns>
        CacheItem this[int i] { get; set; }
        /// <summary>
        /// 从数据库中加载数据
        /// </summary>
        /// <typeparam name="T">数据的类型</typeparam>
        /// <param name="dataId">数据标识</param>
        /// <returns>还原的原始数据</returns>
        T LoadData<T>(string dataId);
        /// <summary>
        /// 从数据库中加载页面数据
        /// </summary>
        /// <returns>加载后的列表</returns>
        ICacheList LoadPageData();
        /// <summary>
        /// 持久化数据变化
        /// </summary>
        void Save();
    }
}

其中 IEnumerable<CacheItem> 中的 CacheItem 表示缓存项的虚基类:

using System;
using System.Diagnostics;

using Lenic.Data;
using Lenic.Extensions;

namespace Lenic.Web.Caches
{
    /// <summary>
    /// 缓存项
    /// </summary>
    [Serializable]
    [DebuggerStepThrough]
    public abstract class CacheItem
    {
        #region Instance
        /// <summary>
        /// 初始化创建一个 <paramref name="CacheItem"/> 类的对象。
        /// </summary>
        public CacheItem()
        {
            MarkNew();
        }

        /// <summary>
        /// 初始化创建一个 <paramref name="CacheItem"/> 类的对象。
        /// </summary>
        /// <param name="isFetched"><c>true</c> 表示是从数据库中填充获得的; 否则返回 <c>false</c> </param>
        public CacheItem(bool isFetched)
        {
            if (isFetched)
                MarkFetched();
            else
                MarkNew();
        }

        /// <summary>
        /// 初始化创建一个 <paramref name="CacheItem"/> 类的对象。
        /// </summary>
        /// <param name="sessionID">会话标识</param>
        /// <param name="userID">用户标识</param>
        /// <param name="pageID">页面标识</param>
        /// <param name="dataID">数据标识</param>
        /// <returns>获得或新建的一个 <paramref name="CacheItem"/> 类的对象</returns>
        public CacheItem(string sessionID, string userID, string pageID, string dataID)
            : this()
        {
            SessionID = sessionID;
            UserID = userID;
            PageID = pageID;
            DataID = dataID;
        }
        #endregion

        #region Business Properties
        /// <summary>
        /// 获得或设置当前会话标识。
        /// </summary>
        public string SessionID { get; set; }
        /// <summary>
        /// 获得当前用户标识。
        /// </summary>
        public string UserID { get; set; }
        /// <summary>
        /// 获得或设置当前页面标识。
        /// </summary>
        public string PageID { get; set; }
        /// <summary>
        /// 获得或设置当前数据标识。
        /// </summary>
        public string DataID { get; set; }

        private byte[] _data = null;
        /// <summary>
        /// 获得或设置当前数据对象数据。
        /// </summary>
        public byte[] Data
        {
            get { return _data; }
            set
            {
                _data = value;
                IsDirty = true;
            }
        }

        /// <summary>
        /// 获得最近一次的修改时间
        /// </summary>
        public DateTime LastChanged { get; set; }

        /// <summary>
        /// 获得当前数据对象。
        /// </summary>
        public object DataObject
        {
            get { return Data == null ? null : Data.DeserializeFromByte<object>(); }
        }
        #endregion

        #region Mark Instance
        /// <summary>
        /// 获得当前实例是否是新建、未保存到数据库中。
        /// </summary>
        public bool IsNew { get; protected set; }

        /// <summary>
        /// 获得当前实例是否是否是从数据库中检索并填充的。
        /// </summary>
        public bool IsFetched { get; protected set; }

        /// <summary>
        /// 获得一个值, 通过该值指示当前实例对象是否被修改过。
        /// </summary>
        /// <value><c>true</c> 表示被修改过; 否则返回 <c>false</c> </value>
        public bool IsDirty { get; protected set; }

        /// <summary>
        /// 获得当前实例是否已经标识为删除。
        /// </summary>
        public bool IsDeleted { get; protected set; }

        /// <summary>
        /// 标识当前对象是新建、未保存到数据库中。
        /// </summary>
        /// <returns>更改后的自身。</returns>
        public CacheItem MarkNew()
        {
            IsDirty = false;
            IsNew = true;
            IsFetched = false;
            IsDeleted = false;
            return this;
        }
        /// <summary>
        /// 标识当前对象为从数据库中检索并填充的。
        /// </summary>
        /// <returns>更新后的自身。</returns>
        public CacheItem MarkFetched()
        {
            IsDirty = false;
            IsNew = false;
            IsFetched = true;
            IsDeleted = false;
            return this;
        }
        /// <summary>
        /// 标识当前实例对象需要在保存时删除。
        /// </summary>
        /// <returns>修改后的自身。</returns>
        public CacheItem MarkDeleted()
        {
            IsDirty = true;
            IsDeleted = true;
            return this;
        }
        #endregion

        #region Equal
        public override bool Equals(object obj)
        {
            if (obj == null || typeof(CacheItem) != obj.GetType())
                return false;

            var target = obj as CacheItem;
            if (this.SessionID == target.SessionID &&
                this.UserID == target.UserID &&
                this.PageID == target.PageID &&
                this.DataID == target.DataID)
                return true;

            return base.Equals(obj);
        }

        public override int GetHashCode()
        {
            int hash = SessionID.GetHashCode();
            hash ^= UserID.GetHashCode();
            hash ^= PageID.GetHashCode();
            hash ^= DataID.GetHashCode();

            return hash;
        }
        #endregion

        #region Data Operater
        /// <summary>
        /// 持久化到数据库中。
        /// </summary>
        /// <param name="t">数据库操作实例对象</param>
        /// <returns>影响的行数。</returns>
        public abstract int Save(IDataAccesser t);
        #endregion
    }
}

在项目中我用 Sqlite 写了一个实现,具体代码如下:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using Lenic.Data;
using Lenic.Data.Extensions;
using System.Linq;

using Lenic.Extensions;

namespace Lenic.Web.Caches.Sqlite
{
    /// <summary>
    /// 页面 ViewState 数据库缓冲
    /// </summary>
    [Serializable]
    [DebuggerStepThrough]
    public class SqliteCacheList : ICacheList
    {
        private List<SqliteCacheItem> list = new List<SqliteCacheItem>();

        #region DbHelper
        /// <summary>
        /// 缓存数据库连接字符串
        /// </summary>
        public static string ConnectionString = @"Data Source=|DataDirectory|\PageCache.dll";

        /// <summary>
        /// 数据库访问实例对象
        /// </summary>
        private static IDataAccesser DB = new DbHelper(DbProviders.SQLiteProvider, ConnectionString);

        /// <summary>
        /// 查询缓冲表是否在数据库中存在, 返回一个 <paramref name="System.Int64"/> 类型的值表示找到的个数.
        /// </summary>
        private const string SelectExists = "SELECT COUNT(*) AS COUNTS FROM SQLITE_MASTER WHERE TYPE = 'table' AND NAME = 'TB_Page'";

        /// <summary>
        /// 创建缓冲表语句.
        /// </summary>
        private const string CreateTable = @"CREATE TABLE [TB_Page] (
                                                 [SessionID] nvarchar(50) NOT NULL,
                                                 [UserID] nvarchar(50) NOT NULL,
                                                 [PageID] nvarchar(50) NOT NULL,
                                                 [DataID] nvarchar(50) NOT NULL,
                                                 [Data] blob,
                                                 [LastChanged] timestamp NOT NULL
                                             )";

        /// <summary>
        /// 删除缓冲表语句.
        /// </summary>
        private const string DropTable = "Drop Table TB_Page";

        /// <summary>
        /// 收缩数据库语句.
        /// </summary>
        private const string ShrinkDB = "Vacuum";

        /// <summary>
        /// 清除用户之前的记录文本.
        /// </summary>
        private const string ClearPreviousText = "DELETE FROM [TB_Page] WHERE [UserID] = '{0}'";

        /// <summary>
        /// 清除指定 SessionID 指定用户的的记录文本.
        /// </summary>
        private const string ClearSessionText = "DELETE FROM [TB_Page] WHERE [SessionID] = {0} AND [UserID] = '{1}'";

        /// <summary>
        /// 【页面缓存】0 = 会话标识 AND 1 = 用户标识 AND 2 = 页面标识
        /// </summary>
        private const string SelectPageSql = "SELECT * FROM [TB_Page] WHERE [SessionID] = '{0}' AND [UserID] = '{1}' AND [PageID] = '{2}'";

        /// <summary>
        /// 【变量数据】0 = 会话标识 AND 1 = 用户标识 AND 2 = 页面标识 AND 3 = 数据标识
        /// </summary>
        private const string SelectSingleDataSql = "SELECT [Data] FROM [TB_Page] WHERE [SessionID] = '{0}' AND [UserID] = '{1}' AND [PageID] = '{2}' AND [DataID] = '{3}'";
        #endregion

        #region Business Properties
        /// <summary>
        /// 获得会话标识
        /// </summary>
        public string SessionID { get; private set; }
        /// <summary>
        /// 获得用户标识
        /// </summary>
        public string UserID { get; private set; }
        /// <summary>
        /// 获得页面标识
        /// </summary>
        public string PageID { get; private set; }
        #endregion

        #region New Instance
        private SqliteCacheList() { }
        /// <summary>
        /// 新建一个列表对象
        /// </summary>
        public static SqliteCacheList NewInstance(string sessionID, string userID, string pageID)
        {
            return new SqliteCacheList
            {
                SessionID = sessionID,
                UserID = userID,
                PageID = pageID,
            };
        }

        /// <summary>
        /// 【自动新建】获得或设置缓存项。
        /// </summary>
        /// <value></value>
        /// <returns>缓存项</returns>
        public CacheItem this[int i]
        {
            get
            {
                var item = list.ElementAtOrDefault(i);
                if (item == null)
                {
                    item = new SqliteCacheItem(SessionID, UserID, PageID, i.ToString());
                    list.Add(item);
                }
                return item;
            }
            set
            {
                if (i >= list.Count)
                    list.Add((SqliteCacheItem)value);
                else
                {
                    list.RemoveAt(i);
                    list.Insert(i, (SqliteCacheItem)value);
                }
            }
        }
        #endregion

        #region IEnumerable 成员
        /// <summary>
        /// 返回一个循环访问集合的枚举数。
        /// </summary>
        /// <returns>可用于循环访问集合的 System.Collections.Generic.IEnumeratorlt;SqliteCacheItemgt;。</returns>
        public IEnumerator<CacheItem> GetEnumerator()
        {
            var data = list.GetEnumerator();
            while (data.MoveNext())
            {
                yield return (CacheItem)data.Current;
            }
        }

        /// <summary>
        /// 返回一个循环访问集合的枚举数。
        /// </summary>
        /// <returns>可用于循环访问集合的 System.Collections.Generic.IEnumeratorlt;SqliteCacheItemgt;。</returns>
        IEnumerator<CacheItem> IEnumerable<CacheItem>.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        /// <summary>
        /// 返回一个循环访问集合的枚举数。
        /// </summary>
        /// <returns>可用于循环访问集合的 System.Collections.IEnumerator。</returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
        #endregion

        #region Data Operater
        /// <summary>
        /// 初始化缓冲数据库。
        /// </summary>
        /// <param name="deleteTable">如果设置为 <c>true</c> 标识先删除 Table 再重建。</param>
        public static void Init(bool deleteTable)
        {
            long count = 0;
            using (DataReaderEx con = DB.Read(SelectExists))
            {
                count = con.Field<long>("COUNTS");
            }

            if (count > 0 && deleteTable)
                DB.Write(DropTable);
            if (count == 0)
                DB.Write(CreateTable);
            DB.Write(ShrinkDB);
        }

        /// <summary>
        /// 从数据库中清除用户旧的记录, 立即生效!
        /// </summary>
        /// <param name="userID">用户标识.</param>
        public static void ClearPrevious(string userId)
        {
            DB.Write(ClearPreviousText.With(userId));
        }

        /// <summary>
        /// 持久化数据变化
        /// </summary>
        public void Save()
        {
            using (var con = DbProviders.SQLiteProvider.CreateConnection())
            {
                con.ConnectionString = ConnectionString;
                con.Open();
                var transaction = con.BeginTransaction();

                var t = new TransactionHelper(transaction);
                try
                {
                    foreach (var item in this)
                    {
                        int count = item.Save(t);
                        if (count == 0)
                            throw new DatabaseException("数据库操作失败");
                    }
                    t.Commit();
                }
                catch (Exception e)
                {
                    t.Rollback();
                    throw e;
                }
            }
        }

        /// <summary>
        /// 从数据库中加载页面数据
        /// </summary>
        /// <param name="sessionId">会话标识</param>
        /// <param name="userId">用户标识</param>
        /// <param name="pageId">页面标识</param>
        /// <returns>加载后的列表</returns>
        public ICacheList LoadPageData()
        {
            if (SessionID.IsNullOrEmptyTrim())
                throw new ApplicationException("会话标识不能为空");
            if (UserID.IsNullOrEmptyTrim())
                throw new ApplicationException("用户标识不能为空");
            if (PageID.IsNullOrEmptyTrim())
                throw new ApplicationException("页面标识不能为空");

            using (var con = DbProviders.SQLiteProvider.CreateConnection())
            {
                con.ConnectionString = ConnectionString;
                con.Open();
                var transaction = con.BeginTransaction();

                var t = new TransactionHelper(transaction);
                try
                {
                    using (DataReaderEx dr = t.Read(SelectPageSql.With(SessionID, UserID, PageID)))
                    {
                        list = dr.ToList<SqliteCacheItem>(p => new SqliteCacheItem(true)
                        {
                            SessionID = p.Field<string>("SessionID"),
                            UserID = p.Field<string>("UserID"),
                            PageID = p.Field<string>("PageID"),
                            DataID = p.Field<string>("DataID"),
                            Data = p.Field<byte[]>("Data"),
                            LastChanged = p.Field<DateTime>("LastChanged"),
                        });
                    }
                }
                finally
                {
                    t.Rollback();
                }
            }
            return this;
        }

        /// <summary>
        /// 从数据库中加载数据
        /// </summary>
        /// <typeparam name="T">数据的类型</typeparam>
        /// <param name="sessionId">会话标识</param>
        /// <param name="userId">用户标识</param>
        /// <param name="dataId">数据标识</param>
        /// <returns>还原的原始数据</returns>
        public T LoadData<T>(string dataId)
        {
            if (SessionID.IsNullOrEmptyTrim())
                throw new ApplicationException("会话标识不能为空");
            if (UserID.IsNullOrEmptyTrim())
                throw new ApplicationException("用户标识不能为空");

            var data = DB.GetValue(SelectSingleDataSql.With(SessionID, UserID, "Lenic.Global", dataId))
                         .DirectTo<byte[]>();

            if (data == null) return default(T);
            return data.DeserializeFromByte<T>();
        }
        #endregion
    }
}

下面是缓存项的实现类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Lenic.Data;
using Lenic.Extensions;

namespace Lenic.Web.Caches.Sqlite
{
    /// <summary>
    /// Sqlite 缓存项
    /// </summary>
    [Serializable]
    [DebuggerStepThrough]
    public class SqliteCacheItem : CacheItem
    {
        #region Instance
        /// <summary>
        /// 初始化创建一个 <paramref name="SqliteCacheItem"/> 类的对象。
        /// </summary>
        public SqliteCacheItem()
        {
            MarkNew();
        }

        /// <summary>
        /// 初始化创建一个 <paramref name="SqliteCacheItem"/> 类的对象。
        /// </summary>
        /// <param name="isFetched"><c>true</c> 表示是从数据库中填充获得的; 否则返回 <c>false</c> </param>
        public SqliteCacheItem(bool isFetched)
        {
            if (isFetched)
                MarkFetched();
            else
                MarkNew();
        }

        /// <summary>
        /// 初始化创建一个 <paramref name="SqliteCacheItem"/> 类的对象。
        /// </summary>
        /// <param name="sessionID">会话标识</param>
        /// <param name="userID">用户标识</param>
        /// <param name="pageID">页面标识</param>
        /// <param name="dataID">数据标识</param>
        /// <returns>获得或新建的一个 <paramref name="SqliteCacheItem"/> 类的对象</returns>
        public SqliteCacheItem(string sessionID, string userID, string pageID, string dataID)
            : this()
        {
            SessionID = sessionID;
            UserID = userID;
            PageID = pageID;
            DataID = dataID;
        }
        #endregion

        #region Command Text
        /// <summary>
        /// 【插入】0 = 会话标识 AND 1 = 用户标识 AND 2 = 页面标识 AND 3 = 数据标识 AND 4 = 插入序号
        /// </summary>
        internal const string InsertText = "INSERT INTO [TB_Page]([SessionID], [UserID], [PageID], [DataID], [Data], [LastChanged]) Values('{0}', '{1}', '{2}', '{3}', @p{4}, datetime());";
        /// <summary>
        /// 【更新】0 = 会话标识 AND 1 = 用户标识 AND 2 = 页面标识 AND 3 = 数据标识 AND 4 = 更新序号
        /// </summary>
        internal const string UpdateText = "UPDATE [TB_Page] SET [Data] = @p{4}, [LastChanged] = datetime() WHERE [SessionID] = '{0}' AND [UserID] = '{1}' AND [PageID] = '{2}' AND [DataID] = '{3}'";
        /// <summary>
        /// 【删除】0 = 会话标识 AND 1 = 用户标识 AND 2 = 页面标识 AND 3 = 数据标识
        /// </summary>
        internal const string DeleteText = "Delete From [TB_Page] WHERE [SessionID] = '{0}' AND [UserID] = '{1}' AND [PageID] = '{2}' AND [DataID] = '{3}'";
        #endregion

        #region Data Operater
        /// <summary>
        /// 持久化到数据库中。
        /// </summary>
        /// <param name="t">数据库操作实例对象</param>
        /// <returns>影响的行数。</returns>
        public override int Save(IDataAccesser t)
        {
            if (IsNew && IsDeleted) return -1;
            if (!IsDirty) return -1;
            if (IsDeleted) return t.WriteD(DeleteText.With(SessionID, UserID, PageID, DataID));
            if (IsNew) return t.WriteD(InsertText.With(SessionID, UserID, PageID, DataID, 0), Data);
            if (IsFetched) return t.WriteD(UpdateText.With(SessionID, UserID, PageID, DataID, 0), Data);

            return 0;
        }
        #endregion
    }
}

就到这里吧,能拿出来的都拿出来了。后面的代码也包含了一些自定义方法,我略作解释:

IDataAccesser 接口操作数据库,包含下面的方法,具体靠 DbHelper 和 TransactionHelper 实现,我就不写了,你应该能写出来一个实现类。很简单的!

/// <summary>
/// 数据库操作接口
/// </summary>
public interface IDataAccesser
{
    /// <summary>
    /// 创建一个新的数据库命令对象。
    /// </summary>
    /// <returns>一个新的数据库命令对象。</returns>
    DbCommand NewCommand();
    /// <summary>
    /// 执行查询, 并返回查询所返回的结果集中第一行的第一列. 所有其他的列和行将被忽略.
    /// </summary>
    /// <param name="cmd">查询命令实例对象.</param>
    /// <returns>结果集中第一行的第一列.</returns>
    object GetValue(DbCommand cmd);
    /// <summary>
    /// 从数据库中查询并返回结果集(DataSet 类型)。
    /// </summary>
    /// <param name="cmd">查询命令实例对象。</param>
    /// <returns>查询结果集。</returns>
    DataSet Query(DbCommand cmd);
    /// <summary>
    /// 从数据库中查询并返回一个 <paramref name="System.Data.Common.DbDataReader"/> 类型的实例对象。
    /// </summary>
    /// <param name="cmd">查询命令实例对象。</param>
    /// <returns>查询结果集。</returns>
    DbDataReader Read(DbCommand cmd);
    /// <summary>
    /// 执行数据库操作, 并返回影响的行数。
    /// </summary>
    /// <param name="cmd">执行命令实例对象。</param>
    /// <returns>影响的行数。</returns>
    int Write(DbCommand cmd);
}

DataReaderEx 是 DbDataReader 的一个实现类,用修饰模式实现,这里你可以把其当成 IDataReader 接口来看待。

IsNullOrEmptyTrim 是对 String 类 IsNullOrEmpty 方法的封装,同时增加了对 Trim 方法处理后的判断。

DirectTo 是对类型强转的包装,等于 (T)obj 。

With 是对 String.Format 的封装。

原文地址:https://www.cnblogs.com/lenic/p/2177331.html