从本节课开始,我们开始逐步正式进入实际的编码过程中。本节课的核心内容为SQLHeelper和ModelHelper两个核心类库的介绍。

SQLHelper这个类相信大家都很熟悉了,他是微软petshop示例项目中对数据库操作(ado.net)的一个封装。我们对这个类做了一个改进:

if ((parm.Value == null) || (string.IsNullOrEmpty(parm.Value.ToString())))
{
parm.Value = DBNull.Value;
}

这里涉及了空和null的区别。我们知道,程序都是在内存中运行的。空和null的区别其实也提现在内存中。在系统中,内存整体上分了两个区域,一块存储索引,一块存储内容(注:以上说明并不严谨,只是为了方便理解做的简化说明。实质上在net环境下,内存有堆、栈、托管堆等区分,参见:http://blog.csdn.net/xiongwjw/article/details/7248613 )

0-1 0-2 0-3 0-4
1-1 1-2 1-3 1-4
A(0-1) B(0-2) C(0-3) D(0-4)
E(1-1) F(1-2) G(1-3) (1-4)

空:在内存中分配了索引,但是索引对应的实质存储位置没有数据,例如1-4这个索引的情况,虽然分配了索引,但是索引所对应的实际存储位置没有数据。

Null:连索引也没有分配,完全没有任何内容。

我们增加的if判断主要用于一些输入框没有输入值时,直接在数据库中赋值 DBNull.Value。这样可以防止实际的参数数量和sql语句中规定的参数数量不一致。

在ModelHelper这个类库中,我们创建了四个属性,分别是TableName、PrimaryKey、ModelGuid、TimeStamp。这四个值我们都是通过反射得到的,这里其实包含了一个我们对数据库表的一个约定:

  • 数据库表的第一个必须为自增ID;
  • 数据库表的第二列必须为Guid类型(如果将来在程序中不适用ModelGuid这个属性,则可以忽略此处);
  • 数据库表的最后一列必须为TimeStamp类型;

有些同学可能奇怪,为什么有了自增ID还要创建Guid,这个设置主要是用来恢复删除的数据使用的。比如有一条数据,他的ID是3,删除这条数据后,表中又添加了一些数据,某天,突然要恢复这条数据,将它从已删除中恢复到正常数据表里,这时,受限于主键自增的约束,我们在add这条数据时,ID肯定不为3了。那么为什么唯一确定这条数据的身份证就是Guid了。但是,我们都知道Guid是32位的数字字母混合形式的,虽然具有唯一性,但是不具备可读性,尤其麻烦的是,无法排序。这时,自增ID就会用得到了。从视频中我们可以发现,该项设定其实是移植自一套OA系统中的,这套OA系统有很多复杂的逻辑,功能也很强大,其中就包含了关键业务数据的删除恢复这个功能。我们在网站中,其实也会遇到重要数据删除后想恢复的情况,所以这个设定就保留了下来。

数据表中的最后一列为TimeStamp类型是为了考虑并发而设计的。比如A打开了一个用户——张三的信息进行编辑,然后提交。在A编辑(尚未提交时)的过程中,B也在编辑这条数据,并且先A一步进行了提交操作。这时系统有两种选择,1是直接保存A的编辑数据,覆盖B的编辑结果;2是进行提醒:该数据已经被B编辑过了,新的数据值为XXX,您是否仍要保存您的编辑结果?在一般的系统中,我们都是采用第一种方案,但是在一些关键系统的关键数据中,采用第一种方案是有很大风险性的,很容易造成数据失误,必须采用第二种方案。那么我们如何知道B编辑过数据了呢?这时,TimeStamp就用上场了。在MySQL中,TimeStamp是一种DateTime格式,在MSSQL中,TimeStamp是一串二进制值(两者可以互相转化MSSQL中的二进制值相当于序列化后的DateTime格式,我也很奇怪为什么MSSQL不直接使用DateTime,如果哪位亲有认识MSSQL的开发人员,记得帮我问下)。TimeStamp列有个特殊属性,一旦数据库中的记录发生过改动,TimeStamp的值一定会发生改变这样我们只要在update时判断下元数据的TimeStamp值是否发生过改变,就能确定该数据是否被编辑过。

在ModelHelper类中,我们引入了两个命名空间:

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

这两个命名空间是MVC的M部分常用的数据约束。我们主要用于确定SqlParameter的长度。.Net本质上是一大套类库+一种运行方式。Webform和MVC之间并非完全隔离不可互通的。MVC本质上是一种软件架构模式,如果有必要,WebForm也可以实现MVC模式。所以一直犹豫WebForm或是MVC的同学大可以放下你的担心,这两者都很优秀,都能解决问题。我们引入这两个命名空间的主要用途如下:

 public SqlParameter GetSqlParameter(PropertyInfo pi,T model)
{
SqlParameter parameter;
switch (pi.PropertyType.Name)
{
case "String":
int length = ;
object[] attributes = pi.GetCustomAttributes(typeof(StringLengthAttribute), true);
if (attributes != null && attributes.Length > )
{
StringLengthAttribute myAttribute = attributes[] as StringLengthAttribute;
if (myAttribute != null)
{
length = myAttribute.MaximumLength;
}
}
if (length != )
{
parameter = new SqlParameter("@" + pi.Name, SqlDbType.NVarChar, length);
}
else
{
parameter = new SqlParameter("@" + pi.Name, SqlDbType.NVarChar);
}
parameter.Value = pi.GetValue(model, null);
break;
case "Int32":
parameter = new SqlParameter("@" + pi.Name, SqlDbType.Int, );
parameter.Value = pi.GetValue(model, null);
break;
case "Int64":
parameter = new SqlParameter("@" + pi.Name, SqlDbType.BigInt, );
parameter.Value = pi.GetValue(model, null);
break;
case "DataTime":
parameter = new SqlParameter("@" + pi.Name, SqlDbType.DateTime, );
parameter.Value = pi.GetValue(model, null);
break;
case "Boolean":
parameter = new SqlParameter("@" + pi.Name, SqlDbType.Bit, );
parameter.Value = pi.GetValue(model, null);
break;
case "Guid":
parameter = new SqlParameter("@" + pi.Name, SqlDbType.UniqueIdentifier, );
parameter.Value = pi.GetValue(model, null);
break;
case "Byte":
parameter = new SqlParameter("@" + pi.Name, SqlDbType.Timestamp, );
parameter.Value = pi.GetValue(model, null);
break;
default:
parameter = new SqlParameter("@" + pi.Name, pi.GetValue(model, null));
break;
}
return parameter;
}

我们通过反射,获取model上的stringlength属性,通过这个属性的值来确定SqlParameter的长度。我在博客园上曾经看到一篇文章,MSSQL中,如果不指定参数的长度,就无法做到执行计划的复用,每次都要编译,其效率并不比直接写SQL语句高多少,我没有实际做过测试,但我感觉对方说的有道理,喜欢刨根挖底的朋友建议在园子里搜搜这篇文章,自己亲自做个测试,当然了,有结果也请告知我一下^&^。

在ModelHelper的代码中,我们还是用到了缓存,缓存的主要代码我就不贴了。核心的关键有两个:

1:单例模式的双重锁定,园子里有篇文章列举过单例模式的几种实现,我唯一能确定的是,双重锁定是能可以确保线程安全的(static静态变量的似乎也可以),至于其他的,大家可以搜搜看,在那篇文章中有测试。

2:戴振军同学曾贡献过discuz的缓存设计架构http://www.cnblogs.com/daizhj/archive/2007/08/15/855163.html 这篇文章相对好理解,他们实质上是在内存中虚拟了一个“XML DOM树”,将不同的缓存放在不同的节点下,这样可以方便的集中管理缓存,同时他们还实现了策略模式,戴同学讲的很透彻,大家仔细看下这篇文章,一定不难理解的。我们的缓存设计的主要特点是采用了.net4.0的新增类库System.Runtime.Caching 微软已宣传在后续的net版本中,将主要维护这个类库,原System.Web.Cache将不再维护,建议大家日后使用新类库。另一个特点是我们使用了数据库缓存依赖。就我个人所知而言,这个设计是及其伟大的,微软我爱你,盖茨,你虽然不帅,我照样爱你。缓存最大的问题在于时效性问题,也就是我们常说的脏数据问题。net的数据库缓存依赖可以做到当数据库数据改变以后,SQL通过一个叫做”查询通知“的功能告知NET运行时:Hi,老兄,你的数据已过期了,赶紧删除吧!(我也好奇,为什么叫查询通知而不叫更新通知,难道是翻译问题?)通过这点,我们可以做到缓存效率和时效性的完美平衡,一个没有脏数据的缓存实在实在是太诱人了!!!缓存依赖不仅仅支持数据库缓存依赖,还支持文件缓存依赖(当文件的最后更新时间改变后,缓存自动失效),甚至可以自定义依赖策略!!!

我们在日后会借鉴戴振军同学贡献的缓存架构并做适当的修改已更加适应cms程序,受限于我们是入门级教程,初期我们就直接以net默认的键值对方式使用,后期再做改进,同时,日后我们也会考虑增加Memcached的使用和配置。

补充:数据库缓存依赖:http://www.cnblogs.com/jackyzhou/archive/2009/04/21/1440483.html

 using System;
using System.Collections;
using System.Collections.Specialized;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Data.Common;
using System.Collections.Generic;
using System.IO;
using System.Text; namespace EasyFast.Repository
{
/// <summary>
/// SqlHelper类是专门提供给广大用户用于高性能、可升级和最佳练习的sql数据操作
/// </summary>
public abstract class SqlHelper
{
//数据库连接字符串
public static readonly string ConnectionString = ConfigurationManager.ConnectionStrings["DataBase"].ConnectionString; // 用于缓存参数的HASH表
private static Hashtable parmCache = Hashtable.Synchronized(new Hashtable()); /// <summary>
/// 给定连接的数据库用假设参数执行一个sql命令(不返回数据集)
/// </summary>
/// <param name="connectionString">一个有效的连接字符串</param>
/// <param name="commandType">命令类型(存储过程, 文本, 等等)</param>
/// <param name="commandText">存储过程名称或者sql命令语句</param>
/// <param name="commandParameters">执行命令所用参数的集合</param>
/// <returns>执行命令所影响的行数</returns>
public static int ExecuteNonQuery(string connectionString, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
{
SqlCommand cmd = new SqlCommand();
using (SqlConnection conn = new SqlConnection(connectionString))
{
PrepareCommand(cmd, conn, null, cmdType, cmdText, commandParameters);
int val = cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
return val;
}
} /// <summary>
/// 用现有的数据库连接执行一个sql命令(不返回数据集)
/// </summary>
/// <param name="conn">一个现有的数据库连接</param>
/// <param name="commandType">命令类型(存储过程, 文本, 等等)</param>
/// <param name="commandText">存储过程名称或者sql命令语句</param>
/// <param name="commandParameters">执行命令所用参数的集合</param>
/// <returns>执行命令所影响的行数</returns>
public static int ExecuteNonQuery(SqlConnection connection, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
{
SqlCommand cmd = new SqlCommand();
PrepareCommand(cmd, connection, null, cmdType, cmdText, commandParameters);
int val = cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
return val;
} /// <summary>
///使用现有的SQL事务执行一个sql命令(不返回数据集)
/// </summary>
/// <remarks>
///举例:
/// int result = ExecuteNonQuery(connString, CommandType.StoredProcedure, "PublishOrders", new SqlParameter("@prodid", 24));
/// </remarks>
/// <param name="trans">一个现有的事务</param>
/// <param name="commandType">命令类型(存储过程, 文本, 等等)</param>
/// <param name="commandText">存储过程名称或者sql命令语句</param>
/// <param name="commandParameters">执行命令所用参数的集合</param>
/// <returns>执行命令所影响的行数</returns>
public static int ExecuteNonQuery(SqlTransaction trans, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
{
SqlCommand cmd = new SqlCommand();
PrepareCommand(cmd, trans.Connection, trans, cmdType, cmdText, commandParameters);
int val = cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
return val;
} /// <summary>
/// 用指定的数据库连接执行一个返回数据集的sql命令
/// </summary>
/// <remarks>
/// 举例:
/// SqlDataReader r = ExecuteReader(connString, CommandType.StoredProcedure, "PublishOrders", new SqlParameter("@prodid", 24));
/// </remarks>
/// <param name="connectionString">一个有效的连接字符串</param>
/// <param name="commandType">命令类型(存储过程, 文本, 等等)</param>
/// <param name="commandText">存储过程名称或者sql命令语句</param>
/// <param name="commandParameters">执行命令所用参数的集合</param>
/// <returns>包含结果的读取器</returns>
public static SqlDataReader ExecuteReader(string connectionString, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
{
//创建一个SqlCommand对象
SqlCommand cmd = new SqlCommand();
//创建一个SqlConnection对象
SqlConnection conn = new SqlConnection(connectionString); //在这里我们用一个try/catch结构执行sql文本命令/存储过程,因为如果这个方法产生一个异常我们要关闭连接,因为没有读取器存在,
//因此commandBehaviour.CloseConnection 就不会执行
try
{
//调用 PrepareCommand 方法,对 SqlCommand 对象设置参数
PrepareCommand(cmd, conn, null, cmdType, cmdText, commandParameters);
//调用 SqlCommand 的 ExecuteReader 方法
SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
//清除参数
cmd.Parameters.Clear();
return reader;
}
catch
{
//关闭连接,抛出异常
conn.Close();
throw;
}
} /// <summary>
/// 用指定的数据库连接字符串执行一个命令并返回一个数据集的第一列
/// </summary>
/// <remarks>
///例如:
/// Object obj = ExecuteScalar(connString, CommandType.StoredProcedure, "PublishOrders", new SqlParameter("@prodid", 24));
/// </remarks>
///<param name="connectionString">一个有效的连接字符串</param>
/// <param name="commandType">命令类型(存储过程, 文本, 等等)</param>
/// <param name="commandText">存储过程名称或者sql命令语句</param>
/// <param name="commandParameters">执行命令所用参数的集合</param>
/// <returns>用 Convert.To{Type}把类型转换为想要的 </returns>
public static object ExecuteScalar(string connectionString, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand cmd = new SqlCommand();
PrepareCommand(cmd, connection, null, cmdType, cmdText, commandParameters);
object val = cmd.ExecuteScalar();
cmd.Parameters.Clear();
return val;
}
} /// <summary>
/// 用现有的SQL事务执行一个sql命令,返回数据集的第一列
/// </summary>
/// <param name="trans">一个现有的事务</param>
/// <param name="commandType">命令类型(存储过程, 文本, 等等)</param>
/// <param name="commandText">存储过程名称或者sql命令语句</param>
/// <param name="commandParameters">执行命令所用参数的集合</param>
/// <returns>数据集的第一列</returns>
public static object ExecuteScalar(SqlTransaction trans, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
{
SqlCommand cmd = new SqlCommand();
PrepareCommand(cmd, trans.Connection, trans, cmdType, cmdText, commandParameters);
object val = cmd.ExecuteScalar();
cmd.Parameters.Clear();
return val;
} /// <summary>
/// 用现有的数据库连接执行一个命令并返回一个数据集的第一列
/// </summary>
/// <remarks>
/// 例如:
/// Object obj = ExecuteScalar(connString, CommandType.StoredProcedure, "PublishOrders", new SqlParameter("@prodid", 24));
/// </remarks>
/// <param name="conn">一个存在的数据库连接</param>
/// <param name="commandType">命令类型(存储过程, 文本, 等等)</param>
/// <param name="commandText">存储过程名称或者sql命令语句</param>
/// <param name="commandParameters">执行命令所用参数的集合</param>
/// <returns>用 Convert.To{Type}把类型转换为想要的 </returns>
public static object ExecuteScalar(SqlConnection connection, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
{
SqlCommand cmd = new SqlCommand();
PrepareCommand(cmd, connection, null, cmdType, cmdText, commandParameters);
object val = cmd.ExecuteScalar();
cmd.Parameters.Clear();
return val;
} /// <summary>
/// 根据指定的数据库连接字符串返回一个DataSet
/// </summary>
/// <param name="connectionString">数据库连接字符串</param>
/// <param name="commandType">命令类型(存储过程, 文本, 等等)</param>
/// <param name="commandText">存储过程名称或者sql命令语句</param>
/// <param name="commandParameters">执行命令所用参数的集合</param>
/// <returns>DataSet</returns>
public static DataSet GetDataSet(string connectionString, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
SqlCommand cmd = new SqlCommand();
PrepareCommand(cmd, conn, null, cmdType, cmdText, commandParameters);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
da.Fill(ds);
cmd.Parameters.Clear();
return ds;
}
} /// <summary>
/// 根据指定的数据库连接字符串返回一个DataTable
/// </summary>
/// <param name="connectionString">数据库连接字符串</param>
/// <param name="commandType">命令类型(存储过程, 文本, 等等)</param>
/// <param name="commandText">存储过程名称或者sql命令语句</param>
/// <param name="commandParameters">执行命令所用参数的集合</param>
/// <returns>DataTable</returns>
public static DataTable GetDataTable(string connectionString, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
SqlCommand cmd = new SqlCommand();
PrepareCommand(cmd, conn, null, cmdType, cmdText, commandParameters);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
da.Fill(ds);
cmd.Parameters.Clear();
return ds.Tables[];
}
} /// <summary>
/// 将参数集合添加到缓存
/// </summary>
/// <param name="cacheKey">添加到缓存的变量</param>
/// <param name="cmdParms">一个将要添加到缓存的sql参数集合</param>
public static void CacheParameters(string cacheKey, params SqlParameter[] commandParameters)
{
parmCache[cacheKey] = commandParameters;
} /// <summary>
/// 找会缓存参数集合
/// </summary>
/// <param name="cacheKey">用于找回参数的关键字</param>
/// <returns>缓存的参数集合</returns>
public static SqlParameter[] GetCachedParameters(string cacheKey)
{
SqlParameter[] cachedParms = (SqlParameter[])parmCache[cacheKey]; if (cachedParms == null)
return null; SqlParameter[] clonedParms = new SqlParameter[cachedParms.Length]; for (int i = , j = cachedParms.Length; i < j; i++)
clonedParms[i] = (SqlParameter)((ICloneable)cachedParms[i]).Clone(); return clonedParms;
} /// <summary>
/// 准备执行一个命令
/// </summary>
/// <param name="cmd">sql命令</param>
/// <param name="conn">Sql连接</param>
/// <param name="trans">Sql事务</param>
/// <param name="cmdType">命令类型例如 存储过程或者文本</param>
/// <param name="cmdText">命令文本,例如:Select * from Products</param>
/// <param name="cmdParms">执行命令的参数</param>
private static void PrepareCommand(SqlCommand cmd, SqlConnection conn, SqlTransaction trans, CommandType cmdType, string cmdText, SqlParameter[] cmdParms)
{ if (conn.State != ConnectionState.Open)
{
conn.Open();
}
cmd.Connection = conn;
cmd.CommandText = cmdText;
if (trans != null)
{
cmd.Transaction = trans;
}
cmd.CommandType = cmdType;
if (cmdParms != null)
{
foreach (SqlParameter parm in cmdParms)
{
if ((parm.Value == null) || (string.IsNullOrEmpty(parm.Value.ToString())))
{
parm.Value = DBNull.Value;
}
cmd.Parameters.Add(parm);
}
}
}
}
}
 using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Runtime.Caching;
using System.Data;
using System.Data.SqlClient;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using EasyFast.Utility.CacheUtility; namespace EasyFast.Repository
{
public class ModelHelper<T>
{
private string prefix = ConfigurationManager.AppSettings["Prefix"]; #region 表名、主键与TimeStamp
//数据库表名称
public string TableName
{
get
{
return prefix + typeof(T).Name;
}
set
{
TableName = value;
}
} //表主键
public string PrimaryKey
{
get
{
return GetPropertyInfo().First().Name;
}
} public string ModelGuid
{
get
{
return GetPropertyInfo()[].Name;
}
} //TimeStamp键
public string TimeStamp
{
get
{
return GetPropertyInfo().Last().Name;
}
}
#endregion /// <summary>
/// 从缓存中获取泛型类的PropertyInfo数组,如缓存失效,则创建新的缓存
/// </summary>
/// <returns></returns>
public PropertyInfo[] GetPropertyInfo()
{
PropertyInfo[] infos;
ObjectCache cache = MemoryCache.Default;
string cacheKey = TableName + "_PropertyInfo";
if (EasyFastCache.GetInstance().GetCache(cacheKey) == null)
{
infos = typeof(T).GetProperties();
EasyFastCache.GetInstance().AddObjectForever(cacheKey, infos);
}
else
{
infos = (PropertyInfo[])EasyFastCache.GetInstance().GetCache(cacheKey);
}
return infos;
} public SqlParameter GetSqlParameter(PropertyInfo pi,T model)
{
SqlParameter parameter;
switch (pi.PropertyType.Name)
{
case "String":
int length = ;
object[] attributes = pi.GetCustomAttributes(typeof(StringLengthAttribute), true);
if (attributes != null && attributes.Length > )
{
StringLengthAttribute myAttribute = attributes[] as StringLengthAttribute;
if (myAttribute != null)
{
length = myAttribute.MaximumLength;
}
}
if (length != )
{
parameter = new SqlParameter("@" + pi.Name, SqlDbType.NVarChar, length);
}
else
{
parameter = new SqlParameter("@" + pi.Name, SqlDbType.NVarChar);
}
parameter.Value = pi.GetValue(model, null);
break;
case "Int32":
parameter = new SqlParameter("@" + pi.Name, SqlDbType.Int, );
parameter.Value = pi.GetValue(model, null);
break;
case "Int64":
parameter = new SqlParameter("@" + pi.Name, SqlDbType.BigInt, );
parameter.Value = pi.GetValue(model, null);
break;
case "DataTime":
parameter = new SqlParameter("@" + pi.Name, SqlDbType.DateTime, );
parameter.Value = pi.GetValue(model, null);
break;
case "Boolean":
parameter = new SqlParameter("@" + pi.Name, SqlDbType.Bit, );
parameter.Value = pi.GetValue(model, null);
break;
case "Guid":
parameter = new SqlParameter("@" + pi.Name, SqlDbType.UniqueIdentifier, );
parameter.Value = pi.GetValue(model, null);
break;
case "Byte":
parameter = new SqlParameter("@" + pi.Name, SqlDbType.Timestamp, );
parameter.Value = pi.GetValue(model, null);
break;
default:
parameter = new SqlParameter("@" + pi.Name, pi.GetValue(model, null));
break;
}
return parameter;
}
}
}
 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Runtime.Caching; namespace EasyFast.Utility.CacheUtility
{
public class EasyFastCache
{
private static object lockHelper = new object();
private static EasyFastCache instance = null; private EasyFastCache() { } public static EasyFastCache GetInstance()
{
if (instance == null)
{
lock (lockHelper)
{
if (instance == null)
{
instance = new EasyFastCache();
}
}
}
return instance;
} /// <summary>
/// 添加一个对象到缓存中,如30分钟未访问,则删除该缓存项
/// </summary>
/// <param name="name">该缓存项的唯一标识符</param>
/// <param name="obj">要插入的对象。</param>
public void AddObject(string name, Object dt)
{
ObjectCache cache = MemoryCache.Default;
CacheItemPolicy policy = new CacheItemPolicy();
policy.SlidingExpiration = TimeSpan.FromMinutes();
cache.Set(name, dt, policy);
} /// <summary>
/// 添加一个对象到缓存中,该对象拥有最高优先级,永不被删除
/// </summary>
/// <param name="name">该缓存项的唯一标识符</param>
/// <param name="obj">要插入的对象。</param>
public void AddObjectForever(string name, Object obj)
{
ObjectCache cache = MemoryCache.Default;
CacheItemPolicy policy = new CacheItemPolicy();
policy.Priority = CacheItemPriority.NotRemovable;
cache.Set(name, obj, policy);
} /// <summary>
/// 添加一个DataTable到缓存中,并且依赖于数据库过期策略,该方法只支持SQL2005及以上
/// </summary>
/// <param name="name">该缓存项的唯一标识符</param>
/// <param name="dt">要插入的DataTable</param>
/// <param name="sm">数据库依赖监控对象</param>
public void AddObject(string name, DataTable dt, SqlChangeMonitor sm)
{
ObjectCache cache = MemoryCache.Default;
CacheItemPolicy policy = new CacheItemPolicy();
policy.ChangeMonitors.Add(sm);
cache.Set(name, dt, policy);
} /// <summary>
/// 获取指定标识符的缓存项
/// </summary>
/// <param name="cacheKey">该缓存项的唯一标识符</param>
/// <returns>该缓存</returns>
public object GetCache(string cacheKey)
{
ObjectCache cache = MemoryCache.Default;
return cache[cacheKey];
}
}
}

本节代码下载:EasyFastCMS-2014.05.30.zip

EasyFastCMS系列教学课程——2、底层代码 ModelHelper与SQLHelper简介的更多相关文章

  1. EasyFastCMS系列教学课程——1、三层框架的搭建

    在本系列教程中,我们以一个大型CMS系统的完整开发流程为例,和大家一起探讨net开发的经验和教训.在本程序中,我们采用了流行的三层/N层框架+仓储模式的架构模式.项目分层示意图:     各层的主要用 ...

  2. Java并发编程系列-(8) JMM和底层实现原理

    8. JMM和底层实现原理 8.1 线程间的通信与同步 线程之间的通信 线程的通信是指线程之间以何种机制来交换信息.在编程中,线程之间的通信机制有两种,共享内存和消息传递. 在共享内存的并发模型里,线 ...

  3. 系列免费课程汇总(Java、单体应用、微服务、物联网、SaaS)

    概述 2020年春节尽在眼前,又忙碌了一年的你一定有很多收获:是升职加薪,还是收获爱情?是买房置业,还是新添人口? 我在2019年的最大收获是:我的第二枚千金诞生,使我顺利加入富豪行列! 新年伊始我们 ...

  4. 微软云平台windows azure入门系列八课程

    微软云平台windows azure入门系列八课程: Windows Azure入门教学系列 (一): 创建第一个WebRole程序与部署 Windows Azure入门教学系列 (二): 创建第一个 ...

  5. [No000019A]【波浪理论精典教学课程】

    波浪理论的产生和发展     拉尔夫·纳尔逊·艾略特(Ralph Nelson Elliott ),是波浪理论的创始人.1871年7月28日出生在美国密苏里州堪萨斯市的玛丽斯维利镇Marysville ...

  6. RobotFrameWork系列免费课程-开课了~

    1. 背景介绍 有一段时间没有发表过文章了,一方面除了自己确实变得懒惰外,另一方面也确实有其它事情,无法抽出闲余时间来坚持写下去. 之前在博客园中,发表了关于<公开课一:Robot FrameW ...

  7. 前端测试框架Jest系列教程 -- Asynchronous(测试异步代码)

    写在前面: 在JavaScript代码中,异步运行是很常见的.当你有异步运行的代码时,Jest需要知道它测试的代码何时完成,然后才能继续进行另一个测试.Jest提供了几种方法来处理这个问题. 测试异步 ...

  8. java中CRUD(增删查改)底层代码的实现

    java中CRUD(增删查改)底层代码的实现: package com.station.dao; import com.station.model.Product; import java.sql.* ...

  9. 底层代码创建GUI

    %底层代码创建GUI hf = figure(... 'Units','Normalized',... 'Color','w',... 'Position',[0.1 0.1 0.8 0.8]); h ...

随机推荐

  1. RocketMQ初探(二)之RocketMQ3.26版本搭建(含简单Demo测试案例)

    作为一名程序猿,要敢于直面各种现实,脾气要好,心态要棒,纵使Bug虐我千百遍,我待它如初恋,方法也有千万种,一条路不行,换条路走走,方向对了,只要前行,总会上了罗马的道. Apache4.x最新版本既 ...

  2. C++STL:流迭代器

    流迭代器是一种迭代器适配器.istream_iterator用于读取输入流,ostream_iterator用于写输出流.这些迭代器将它们所对应的流视为特定类型的元素序列.使用流迭代器时,可以用泛型算 ...

  3. springboot 中使用thymeleaf

    Spring Boot支持FreeMarker.Groovy.Thymeleaf和Mustache四种模板解析引擎,官方推荐使用Thymeleaf. spring-boot-starter-thyme ...

  4. PHP 数组中出现中文乱码,json_encode返回结果为null 或false

    想要解决这个问题,没有特别方便的方法,只有循环数组,将数组中的key和value字符串转码,转换为utf-8,即可解决问题. 代码示例:

  5. href 和src 的区别

    转载地址:http://www.58maisui.com/2016/08/03/30/?utm_source=tuicool&utm_medium=referral href和src的区别: ...

  6. NHibernate注意事项

    1.检查映射文件是否正确 2.检查配置文件的“嵌入的资源”选项 3.检查Configuration是否加载了程序集

  7. Java中Final修饰一个变量时,是引用不能变还是引用的对象不能变

    Java中,使用Final修饰一个变量,是引用不能变,还是引用对象不能变? 是引用对象的地址不能变,引用变量所指的对象的内容可以改变. final变量永远指向这个对象,是一个常量指针,而不是指向常量的 ...

  8. 混合开发之iOS快速集成DSBridge

    DSBridge-IOS github:https://github.com/wendux/DSBridge-IOS 使用 Native 实现API 代理类 //JsApiTest.m @implem ...

  9. 大话CNN

    这几年深度学习快速发展,在图像识别.语音识别.物体识别等各种场景上取得了巨大的成功,例如AlphaGo击败世界围棋冠军,iPhone X内置了人脸识别解锁功能等等,很多AI产品在世界上引起了很大的轰动 ...

  10. http和socket之长连接和短连接区别(转)

    TCP/IP TCP/IP是个协议组,可分为三个层次:网络层.传输层和应用层. 在网络层有IP协议.ICMP协议.ARP协议.RARP协议和BOOTP协议. 在传输层中有TCP协议与UDP协议. 在应 ...