EasyFastCMS系列教学课程——2、底层代码 ModelHelper与SQLHelper简介
从本节课开始,我们开始逐步正式进入实际的编码过程中。本节课的核心内容为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简介的更多相关文章
- EasyFastCMS系列教学课程——1、三层框架的搭建
在本系列教程中,我们以一个大型CMS系统的完整开发流程为例,和大家一起探讨net开发的经验和教训.在本程序中,我们采用了流行的三层/N层框架+仓储模式的架构模式.项目分层示意图: 各层的主要用 ...
- Java并发编程系列-(8) JMM和底层实现原理
8. JMM和底层实现原理 8.1 线程间的通信与同步 线程之间的通信 线程的通信是指线程之间以何种机制来交换信息.在编程中,线程之间的通信机制有两种,共享内存和消息传递. 在共享内存的并发模型里,线 ...
- 系列免费课程汇总(Java、单体应用、微服务、物联网、SaaS)
概述 2020年春节尽在眼前,又忙碌了一年的你一定有很多收获:是升职加薪,还是收获爱情?是买房置业,还是新添人口? 我在2019年的最大收获是:我的第二枚千金诞生,使我顺利加入富豪行列! 新年伊始我们 ...
- 微软云平台windows azure入门系列八课程
微软云平台windows azure入门系列八课程: Windows Azure入门教学系列 (一): 创建第一个WebRole程序与部署 Windows Azure入门教学系列 (二): 创建第一个 ...
- [No000019A]【波浪理论精典教学课程】
波浪理论的产生和发展 拉尔夫·纳尔逊·艾略特(Ralph Nelson Elliott ),是波浪理论的创始人.1871年7月28日出生在美国密苏里州堪萨斯市的玛丽斯维利镇Marysville ...
- RobotFrameWork系列免费课程-开课了~
1. 背景介绍 有一段时间没有发表过文章了,一方面除了自己确实变得懒惰外,另一方面也确实有其它事情,无法抽出闲余时间来坚持写下去. 之前在博客园中,发表了关于<公开课一:Robot FrameW ...
- 前端测试框架Jest系列教程 -- Asynchronous(测试异步代码)
写在前面: 在JavaScript代码中,异步运行是很常见的.当你有异步运行的代码时,Jest需要知道它测试的代码何时完成,然后才能继续进行另一个测试.Jest提供了几种方法来处理这个问题. 测试异步 ...
- java中CRUD(增删查改)底层代码的实现
java中CRUD(增删查改)底层代码的实现: package com.station.dao; import com.station.model.Product; import java.sql.* ...
- 底层代码创建GUI
%底层代码创建GUI hf = figure(... 'Units','Normalized',... 'Color','w',... 'Position',[0.1 0.1 0.8 0.8]); h ...
随机推荐
- LUA upvalue使用陷阱一例
CA = {} CA.TestCb = function(self, cb) if not self._cb then self._cb = function() cb() end end self. ...
- PHP - 输出缓存 - 关于ob系列函数和flush函数
偶然机会看到了flush().知道他的神奇功能可以用在异步传输的comet 模式之后,于是我去试了试这个方法.然后翻手册去看了看什么意思.发现它神奇的和ob类函数在一起.有点好奇,先放一放,来说flu ...
- 通过devtools在centos系统中启用高版本的gcc
C++11出来好久了,现在还是使用c++03的,需要在centos6.6的系统上实现gcc的升级,又不想自己编译代码. 于是选用了devtoolsset系列,安装脚本如下 安装脚本如下 functio ...
- ROS 禁止公网暴力破解SSH FTP
最简单的彻底禁止公网访问SSH FTP端口 1 2 /ip firewall filter add chain=input protocol=tcp dst-port=21-22 src-addres ...
- Python下载与安装
Python目前已支持所有主流操作系统,在Linux,Unix,Mac系统上自带Python环境,一般默认装的是Python2版本,Windows系统上没有Pyhton环境,需要我们手动安装一下,过程 ...
- spring boot aop打印http请求回复日志包含请求体
一.引入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...
- 201671010140. 2016-2017-2 《Java程序设计》java学习第十周
---恢复内容开始--- Java学习第十周 本周,学习泛型程序设计,泛型也被称为参数化类型(parameterized type),就是在定义类.接口和方法时,通过类型参数指示将要处理的 ...
- C#中不同格式数据校验的正则表达式
网上经常看到用正则表达式校验数据的文章,有的虽然总结得很全,但是大多数都没有经过严格验证,错误较多. 本文包含三十余条不同格式数据校验的C#正则表达式,一般均附有说明,且在Visual Studio里 ...
- [C++] How to prevent memory leaks
How to prevent memory leaks ? overload new/delete
- Red Hat 6.5 Samba服务器的搭建(匿名访问,免登录)
搭建Samba服务器是为了实现Linux共享目录之后,在Windows可以直接访问该共享目录. 现在介绍如何在红帽6.5系统中搭建Samba服务. 搭建Samba服务之前,yum源必须配置好,本地源和 ...