using System; using System.Collections.Generic; using System.Text; using CYQ.Data.Table; using CYQ.Data.SQL; using System.IO; using CYQ.Data.Tool; using CYQ.Data.Json; namespace CYQ.Data { internal class NoSqlAction : IDisposable { /// /// 清除缓存数据 /// internal static void Clear() { ResetStaticVar(); _needToSaveState.Clear(); _lockNextIDObj.Clear(); _maxID.Clear(); _lastWriteTimeList.Clear(); _isDeleteAll.Clear(); } /// /// 重置静态变量(方便回收内存,避免大量导数据后内存不回收) /// internal static void ResetStaticVar() { _tableList.Clear(); _tableList = null;//静态字典置为Null才能释放内存。 _tableList = new MDictionary(5, StringComparer.OrdinalIgnoreCase);//重新初始化。 //_needToSaveState.Clear(); //_lockNextidObj.Clear(); //_maxid.Clear(); } private static MDictionary _tableList = new MDictionary(5, StringComparer.OrdinalIgnoreCase);//内存数据库 // private static readonly object _lockTableListObj = new object(); /// /// 是否需要更新:0未更新;1仅插入[往后面插数据];2更新删除或插入[重新保存],//需要更新[全局的可以有效处理并发] /// private static MDictionary _needToSaveState = new MDictionary(5, StringComparer.OrdinalIgnoreCase); private static MDictionary _lockNextIDObj = new MDictionary(5, StringComparer.OrdinalIgnoreCase);//自增加id锁 private static MDictionary _maxID = new MDictionary(5, StringComparer.OrdinalIgnoreCase);//当前表的最大id private static MDictionary _lastWriteTimeList = new MDictionary(5, StringComparer.OrdinalIgnoreCase);//当前表的最大id // private static MDictionary _lockOperatorObj = new MDictionary(5, StringComparer.OrdinalIgnoreCase);//增删改时的互锁 private List _insertRows = new List();//新插入的集合,仅是引用MDataTable的索引 private static List _isDeleteAll = new List(5); /// /// 最后的写入时间 /// // private static DateTime _lastWriteTimeUtc = DateTime.UtcNow; private MDataTable _Table = null; private MDataTable Table { get { if (_Table != null && _Table.Rows.Count > 0) { return _Table; } else if (_tableList.ContainsKey(_FileFullName)) { _Table = _tableList[_FileFullName]; if (_Table.Rows.Count == 0 && !_isDeleteAll.Contains(_FileFullName)) { if (_maxID.ContainsKey(_FileFullName)) { _maxID.Remove(_FileFullName); } _tableList.Remove(_FileFullName);// 会引发最后一条数据无法删除的问题(已修正该问题,所以可开放)。 } else { return _Table; } } switch (_DalType) { case DataBaseType.Txt: _Table = MDataTable.CreateFrom(_FileFullName, _Row.Columns, EscapeOp.Encode); break; case DataBaseType.Xml: _Table = MDataTable.CreateFromXml(_FileFullName, _Row.Columns); break; } if (_Table == null || _Table.Columns.Count == 0) { Error.Throw("MDataTable can't load data from file : " + _FileFullName); } DateTime lastWriteTime = new IOInfo(_FileFullName).LastWriteTime; if (!_lastWriteTimeList.ContainsKey(_FileFullName)) { _lastWriteTimeList.Add(_FileFullName, lastWriteTime); } if (_Table.Rows.Count > 0) { if (!_tableList.ContainsKey(_FileFullName)) { _tableList.Add(_FileFullName, _Table); } } return _Table; } } private int maxID { get { if (!_maxID.ContainsKey(_FileFullName)) { _maxID.Add(_FileFullName, 0); } return _maxID[_FileFullName]; } set { _maxID[_FileFullName] = value; } } private object lockNextIDObj { get { if (!_lockNextIDObj.ContainsKey(_FileFullName)) { _lockNextIDObj.Add(_FileFullName, new object()); } return _lockNextIDObj[_FileFullName]; } } private int needToSaveState { get { if (!_needToSaveState.ContainsKey(_FileFullName)) { _needToSaveState.Add(_FileFullName, 0); } return _needToSaveState[_FileFullName]; } set { _needToSaveState[_FileFullName] = value; } } /// /// 下一个自增加id /// private int NextID { get { lock (lockNextIDObj) { if (maxID > 0) { maxID++; } else if (DataType.GetGroup(Table.Columns.FirstPrimary.SqlType) == DataGroupType.Number)//自增id仅对int有效 { try { int rowsCount = Table.Rows.Count; if (rowsCount > 0) { #region 读取索引 int maxRowID = 0; for (int i = 0; i < 50; i++) { int index = rowsCount - 1 - i; if (index >= 0) { int value = Table.Rows[index][0].Get(); maxRowID = Math.Max(maxRowID, value); } else { break; } } maxID = maxRowID + 1; //do //{ // if (lastIndex >= 0) // { // if (_Table.Rows[lastIndex][0].IsNull) // { // lastIndex--; // } // else // { // maxID = Convert.ToInt32(_Table.Rows[lastIndex][0].Value) + 1; // } // } // else // { // maxID = 1; // } //} //while (maxID == 0); #endregion } else { maxID = 1; } } catch { } } else { Error.Throw("Increment id only allow use for int type"); } } return maxID; } } /// /// 包含路径的完整文件名称 /// string _FileFullName = string.Empty; /// /// (文件名):不包含路径的文件名称(带扩展名) /// public string _FileName = string.Empty; internal MDataRow _Row;//MAction中的Row DataBaseType _DalType = DataBaseType.None; /// /// 表名 /// public string TableName; public NoSqlAction(ref MDataRow row, string fileName, string filePath, DataBaseType dalType) { Reset(ref row, fileName, filePath, dalType); } /// /// 切换表 /// /// 数据行结构 /// 文件名称 /// 文件路径 /// 数据类型 public void Reset(ref MDataRow row, string fileName, string filePath, DataBaseType dalType) { TableName = Path.GetFileNameWithoutExtension(fileName); string exName = Path.GetExtension(fileName); if (string.IsNullOrEmpty(exName)) { switch (dalType) { case DataBaseType.Txt: fileName = fileName + ".txt"; break; case DataBaseType.Xml: fileName = fileName + ".xml"; break; } } if (fileName != _FileName) { _insertRows.Clear();//切换表的时候重置。 Dispose();//先保存 } _Row = row; _FileName = fileName; _FileFullName = filePath + _FileName; _DalType = dalType; _Table = null; } public bool Delete(object where) { int count = 0; return Delete(where, out count); } internal bool Delete(object where, out int count) { count = -1; if (!string.IsNullOrEmpty(Convert.ToString(where))) { //lock (lockOperatorObj) // 删除条件会影响到Insert。 //{ IList rowList = Table.FindAll(where); if (rowList != null) { count = rowList.Count; if (count > 0) { bool isDeleteAll = count == Table.Rows.Count; if (isDeleteAll) { _Table.Rows.Clear();//清空竟然无效,暂时未找到原因 _insertRows.Clear(); if (!_isDeleteAll.Contains(_FileFullName)) { _isDeleteAll.Add(_FileFullName); } } else { for (int i = rowList.Count - 1; i >= 0; i--) { try { MDataRow row = rowList[i]; if (row != null) { if (Table.Rows.Contains(row)) //线程多时,有时候会重复删 { Table.Rows.Remove(row); } if (_insertRows.Count > 0 && _insertRows.Contains(row)) { _insertRows.Remove(row); } } } catch { } } } // if (isDeleteAll) { Save(); } needToSaveState = 2; return true; } } //} } return false; } public bool Insert(bool isOpenTrans) { MDataCell cell = _Row.PrimaryCell; if (IsCanDoInsertCheck((cell.IsNullOrEmpty || cell.Struct.IsAutoIncrement || cell.Struct.IsPrimaryKey) ? 1 : 0)) { //判断是否需要增加自增加id if (!cell.Struct.IsCanNull && (cell.Struct.IsAutoIncrement || cell.Struct.IsPrimaryKey)) { #region 给主键赋值 DataGroupType group = DataType.GetGroup(cell.Struct.SqlType); string existWhere = cell.ColumnName + (group == DataGroupType.Number ? "={0}" : "='{0}'"); if (cell.IsNull || cell.State == 0 || cell.StringValue == "0" || Exists(string.Format(existWhere, cell.Value)))//这里检测存在,避免id重复 { switch (group) { case DataGroupType.Number: cell.Value = NextID; break; case DataGroupType.Guid: cell.Value = Guid.NewGuid(); break; case DataGroupType.Text: cell.Value = Guid.NewGuid().ToString(); break; default: return (bool)Error.Throw("first column value can't be null"); } } if (group == DataGroupType.Number || group == DataGroupType.Guid)//再检测是否已存在 { if (!isOpenTrans && Exists(string.Format(existWhere, cell.Value)))//事务时,由于自动补id,避开检测,提升性能 { Error.Throw("first column value must be unique:(" + cell.ColumnName + ":" + cell.Value + ")"); } else if (group == DataGroupType.Number) { maxID = (int)cell.Value; } } #endregion } CheckFileChanged(true); _Row.SetState(0);//状态重置,避免重复使用插入! MDataRow newRow = Table.NewRow(true); newRow.LoadFrom(_Row); _insertRows.Add(newRow);//插入引用,内存表有数据,还没写文章! needToSaveState = needToSaveState > 1 ? 2 : 1; return true; } return false; } public bool Update(object where) { int count = 0; return Update(where, out count); } public bool Update(object where, out int count) { count = -1; CheckFileChanged(true); IList rowList = Table.FindAll(where); if (rowList != null) { count = rowList.Count; if (count > 0) { for (int i = rowList.Count - 1; i >= 0; i--) { rowList[i].LoadFrom(_Row, RowOp.Update, false); rowList[i].SetState(0);//状态重置 } _Row.SetState(0); needToSaveState = 2; return true; } } return false; } public bool Fill(object where) { CheckFileChanged(true); MDataRow row = Table.FindRow(where); if (row != null) { _Row.LoadFrom(row); _Row.SetState(0);//查询时,后续会定位状态为1 return true; } return false; } public int GetCount(object where) { CheckFileChanged(true); return Table.GetCount(where); } public bool Exists(object where) { CheckFileChanged(true); return Table.FindRow(where) != null; } public MDataTable Select(int pageIndex, int pageSize, object where, out int rowCount, params object[] selectColumns) { CheckFileChanged(true); MDataTable dt = Table.Select(pageIndex, pageSize, where, selectColumns); rowCount = dt.RecordsAffected; return dt; } #region 其它方法 private bool IsCanDoInsertCheck(int start) { bool isCanDo = false; for (int i = start; i < _Row.Count; i++) { if (_Row[i].State == 0 && !_Row[i].IsNull) { _Row[i].Value = null; } if (!_Row[i].IsNullOrEmpty) { isCanDo = true; } else if (Convert.ToString(_Row[i].Struct.DefaultValue).Length > 0) { _Row[i].SetDefaultValueToValue(); if (!_Row[i].IsNullOrEmpty) { isCanDo = true; } } else if (!_Row[i].Struct.IsCanNull) { Error.Throw("Column [" + _Row[i].ColumnName + "] 's value can't be null or empty ! (tip:column property:iscannull=false)"); } } if (!isCanDo && start == 1) // 允许插入空的主键(非自增)。 { return !_Row[0].IsNullOrEmpty && _Row[0].Struct.IsPrimaryKey && !_Row[0].Struct.IsAutoIncrement; } return isCanDo; } #endregion #region IDisposable 成员 public void Dispose() { if (string.IsNullOrEmpty(_FileFullName) || AppConfig.DB.IsTxtReadOnly) { return; } int state = needToSaveState; if (state > 0) { bool isFirstAddRow = (Table.Rows.Count - _insertRows.Count) == 0;//如果是首次新增加数据。 if (state > 1 || isFirstAddRow || _DalType == DataBaseType.Xml || _insertRows.Count == 0) { Save(); } else//文本仅有插入 { StringBuilder sb = new StringBuilder(); for (int i = 0; i < _insertRows.Count; i++) { sb.Append("," + AppConst.NewLine + _insertRows[i].ToJson(RowOp.None, false, EscapeOp.Encode)); } _insertRows.Clear();//重置 if (!Tool.IOHelper.Append(_FileFullName, sb.ToString())) { Save();//失败,则重新尝试写入! } else { needToSaveState = 0; } } } } /// /// 检测文件是否已被修改过 /// /// private void CheckFileChanged(bool isNeedToReloadTable) { if (!_isDeleteAll.Contains(_FileFullName)) { DateTime lastWriteTime = _lastWriteTimeList[_FileFullName]; if (IOHelper.IsLastFileWriteTimeChanged(_FileFullName, ref lastWriteTime)) { _lastWriteTimeList[_FileFullName] = lastWriteTime; if (isNeedToReloadTable && _tableList.ContainsKey(_FileFullName)) { try { _tableList[_FileFullName].Rows.Clear(); _tableList.Remove(_FileFullName); } catch// (Exception err) { } _Table = null;//需要重新加载数据。 } } } } private void Save() { try { string text = string.Empty; if (string.IsNullOrEmpty(text)) { text = _DalType == DataBaseType.Txt ? Table.ToJson(false, true, RowOp.None, false, EscapeOp.Encode).Replace("},{", "},\r\n{").Trim('[', ']') : Table.ToXml(); } int tryAgainCount = 3; bool isError = false; do { try { IOHelper.Write(_FileFullName, text, _DalType == DataBaseType.Txt ? IOHelper.DefaultEncoding : Encoding.UTF8); needToSaveState = 0;//重置为0 DateTime lastWriteTime = _lastWriteTimeList[_FileFullName]; if (IOHelper.IsLastFileWriteTimeChanged(_FileFullName, ref lastWriteTime)) { //更新最后文件时间 _lastWriteTimeList[_FileFullName] = lastWriteTime; } tryAgainCount = 0; if (_isDeleteAll.Contains(_FileFullName)) { _isDeleteAll.Remove(_FileFullName); } } catch { tryAgainCount--; isError = true; } if (isError) { System.Threading.Thread.Sleep(20 * (4 - tryAgainCount)); } } while (tryAgainCount > 0); } catch (Exception err) { Log.Write(err, LogType.DataBase); } } #endregion } }