1079 lines
40 KiB
C#
1079 lines
40 KiB
C#
using CYQ.Data.Aop;
|
||
using CYQ.Data.Cache;
|
||
using CYQ.Data.Json;
|
||
using CYQ.Data.SQL;
|
||
using CYQ.Data.Table;
|
||
using CYQ.Data.Tool;
|
||
using System;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using System.Data.Common;
|
||
using System.Reflection;
|
||
using System.Text;
|
||
using System.Threading;
|
||
|
||
namespace CYQ.Data.Aop
|
||
{
|
||
/// <summary>
|
||
/// 内部智能缓存:AutoCache
|
||
/// </summary>
|
||
internal static partial class AopCache
|
||
{
|
||
private static DistributedCache _AopCache = DistributedCache.Instance;//有可能使用MemCache操作
|
||
|
||
internal static bool GetCache(AopEnum action, AopInfo aopInfo)//Begin
|
||
{
|
||
switch (action)
|
||
{
|
||
case AopEnum.ExeNonQuery:
|
||
case AopEnum.Insert:
|
||
case AopEnum.Update:
|
||
case AopEnum.Delete:
|
||
return false;
|
||
}
|
||
if (!IsCanOperateCache(action, aopInfo))
|
||
{
|
||
return false;
|
||
}
|
||
string baseKey = GetBaseKey(aopInfo);
|
||
//查看是否通知我移除
|
||
string key = GetKey(action, aopInfo, baseKey);
|
||
object obj = _AopCache.Get(key);
|
||
switch (action)
|
||
{
|
||
case AopEnum.ExeMDataTableList:
|
||
if (obj != null)
|
||
{
|
||
List<MDataTable> list = new List<MDataTable>();
|
||
if (obj is List<MDataTable>)
|
||
{
|
||
List<MDataTable> listObj = obj as List<MDataTable>;
|
||
foreach (MDataTable table in listObj)
|
||
{
|
||
list.Add(table.Clone());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Dictionary<string, string> jd = JsonHelper.Split(obj.ToString());
|
||
if (jd != null && jd.Count > 0)
|
||
{
|
||
foreach (KeyValuePair<string, string> item in jd)
|
||
{
|
||
list.Add(MDataTable.CreateFrom(item.Value, null, EscapeOp.Encode));
|
||
}
|
||
}
|
||
}
|
||
aopInfo.TableList = list;
|
||
}
|
||
break;
|
||
case AopEnum.Select:
|
||
case AopEnum.ExeMDataTable:
|
||
if (obj != null)
|
||
{
|
||
if (obj is MDataTable)
|
||
{
|
||
aopInfo.Table = (obj as MDataTable).Clone();
|
||
}
|
||
else
|
||
{
|
||
aopInfo.Table = MDataTable.CreateFrom(obj.ToString(), null, EscapeOp.Encode);
|
||
}
|
||
}
|
||
break;
|
||
case AopEnum.ExeList:
|
||
case AopEnum.SelectList:
|
||
case AopEnum.ExeJson:
|
||
case AopEnum.SelectJson:
|
||
case AopEnum.ExeScalar:
|
||
case AopEnum.Exists:
|
||
if (obj != null)
|
||
{
|
||
aopInfo.ExeResult = obj;
|
||
}
|
||
break;
|
||
case AopEnum.Fill:
|
||
if (obj != null)
|
||
{
|
||
MDataRow row;
|
||
if (obj is MDataRow)
|
||
{
|
||
row = (obj as MDataRow).Clone();
|
||
}
|
||
else
|
||
{
|
||
row = MDataRow.CreateFrom(obj);
|
||
}
|
||
aopInfo.Row = row;
|
||
aopInfo.IsSuccess = true;
|
||
}
|
||
break;
|
||
case AopEnum.GetCount:
|
||
if (obj != null)
|
||
{
|
||
aopInfo.ExeResult = obj;
|
||
}
|
||
break;
|
||
}
|
||
baseKey = key = null;
|
||
return obj != null;
|
||
}
|
||
|
||
internal static void SetCache(AopEnum action, AopInfo aopInfo)//End
|
||
{
|
||
if (!IsCanOperateCache(action, aopInfo))
|
||
{
|
||
return;
|
||
}
|
||
string baseKey = GetBaseKey(aopInfo);
|
||
switch (action)
|
||
{
|
||
case AopEnum.ExeNonQuery:
|
||
case AopEnum.Insert:
|
||
case AopEnum.Update:
|
||
case AopEnum.Delete:
|
||
if (aopInfo.IsSuccess || (aopInfo.ExeResult is int) && ((int)aopInfo.ExeResult > 0))
|
||
{
|
||
if (action == AopEnum.Update || action == AopEnum.ExeNonQuery)
|
||
{
|
||
//检测是否指定忽略的列名(多数据库的指定?{XXX.XXX})
|
||
if (!IsCanRemoveCache(action, aopInfo))
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
ReadyForRemove(baseKey);
|
||
}
|
||
return;
|
||
}
|
||
|
||
|
||
if (_AopCache.CacheType == CacheType.LocalCache && _AopCache.Count > 5000000)//数量超过500万
|
||
{
|
||
return;
|
||
}
|
||
string key = GetKey(action, aopInfo, baseKey);
|
||
int flag;//0 正常;1:未识别;2:不允许缓存
|
||
SetBaseKeys(aopInfo, key, out flag);//存档Key,后续缓存失效 批量删除
|
||
if (flag == 2)
|
||
{
|
||
return;//
|
||
}
|
||
double cacheTime = AppConfig.Cache.DefaultMinutes;// Math.Abs(12 - DateTime.Now.Hour) * 60 + DateTime.Now.Second;//缓存中午或到夜里1点
|
||
if (flag == 1 || aopInfo.PageIndex > 2) // 后面的页数,缓存时间可以短一些
|
||
{
|
||
cacheTime = 1;//未知道操作何表时,只缓存1分钟(比如存储过程等语句)
|
||
}
|
||
switch (action)
|
||
{
|
||
case AopEnum.ExeMDataTableList:
|
||
if (IsCanSetCache(aopInfo.TableList))
|
||
{
|
||
if (_AopCache.CacheType == CacheType.LocalCache)
|
||
{
|
||
List<MDataTable> cloneList = new List<MDataTable>(aopInfo.TableList.Count);
|
||
foreach (MDataTable table in aopInfo.TableList)
|
||
{
|
||
cloneList.Add(table.Clone());
|
||
}
|
||
_AopCache.Set(key, cloneList, cacheTime);
|
||
}
|
||
else
|
||
{
|
||
JsonHelper js = new JsonHelper(false, false);
|
||
foreach (MDataTable table in aopInfo.TableList)
|
||
{
|
||
js.Add(Guid.NewGuid().ToString(), table.ToJson(true, true, RowOp.IgnoreNull, false, EscapeOp.Encode));
|
||
}
|
||
js.AddBr();
|
||
_AopCache.Set(key, js.ToString(), cacheTime);
|
||
}
|
||
}
|
||
break;
|
||
case AopEnum.Select:
|
||
case AopEnum.ExeMDataTable:
|
||
if (IsCanSetCache(aopInfo.Table))
|
||
{
|
||
if (_AopCache.CacheType == CacheType.LocalCache)
|
||
{
|
||
_AopCache.Set(key, aopInfo.Table.Clone(), cacheTime);
|
||
}
|
||
else
|
||
{
|
||
_AopCache.Set(key, aopInfo.Table.ToJson(true, true, RowOp.IgnoreNull, false, EscapeOp.Encode), cacheTime);
|
||
}
|
||
}
|
||
break;
|
||
case AopEnum.ExeJson:
|
||
if (IsCanSetCache(aopInfo.ExeResult))
|
||
{
|
||
_AopCache.Set(key, aopInfo.ExeResult, cacheTime);
|
||
}
|
||
break;
|
||
case AopEnum.ExeList:
|
||
if (IsCanSetCache(aopInfo.ExeResult))
|
||
{
|
||
_AopCache.Set(key, JsonHelper.ToJson(aopInfo.ExeResult, false, RowOp.IgnoreNull, EscapeOp.Encode), cacheTime);
|
||
}
|
||
break;
|
||
case AopEnum.SelectList:
|
||
if (IsCanSetCache(aopInfo.ExeResult))
|
||
{
|
||
JsonHelper js = new JsonHelper(true, true);
|
||
js.Escape = EscapeOp.Encode;
|
||
if (aopInfo.TotalCount > 0)
|
||
{
|
||
js.Total = aopInfo.TotalCount;
|
||
}
|
||
js.LoopCheckList.Add(aopInfo.ExeResult.GetHashCode(), 0);
|
||
js.Fill(aopInfo.ExeResult);
|
||
string result = js.ToString(true);
|
||
_AopCache.Set(key, result, cacheTime);
|
||
}
|
||
break;
|
||
|
||
case AopEnum.SelectJson:
|
||
if (IsCanSetCache(aopInfo.ExeResult))
|
||
{
|
||
JsonHelper js = new JsonHelper(false, false);
|
||
js.Escape = EscapeOp.Encode;
|
||
js.Add("total", aopInfo.TotalCount.ToString(), true);
|
||
js.Add("rows", aopInfo.ExeResult.ToString());
|
||
_AopCache.Set(key, js.ToString(), cacheTime);
|
||
}
|
||
break;
|
||
case AopEnum.ExeScalar:
|
||
_AopCache.Set(key, aopInfo.ExeResult, cacheTime);
|
||
break;
|
||
case AopEnum.Fill:
|
||
if (_AopCache.CacheType == CacheType.LocalCache)
|
||
{
|
||
_AopCache.Set(key, aopInfo.Row.Clone(), cacheTime);
|
||
}
|
||
else
|
||
{
|
||
_AopCache.Set(key, aopInfo.Row.ToJson(RowOp.IgnoreNull, false, EscapeOp.Encode), cacheTime);
|
||
}
|
||
break;
|
||
case AopEnum.GetCount:
|
||
_AopCache.Set(key, aopInfo.ExeResult, cacheTime);
|
||
break;
|
||
case AopEnum.Exists:
|
||
_AopCache.Set(key, aopInfo.ExeResult, cacheTime);
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
#region 检测过滤
|
||
static bool IsCanSetCache(List<MDataTable> dtList)
|
||
{
|
||
foreach (MDataTable item in dtList)
|
||
{
|
||
if (!IsCanSetCache(item))
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
static bool IsCanSetCache(MDataTable dt)
|
||
{
|
||
if (dt == null || dt.Rows.Count > 1000)
|
||
{
|
||
return false;// 大于1000条的不缓存,1000这个数字,性能调节上相对合适。
|
||
}
|
||
if (_AopCache.CacheType != CacheType.LocalCache)
|
||
{
|
||
foreach (MCellStruct item in dt.Columns)
|
||
{
|
||
if (DataType.GetGroup(item.SqlType) == DataGroupType.Object)//只存档基础类型
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
static bool IsCanSetCache(object listResult)
|
||
{
|
||
//return false;
|
||
if (listResult == null)
|
||
{
|
||
return false;
|
||
}
|
||
if (!(listResult is String) && listResult is IEnumerable)
|
||
{
|
||
int i = 0;
|
||
foreach (object o in listResult as IEnumerable)
|
||
{
|
||
if (i == 0)// && _AutoCache.CacheType != CacheType.LocalCache
|
||
{
|
||
List<PropertyInfo> pis = ReflectTool.GetPropertyList(o.GetType());
|
||
foreach (PropertyInfo item in pis)
|
||
{
|
||
if (DataType.GetGroup(DataType.GetSqlType(item.PropertyType)) == DataGroupType.Object)//只存档基础类型
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
i++;
|
||
if (i > 100)// 大于N条的不缓存。List<T> 的缓存,在存和取间都要转2次( JsonString=>MDataTable=>List<T>),所以限制条数少一些,性能调节上相对合适。
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
#region 对列进行处理。
|
||
private static Dictionary<string, string> _IngoreCacheColumns = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||
static readonly object obj = new object();
|
||
internal static Dictionary<string, string> IngoreCacheColumns
|
||
{
|
||
get
|
||
{
|
||
if (_IngoreCacheColumns.Count == 0)
|
||
{
|
||
string ignoreColumns = AppConfig.AutoCache.IngoreColumns;
|
||
if (!string.IsNullOrEmpty(ignoreColumns))
|
||
{
|
||
lock (obj)
|
||
{
|
||
if (_IngoreCacheColumns.Count == 0)
|
||
{
|
||
_IngoreCacheColumns = JsonHelper.Split(ignoreColumns);
|
||
}
|
||
}
|
||
if (_IngoreCacheColumns == null)
|
||
{
|
||
Error.Throw("IngoreCacheColumns config must be a json!");
|
||
}
|
||
}
|
||
}
|
||
return _IngoreCacheColumns;
|
||
}
|
||
set
|
||
{
|
||
if (value == null)
|
||
{
|
||
_IngoreCacheColumns.Clear();
|
||
}
|
||
else
|
||
{
|
||
_IngoreCacheColumns = value;
|
||
}
|
||
}
|
||
}
|
||
static bool IsCanRemoveCache(AopEnum action, AopInfo aopInfo)
|
||
{
|
||
if (IngoreCacheColumns.Count > 0)
|
||
{
|
||
string databaseName = string.Empty;
|
||
if (action == AopEnum.ExeNonQuery)
|
||
{
|
||
if (aopInfo.IsProc || !aopInfo.ProcName.ToLower().StartsWith("update "))
|
||
{
|
||
return true;
|
||
}
|
||
databaseName = aopInfo.MProc.DataBaseName;
|
||
}
|
||
else
|
||
{
|
||
databaseName = aopInfo.MAction.DataBaseName;
|
||
}
|
||
string tableName = aopInfo.TableName;
|
||
if (string.IsNullOrEmpty(tableName))
|
||
{
|
||
List<string> tableNames = SqlFormat.GetTableNamesFromSql(aopInfo.ProcName);
|
||
if (tableNames == null || tableNames.Count != 1)//多个表的批量语句也不处理。
|
||
{
|
||
return true;
|
||
}
|
||
tableName = tableNames[0];
|
||
}
|
||
//获取被更新的字段名,
|
||
string[] columns = null;
|
||
if (IngoreCacheColumns.ContainsKey(tableName))
|
||
{
|
||
columns = IngoreCacheColumns[tableName].ToLower().Split(',');
|
||
}
|
||
else if (IngoreCacheColumns.ContainsKey(databaseName + "." + tableName))
|
||
{
|
||
columns = IngoreCacheColumns[databaseName + "." + tableName].ToLower().Split(',');
|
||
}
|
||
if (columns != null)//拿到要忽略的列。
|
||
{
|
||
List<string> updateColumns = GetChangedColumns(action, aopInfo);//拿到已更新的列
|
||
if (columns.Length >= updateColumns.Count)
|
||
{
|
||
List<string> ignoreColumns = new List<string>(columns.Length);
|
||
ignoreColumns.AddRange(columns);
|
||
|
||
foreach (string item in updateColumns)
|
||
{
|
||
if (!ignoreColumns.Contains(item))
|
||
{
|
||
return true;//只要有一个不存在。
|
||
}
|
||
}
|
||
return false;//全都不存在
|
||
}
|
||
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
private static List<string> GetChangedColumns(AopEnum action, AopInfo aopInfo)
|
||
{
|
||
List<string> columns = new List<string>();
|
||
string expression = string.Empty;
|
||
if (action == AopEnum.Update)
|
||
{
|
||
foreach (MDataCell item in aopInfo.Row)
|
||
{
|
||
if (item.State == 2 && !item.Struct.IsPrimaryKey)
|
||
{
|
||
columns.Add(item.ColumnName.ToLower());
|
||
}
|
||
}
|
||
expression = aopInfo.UpdateExpression;
|
||
}
|
||
else if (action == AopEnum.ExeNonQuery && !aopInfo.IsProc)
|
||
{
|
||
string sql = aopInfo.ProcName.ToLower();
|
||
int setStart = sql.IndexOf(" set ");
|
||
int whereEnd = sql.IndexOf(" where ");
|
||
if (whereEnd < setStart)
|
||
{
|
||
expression = sql.Substring(setStart + 5);
|
||
}
|
||
else
|
||
{
|
||
expression = sql.Substring(setStart + 5, whereEnd - setStart + 5);
|
||
}
|
||
}
|
||
if (!string.IsNullOrEmpty(expression))
|
||
{
|
||
string[] items = expression.ToLower().Split(',');
|
||
foreach (string item in items)
|
||
{
|
||
if (item.IndexOf('=') > -1)
|
||
{
|
||
string column = item.Split('=')[0];
|
||
if (!columns.Contains(column))
|
||
{
|
||
columns.Add(column);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return columns;
|
||
}
|
||
#endregion
|
||
#endregion
|
||
|
||
#region 缓存Key的管理
|
||
/// <summary>
|
||
/// 单机存档: 表的baseKey:key1,key2,key3...
|
||
/// </summary>
|
||
private static MDictionary<string, StringBuilder> cacheKeys = new MDictionary<string, StringBuilder>(StringComparer.OrdinalIgnoreCase);
|
||
private static void SetBaseKeys(AopInfo para, string key, out int flag)
|
||
{
|
||
List<string> items = GetBaseKeys(para, out flag);
|
||
if (items != null && items.Count > 0)
|
||
{
|
||
foreach (string item in items)
|
||
{
|
||
SetBaseKey(item, key);
|
||
}
|
||
items = null;
|
||
}
|
||
}
|
||
private static void SetBaseKey(string baseKey, string key)
|
||
{
|
||
//baseKey是表的,不包括视图和自定义语句
|
||
if (_AopCache.CacheType == CacheType.LocalCache)
|
||
{
|
||
if (cacheKeys.ContainsKey(baseKey))
|
||
{
|
||
cacheKeys[baseKey] = cacheKeys[baseKey].Append("," + key);
|
||
}
|
||
else
|
||
{
|
||
cacheKeys.Add(baseKey, new StringBuilder(key));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
StringBuilder sb = _AopCache.Get<StringBuilder>(baseKey);
|
||
if (sb == null)
|
||
{
|
||
_AopCache.Set(baseKey, new StringBuilder(key));
|
||
}
|
||
else
|
||
{
|
||
sb.Append("," + key);
|
||
_AopCache.Set(baseKey, sb);
|
||
}
|
||
}
|
||
}
|
||
//private static List<string> _CacheTables = null;
|
||
//internal static List<string> CacheTables
|
||
//{
|
||
// get
|
||
// {
|
||
// if (_CacheTables == null)
|
||
// {
|
||
// _CacheTables = new List<string>();
|
||
// string tables = AppConfig.Cache.CacheTables.ToLower();
|
||
// if (!string.IsNullOrEmpty(tables))
|
||
// {
|
||
// _CacheTables.AddRange(tables.Split(','));
|
||
// }
|
||
// }
|
||
// return _CacheTables;
|
||
// }
|
||
// set
|
||
// {
|
||
// _CacheTables = value;
|
||
// }
|
||
//}
|
||
//private static List<string> _NoCacheTables = null;
|
||
//internal static List<string> NoCacheTables
|
||
//{
|
||
// get
|
||
// {
|
||
// if (_NoCacheTables == null)
|
||
// {
|
||
// _NoCacheTables = new List<string>();
|
||
// string tables = AppConfig.Cache.NoCacheTables.ToLower();
|
||
// if (!string.IsNullOrEmpty(tables))
|
||
// {
|
||
// _NoCacheTables.AddRange(tables.Split(','));
|
||
// }
|
||
// }
|
||
// return _NoCacheTables;
|
||
// }
|
||
// set
|
||
// {
|
||
// _NoCacheTables = value;
|
||
// }
|
||
//}
|
||
private static bool IsCanOperateCache(AopEnum action, AopInfo para)
|
||
{
|
||
if (para.IsTransaction) // 事务中,读数据时,处理读缓存是无法放置共享锁的情况
|
||
{
|
||
//判断事务等级,如果不是最低等级,则事务的查询不处理缓存
|
||
switch (action)//处理查询 动作
|
||
{
|
||
case AopEnum.Exists:
|
||
case AopEnum.Fill:
|
||
case AopEnum.GetCount:
|
||
case AopEnum.Select:
|
||
case AopEnum.SelectList:
|
||
case AopEnum.SelectJson:
|
||
if (para.MAction.dalHelper.TranLevel != System.Data.IsolationLevel.ReadUncommitted)
|
||
{
|
||
return false;
|
||
}
|
||
break;
|
||
case AopEnum.ExeMDataTable:
|
||
case AopEnum.ExeMDataTableList:
|
||
case AopEnum.ExeScalar:
|
||
case AopEnum.ExeList:
|
||
case AopEnum.ExeJson:
|
||
if (para.MProc.dalHelper.TranLevel != System.Data.IsolationLevel.ReadUncommitted)
|
||
{
|
||
return false;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
List<string> tables = GetRelationTables(para);
|
||
if (tables != null && tables.Count > 0)
|
||
{
|
||
string cacheTables = "," + AppConfig.AutoCache.Tables + ",";//demo.Aa
|
||
string nNoCacheTables = "," + AppConfig.AutoCache.IngoreTables + ",";
|
||
foreach (string tableName in tables)
|
||
{
|
||
if (cacheTables.Length > 2)
|
||
{
|
||
if (cacheTables.IndexOf("," + tableName + ",", StringComparison.OrdinalIgnoreCase) == -1
|
||
&& cacheTables.IndexOf(para.DataBase + "." + tableName + ",", StringComparison.OrdinalIgnoreCase) == -1)
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
else if (nNoCacheTables.Length > 2)
|
||
{
|
||
if (nNoCacheTables.IndexOf("," + tableName + ",", StringComparison.OrdinalIgnoreCase) > -1
|
||
|| nNoCacheTables.IndexOf(para.DataBase + "." + tableName + ",", StringComparison.OrdinalIgnoreCase) > -1)
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
string baseKey = GetBaseKey(para, tableName);
|
||
string delKey = "AutoCache.Delete:" + baseKey;
|
||
if (_AopCache.Contains(delKey))
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
return true;
|
||
// string delKey = "DeleteAutoCache:" + baseKey;
|
||
// return !_MemCache.Contains(delKey);
|
||
//if (baseKey.Contains(".ActionV") || baseKey.Contains(".ProcS"))
|
||
//{
|
||
|
||
// return true;
|
||
//}
|
||
//TimeSpan ts = DateTime.Now - _MemCache.Get<DateTime>("Del:" + baseKey);
|
||
//return ts.TotalSeconds > 6;//5秒内无缓存。
|
||
}
|
||
|
||
private static string GetBaseKey(AopInfo para)
|
||
{
|
||
return GetBaseKey(para, null);
|
||
}
|
||
internal static string GetBaseKey(string tableName, string conn)
|
||
{
|
||
if (string.IsNullOrEmpty(conn))
|
||
{
|
||
conn = CrossDB.GetConn(tableName, out tableName, conn);
|
||
}
|
||
return "AutoCache:" + ConnBean.GetHashKey(conn) + "." + tableName;
|
||
}
|
||
|
||
private static string GetBaseKey(AopInfo para, string tableName)
|
||
{
|
||
if (string.IsNullOrEmpty(tableName))
|
||
{
|
||
if (para.MAction != null)
|
||
{
|
||
foreach (MCellStruct ms in para.MAction.Data.Columns)
|
||
{
|
||
if (!string.IsNullOrEmpty(ms.TableName))
|
||
{
|
||
tableName = ms.TableName;
|
||
break;
|
||
}
|
||
}
|
||
if (string.IsNullOrEmpty(tableName))
|
||
{
|
||
if (para.TableName.Contains(" "))
|
||
{
|
||
tableName = "View_" + TableInfo.GetHashKey(para.TableName);
|
||
}
|
||
else
|
||
{
|
||
tableName = para.TableName;
|
||
}
|
||
//if (para.MAction.Data.Columns.isViewOwner)
|
||
//{
|
||
// tableName = "ActionV" + Math.Abs(para.TableName.GetHashCode());
|
||
//}
|
||
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (!para.IsProc)
|
||
{
|
||
tableName = SqlSyntax.Analyze(para.ProcName).TableName;
|
||
}
|
||
else
|
||
{
|
||
tableName = "Proc_" + para.ProcName;
|
||
}
|
||
}
|
||
}
|
||
return GetBaseKey(tableName, para.ConnectionString);
|
||
}
|
||
private static List<string> GetBaseKeys(AopInfo para, out int flag)
|
||
{
|
||
flag = 0;//0 正常;1:未识别;2:暂不允许缓存
|
||
List<string> baseKeys = new List<string>();
|
||
List<string> tables = GetRelationTables(para);
|
||
if (tables != null && tables.Count > 0)
|
||
{
|
||
foreach (string tableName in tables)
|
||
{
|
||
string baseKey = GetBaseKey(para, tableName);
|
||
string delKey = "AutoCache.Delete:" + baseKey;
|
||
if (_AopCache.Contains(delKey))
|
||
{
|
||
//说明此项不可缓存
|
||
flag = 2;
|
||
baseKeys.Clear();
|
||
baseKeys = null;
|
||
return null;
|
||
}
|
||
baseKeys.Add(baseKey);
|
||
}
|
||
//tables.Clear();//自己给自己造坑,花了2小时才找到这坑
|
||
//tables = null;
|
||
}
|
||
if (baseKeys.Count == 0)
|
||
{
|
||
flag = 1;
|
||
}
|
||
return baseKeys;
|
||
}
|
||
private static string GetKey(AopEnum action, AopInfo aopInfo)
|
||
{
|
||
return GetKey(action, aopInfo, GetBaseKey(aopInfo));
|
||
}
|
||
private static string GetKey(AopEnum action, AopInfo aopInfo, string baseKey)
|
||
{
|
||
StringBuilder sb = new StringBuilder();
|
||
sb.Append(baseKey);
|
||
switch (action)
|
||
{
|
||
case AopEnum.ExeNonQuery:
|
||
case AopEnum.Insert:
|
||
case AopEnum.Update:
|
||
case AopEnum.Delete:
|
||
return sb.ToString();
|
||
}
|
||
|
||
#region Key1:DBType
|
||
if (aopInfo.DBParameters != null && aopInfo.DBParameters.Count > 0)
|
||
{
|
||
foreach (DbParameter item in aopInfo.DBParameters)
|
||
{
|
||
sb.Append(item.ParameterName);
|
||
sb.Append(item.Value);
|
||
}
|
||
}
|
||
if (aopInfo.CustomDbPara != null)
|
||
{
|
||
foreach (AopCustomDbPara item in aopInfo.CustomDbPara)
|
||
{
|
||
sb.Append(item.ParaName);
|
||
sb.Append(item.Value);
|
||
}
|
||
}
|
||
if (aopInfo.SelectColumns != null)
|
||
{
|
||
foreach (object item in aopInfo.SelectColumns)
|
||
{
|
||
sb.Append(item);
|
||
sb.Append(item);
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
switch (action)
|
||
{
|
||
case AopEnum.ExeMDataTableList:
|
||
case AopEnum.ExeScalar:
|
||
sb.Append(aopInfo.IsProc);
|
||
sb.Append(aopInfo.ProcName);
|
||
sb.Append(".");
|
||
sb.Append(action);
|
||
break;
|
||
case AopEnum.ExeMDataTable:
|
||
case AopEnum.ExeList:
|
||
case AopEnum.ExeJson:
|
||
sb.Append(aopInfo.IsProc);
|
||
sb.Append(aopInfo.ProcName);
|
||
sb.Append(".ExeTable");
|
||
break;
|
||
case AopEnum.Exists:
|
||
case AopEnum.Fill:
|
||
case AopEnum.GetCount:
|
||
sb.Append(action);
|
||
sb.Append(aopInfo.TableName);
|
||
sb.Append(aopInfo.Where);
|
||
sb.Append(".");
|
||
sb.Append(action);
|
||
break;
|
||
case AopEnum.Select:
|
||
case AopEnum.SelectList:
|
||
case AopEnum.SelectJson:
|
||
sb.Append(aopInfo.TableName);
|
||
sb.Append(aopInfo.PageIndex);
|
||
sb.Append(aopInfo.PageSize);
|
||
sb.Append(aopInfo.Where);
|
||
sb.Append(".Select");
|
||
break;
|
||
}
|
||
|
||
return StaticTool.GetHashKey(sb.ToString().ToLower());
|
||
}
|
||
private static List<string> GetRelationTables(AopInfo para)
|
||
{
|
||
List<string> tables = null;
|
||
if (para.MAction != null)
|
||
{
|
||
tables = para.MAction.Data.Columns.RelationTables;
|
||
}
|
||
else if (para.MProc != null && !para.IsProc)
|
||
{
|
||
if (para.Table != null)
|
||
{
|
||
tables = para.Table.Columns.RelationTables;
|
||
}
|
||
else
|
||
{
|
||
tables = SqlFormat.GetTableNamesFromSql(para.ProcName);
|
||
}
|
||
}
|
||
return tables;
|
||
}
|
||
|
||
#endregion
|
||
|
||
|
||
|
||
class KeyTable
|
||
{
|
||
public const string KeyTableName = "SysAutoCache";
|
||
public static bool HasAutoCacheTable = false;
|
||
public static bool CheckSysAutoCacheTable()
|
||
{
|
||
if (!HasAutoCacheTable && !string.IsNullOrEmpty(AppConfig.AutoCache.Conn))
|
||
{
|
||
string AutoCacheConn = AppConfig.AutoCache.Conn;
|
||
if (DBTool.TestConn(AutoCacheConn))
|
||
{
|
||
HasAutoCacheTable = DBTool.Exists(KeyTableName, AutoCacheConn);
|
||
//检测数据是否存在表
|
||
if (!HasAutoCacheTable)
|
||
{
|
||
MDataColumn mdc = new MDataColumn();
|
||
mdc.Add("CacheKey", System.Data.SqlDbType.NVarChar, false, false, 200, true, null);
|
||
mdc.Add("CacheTime", System.Data.SqlDbType.BigInt, false, false, -1);
|
||
HasAutoCacheTable = DBTool.CreateTable(KeyTableName, mdc, AutoCacheConn);
|
||
if (!HasAutoCacheTable)//若创建失败,可能并发下其它进程创建了。
|
||
{
|
||
HasAutoCacheTable = DBTool.Exists(KeyTableName, AutoCacheConn);//重新检测表是否存在。
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return HasAutoCacheTable;
|
||
}
|
||
private static MAction _ActionInstance;
|
||
/// <summary>
|
||
/// 使用同一个链接,并且不关闭。
|
||
/// </summary>
|
||
public static MAction ActionInstance
|
||
{
|
||
get
|
||
{
|
||
if (_ActionInstance == null)
|
||
{
|
||
_ActionInstance = new MAction(KeyTableName, AppConfig.AutoCache.Conn);
|
||
_ActionInstance.SetAopState(AopOp.CloseAll);//关掉自动缓存和Aop
|
||
_ActionInstance.dalHelper.IsWriteLogOnError = false;
|
||
}
|
||
return _ActionInstance;
|
||
}
|
||
|
||
}
|
||
public static void SetKey(string key)
|
||
{
|
||
MAction action = ActionInstance;//
|
||
if (action.Exists(key))//更新时间
|
||
{
|
||
action.Set(1, DateTime.Now.ToString("yyyyMMddHHmmssfff"));
|
||
action.Update(key);
|
||
}
|
||
else
|
||
{
|
||
action.Set(0, key);
|
||
action.Set(1, DateTime.Now.ToString("yyyyMMddHHmmssfff"));
|
||
action.AllowInsertID = true;
|
||
action.Insert(InsertOp.None);
|
||
|
||
}
|
||
|
||
}
|
||
public static string keyTime = DateTime.Now.ToString("yyyyMMddHHmmssfff");
|
||
public static void ReadAndRemoveKey()
|
||
{
|
||
MAction action = ActionInstance;//
|
||
string cacheTime = DBTool.Keyword("CacheTime", action.DataBaseType);
|
||
MDataTable dt = action.Select(cacheTime + ">" + keyTime + " order by " + cacheTime + " asc");
|
||
if (dt.Rows.Count > 0)
|
||
{
|
||
foreach (MDataRow row in dt.Rows)
|
||
{
|
||
RemoveCache(row.Get<string>(0));//移除。
|
||
}
|
||
keyTime = dt.Rows[dt.Rows.Count - 1].Get<string>(1);//将时间重置为为最后一次最大的时间。
|
||
}
|
||
}
|
||
}
|
||
/*
|
||
#region 实例对象
|
||
/// <summary>
|
||
/// 单例
|
||
/// </summary>
|
||
public static AutoCache Instance
|
||
{
|
||
get
|
||
{
|
||
|
||
return Shell.instance;
|
||
}
|
||
}
|
||
class Shell
|
||
{
|
||
internal static readonly AutoCache instance = new AutoCache();
|
||
}
|
||
internal AutoCache()
|
||
{
|
||
|
||
}
|
||
|
||
#endregion
|
||
*/
|
||
}
|
||
|
||
/// <summary>
|
||
/// 线程任务
|
||
/// </summary>
|
||
internal static partial class AopCache
|
||
{
|
||
static AopCache()
|
||
{
|
||
ThreadBreak.AddGlobalThread(new ParameterizedThreadStart(ClearCache));
|
||
}
|
||
#region 定时清理Cache机制
|
||
//根据移除的频率,控制该项缓存的存在。
|
||
//此缓存算法,后续增加
|
||
private static Queue<string> removeList = new Queue<string>();
|
||
private static Queue<string> removeListForKeyTask = new Queue<string>();
|
||
public static void ReadyForRemove(string baseKey)
|
||
{
|
||
string delKey = "AutoCache.Delete:" + baseKey;
|
||
if (!_AopCache.Contains(delKey))
|
||
{
|
||
if (!removeList.Contains(baseKey))
|
||
{
|
||
try
|
||
{
|
||
removeList.Enqueue(baseKey);
|
||
}
|
||
catch
|
||
{
|
||
|
||
}
|
||
|
||
}
|
||
}
|
||
_AopCache.Set(delKey, 0, 0.1);//设置6秒时间
|
||
}
|
||
public static void ClearCache(object threadID)
|
||
{
|
||
try
|
||
{
|
||
System.Diagnostics.Debug.WriteLine("AutoCache.ClearCache on Thread :" + threadID);
|
||
while (true)
|
||
{
|
||
|
||
Thread.Sleep(5);
|
||
if (!KeyTable.HasAutoCacheTable && KeyTable.CheckSysAutoCacheTable())//检测并创建表,放在循环中,是因为可能在代码中延后会AppConifg.Cache.AutoCacheConn赋值;
|
||
{
|
||
ThreadBreak.AddGlobalThread(new ParameterizedThreadStart(AutoCacheKeyTask));//将数据库检测独立一个线程,不影响此内存操作。
|
||
}
|
||
if (removeList.Count > 0)
|
||
{
|
||
string baseKey = removeList.Dequeue();
|
||
if (!string.IsNullOrEmpty(baseKey))
|
||
{
|
||
RemoveCache(baseKey);
|
||
if (KeyTable.HasAutoCacheTable)//检测是否开启AutoCacheConn数据库链接
|
||
{
|
||
removeListForKeyTask.Enqueue(baseKey);
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
}
|
||
}
|
||
public static void AutoCacheKeyTask(object threadID)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine("AutoCache.AutoCacheKeyTask on Thread :" + threadID);
|
||
while (true)//定时扫描数据库
|
||
{
|
||
int time = AppConfig.AutoCache.TaskTime;
|
||
if (time <= 0)
|
||
{
|
||
time = 1000;
|
||
}
|
||
Thread.Sleep(time);
|
||
if (removeListForKeyTask.Count > 0)
|
||
{
|
||
string baseKey = removeListForKeyTask.Dequeue();
|
||
if (!string.IsNullOrEmpty(baseKey))
|
||
{
|
||
KeyTable.SetKey(baseKey);
|
||
}
|
||
}
|
||
if (KeyTable.HasAutoCacheTable) //读取看有没有需要移除的键。
|
||
{
|
||
KeyTable.ReadAndRemoveKey();
|
||
}
|
||
}
|
||
}
|
||
private static readonly object lockObj = new object();
|
||
private static DateTime errTime = DateTime.MinValue;
|
||
internal static void RemoveCache(string baseKey)
|
||
{
|
||
try
|
||
{
|
||
lock (lockObj)
|
||
{
|
||
string keys = string.Empty;
|
||
if (_AopCache.CacheType == CacheType.LocalCache)
|
||
{
|
||
if (cacheKeys.ContainsKey(baseKey))
|
||
{
|
||
keys = cacheKeys[baseKey].ToString();
|
||
cacheKeys.Remove(baseKey);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
keys = _AopCache.Get<string>(baseKey);
|
||
}
|
||
if (!string.IsNullOrEmpty(keys))
|
||
{
|
||
foreach (string item in keys.Split(','))
|
||
{
|
||
if (string.IsNullOrEmpty(item)) { continue; }
|
||
_AopCache.Remove(item);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (ThreadAbortException e)
|
||
{
|
||
}
|
||
catch (OutOfMemoryException)
|
||
{ }
|
||
catch (Exception err)
|
||
{
|
||
if (errTime == DateTime.MinValue || errTime.AddMinutes(10) < DateTime.Now) // 10分钟记录一次
|
||
{
|
||
errTime = DateTime.Now;
|
||
Log.Write(err, LogType.Cache);
|
||
}
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
}
|
||
}
|