feat: initial commit - HJHA game server full source
6 major server modules (PdkFriendServer/GlobalSever/ServerCore/GameModule/GameNetModule) + game logic (GameFix/GameDAL/ServerData) + network layer (NetWorkMessage) + data layer (ObjectModel) + utilities (MrWu/Core/Config/CloudAPI/dll) + adapters (zyxAdapter/base) .NET 8.0 C# solution, 16 projects, 958 source files
This commit is contained in:
686
MrWu/Basic/BaseFun.cs
Normal file
686
MrWu/Basic/BaseFun.cs
Normal file
@ -0,0 +1,686 @@
|
||||
/*
|
||||
* 作者:吴隆健
|
||||
* 日期: 2019-04-07
|
||||
* 时间: 20:05
|
||||
*
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.IO;
|
||||
using System.Data;
|
||||
|
||||
namespace MrWu.Basic
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 常用的一些方法_变量
|
||||
/// </summary>
|
||||
public static class BaseFun
|
||||
{
|
||||
|
||||
private static readonly Random _random = new Random();
|
||||
|
||||
/// <summary>
|
||||
/// 随机数
|
||||
/// </summary>
|
||||
public static Random random {
|
||||
get {
|
||||
return _random;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查用户名_检查字符串只包含 字母数字 下划线
|
||||
/// </summary>
|
||||
/// <param name="username">用户名</param>
|
||||
/// <param name="minlen">最小多长</param>
|
||||
/// <param name="maxlen">最大多长</param>
|
||||
/// <returns>true 表示通过 false 表示不通过</returns>
|
||||
public static bool CheckUserName(string username, int minlen, int maxlen)
|
||||
{
|
||||
//0-9 Ascii 48-57
|
||||
//a-z Ascii 97-122
|
||||
//A-Z Ascii 65-90
|
||||
//_ Ascii 95
|
||||
|
||||
if (username == null)
|
||||
return false;
|
||||
//[]里面表示匹配a-z A-Z 0-9 _
|
||||
//如果前面在^表示匹配上述范围之外的
|
||||
//{n,m}表示匹配到n-m次
|
||||
//return Regex.IsMatch(username, @"^[a-zA-Z0-9]{3,6}");
|
||||
|
||||
int len = username.Length;
|
||||
|
||||
if (len < minlen || len > maxlen)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (username[i] >= 97 && username[i] <= 122)
|
||||
continue;
|
||||
if (username[i] >= 65 && username[i] <= 90)
|
||||
continue;
|
||||
if (username[i] >= 48 && username[i] <= 57)
|
||||
continue;
|
||||
if (username[i] == 95)
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool CheckNickName(string nickname, int minlen, int maxlen)
|
||||
{
|
||||
if (nickname == null)
|
||||
return false;
|
||||
int len = nickname.Length;
|
||||
|
||||
if (len < minlen || len > maxlen)
|
||||
return false;
|
||||
|
||||
byte[] bts = Encoding.UTF8.GetBytes(nickname);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (bts[i] > 127 || (bts[i] >= 48 && bts[i] <= 57) || (bts[i] >= 65 && bts[i] <= 90) ||
|
||||
(bts[i] >= 97 && bts[i] <= 122))
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static string SqlStr = @"and|or|exec|execute|insert|select|delete|update|alter|create|drop|count|\*|chr|char|asc|mid|substring|master|truncate|declare|xp_cmdshell|restore|backup|net +user|net +localgroup +administrators";
|
||||
|
||||
/// <summary>
|
||||
/// Sql防注入
|
||||
/// </summary>
|
||||
/// <param name="inputString"></param>
|
||||
/// <returns>true 表示通过 fale 表示不通过</returns>
|
||||
public static bool ProcessSqlStr(string inputString)
|
||||
{
|
||||
if (string.IsNullOrEmpty(inputString))
|
||||
return true;
|
||||
|
||||
//string SqlStr =
|
||||
try
|
||||
{
|
||||
if ((inputString != null) && (inputString != String.Empty))
|
||||
{
|
||||
string str_Regex = @"\b(" + SqlStr + @")\b";
|
||||
|
||||
Regex Regex = new Regex(str_Regex, RegexOptions.IgnoreCase);
|
||||
//string s = Regex.Match(inputString).Value;
|
||||
if (true == Regex.IsMatch(inputString))
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 字符串中是否包含SQL关键字
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsContainsSqlKey(string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (input.IndexOf("'") >= 0) return false;
|
||||
|
||||
string str_Regex = @"\b(" + SqlStr + @")\b";
|
||||
Regex Regex = new Regex(str_Regex, RegexOptions.IgnoreCase);
|
||||
return Regex.IsMatch(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查字符串是否都是数字
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
public static bool CheckStringIsNum(string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
return false;
|
||||
int len = input.Length;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (input[i] < 48 || input[i] > 57)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查字符串长度
|
||||
/// </summary>
|
||||
/// <param name="input">比较的字符串</param>
|
||||
/// <param name="minLength">最小长度</param>
|
||||
/// <param name="maxLength">最大长度</param>
|
||||
/// <param name="isCharLenth">true表示比较字符数量 false表示比较字节数量</param>
|
||||
/// <param name="encod">编码</param>
|
||||
/// <returns>是否符合规则 true 表示符合</returns>
|
||||
public static bool CheckStringLength(string input, int minLength, int maxLength, bool isCharLenth, Encoding encod)
|
||||
{
|
||||
if (input == null)
|
||||
return false;
|
||||
|
||||
int len;
|
||||
if (isCharLenth)
|
||||
{
|
||||
len = input.Length;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] bts = encod.GetBytes(input);
|
||||
len = bts.Length;
|
||||
}
|
||||
return len >= minLength && len <= maxLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定字节的字符串
|
||||
/// </summary>
|
||||
/// <param name="s">字符串</param>
|
||||
/// <param name="count">保留的长度</param>
|
||||
/// <param name="encod">编码</param>
|
||||
/// <returns></returns>
|
||||
public static string GetLengthString(string s, int count, Encoding encod)
|
||||
{
|
||||
|
||||
if (string.IsNullOrEmpty(s))
|
||||
return s;
|
||||
|
||||
char[] chars = s.ToCharArray();
|
||||
|
||||
if (chars.Length < count)
|
||||
return s;
|
||||
|
||||
for (int i = count - 1; i >= 0; i--)
|
||||
{
|
||||
int strCount = encod.GetByteCount(s.ToCharArray(), 0, i + 1);//加上字符的长度
|
||||
|
||||
if (strCount <= count)
|
||||
return s.Substring(0, i + 1);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取数组段组建的新数组
|
||||
/// </summary>
|
||||
/// <param name="ast">数组段</param>
|
||||
/// <returns>新数组</returns>
|
||||
public static T[] GetNewArray<T>(this ArraySegment<T> ast)
|
||||
{
|
||||
T[] tarray = new T[ast.Count];
|
||||
Array.Copy(ast.Array, ast.Offset, tarray, 0, ast.Count);
|
||||
return tarray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取数组的一段
|
||||
/// </summary>
|
||||
/// <param name="array"></param>
|
||||
/// <param name="offset"></param>
|
||||
/// <param name="count"></param>
|
||||
/// <returns></returns>
|
||||
public static T[] GetNewArray<T>(this T[] array, int offset, int count)
|
||||
{
|
||||
T[] tarrray = new T[count];
|
||||
Array.Copy(array, offset, tarrray, 0, count);
|
||||
return tarrray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 排序
|
||||
/// </summary>
|
||||
/// <param name="array"></param>
|
||||
/// <param name="left"></param>
|
||||
/// <param name="right"></param>
|
||||
/// <param name="Asc"></param>
|
||||
public static void Sort(this int[] array, int left, int right, bool Asc = true)
|
||||
{
|
||||
for (int i = left; i < right; i++)
|
||||
{
|
||||
int index = i;
|
||||
for (int j = i + 1; j <= right; j++)
|
||||
{
|
||||
if (Asc)
|
||||
{
|
||||
if (array[index] > array[j])
|
||||
{
|
||||
index = j;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (array[index] < array[j])
|
||||
{
|
||||
index = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (index != i)
|
||||
{
|
||||
int temp = array[i];
|
||||
array[i] = array[index];
|
||||
array[index] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 快速排序
|
||||
/// </summary>
|
||||
/// <param name="array">数组</param>
|
||||
/// <param name="left"></param>
|
||||
/// <param name="right"></param>
|
||||
/// <param name="Asc"></param>
|
||||
/// <returns></returns>
|
||||
public static void QuickSort(this int[] array, int left, int right, bool Asc = true)
|
||||
{
|
||||
if (left >= right)
|
||||
return;
|
||||
int index = left + 1; //下一个要替换的值
|
||||
int i = index;
|
||||
while (i <= right)
|
||||
{
|
||||
if (Asc)
|
||||
{ //先把比第一个值大的放到第一个值前面
|
||||
if (array[left] > array[i])
|
||||
{
|
||||
if (i != index)
|
||||
{
|
||||
int temp = array[index];
|
||||
array[index] = array[i];
|
||||
array[i] = temp;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (array[left] < array[i])
|
||||
{
|
||||
if (i != index)
|
||||
{
|
||||
int temp = array[index];
|
||||
array[index] = array[i];
|
||||
array[i] = temp;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
index--;
|
||||
if (index != left)
|
||||
{
|
||||
int temp = array[index];
|
||||
array[index] = array[left];
|
||||
array[left] = temp;
|
||||
}
|
||||
|
||||
array.QuickSort(left, index - 1, Asc);
|
||||
array.QuickSort(index + 1, right, Asc);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 二分法查找
|
||||
/// </summary>
|
||||
/// <param name="array">数组</param>
|
||||
/// <param name="value">值</param>
|
||||
/// <param name="left"></param>
|
||||
/// <param name="right"></param>
|
||||
/// <returns>查找到返回其位置,没找到返回其应该在的位置的相反数-1</returns>
|
||||
public static int FindIndex(this int[] array, int value, int left = 0, int right = -1)
|
||||
{
|
||||
if (array.Length == 0)
|
||||
return -1;
|
||||
|
||||
if (right < 0)
|
||||
right = array.Length - 1;
|
||||
|
||||
while (left <= right)
|
||||
{
|
||||
int midx = (left + right) / 2;
|
||||
if (array[midx] > value)
|
||||
right = midx - 1;
|
||||
else if (array[midx] < value)
|
||||
left = midx + 1;
|
||||
else
|
||||
return midx;
|
||||
}
|
||||
|
||||
return -left - 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据概率获取是否成功
|
||||
/// </summary>
|
||||
/// <param name="value">概率 0 - 1</param>
|
||||
/// <returns></returns>
|
||||
public static bool Probability(double value)
|
||||
{
|
||||
return random.NextDouble() < value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找Jobject值
|
||||
/// </summary>
|
||||
/// <param name="jobject"></param>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="isLow"></param>
|
||||
/// <returns></returns>
|
||||
public static JToken Find(this JObject jobject, string key, bool isLow = true)
|
||||
{
|
||||
if (key == null)
|
||||
return null;
|
||||
if (!isLow)
|
||||
return jobject[key];
|
||||
var kl = key.ToLower();
|
||||
foreach (var item in jobject.Properties())
|
||||
{
|
||||
if (item.Name.ToLower() == kl)
|
||||
return item.Value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 串连Jobject键值对
|
||||
/// </summary>
|
||||
/// <param name="jobject">要串联的jobject</param>
|
||||
/// <param name="separator">元素与元素之间的分隔符</param>
|
||||
/// <returns>链接好的字符串</returns>
|
||||
public static string Join(this JObject jobject, string separator = "&")
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (var item in jobject)
|
||||
{
|
||||
sb.Append(item.Key);
|
||||
sb.Append("=");
|
||||
sb.Append(item.Value);
|
||||
sb.Append(separator);
|
||||
}
|
||||
if (sb.Length > 0)
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 串联IDictionary键值对
|
||||
/// </summary>
|
||||
/// <param name="dic">要串联的键值对</param>
|
||||
/// <param name="separator"> 元素与元素之间的分割符</param>
|
||||
/// <returns>链接好的字符串</returns>
|
||||
public static string Join(this IDictionary dic, string separator = "&")
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (var item in dic.Keys)
|
||||
{
|
||||
sb.Append(item);
|
||||
sb.Append("=");
|
||||
sb.Append(dic[item]);
|
||||
sb.Append(separator);
|
||||
}
|
||||
if (sb.Length > 0)
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取字符的长度 汉字按2字符算 英文字母1字符算
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public static int GetSeatLength(string key)
|
||||
{
|
||||
return Encoding.GetEncoding("GB2312").GetByteCount(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 键值对对齐
|
||||
/// </summary>
|
||||
/// <param name="key">key</param>
|
||||
/// <param name="value">value</param>
|
||||
/// <param name="wight">小于0 左对齐 大于0右对齐</param>
|
||||
/// <returns></returns>
|
||||
public static string Align(string key, object value, int wight)
|
||||
{
|
||||
int bytelen = GetSeatLength(key);
|
||||
int strlen = key.Length;
|
||||
int count = bytelen - strlen;
|
||||
|
||||
if (count < Math.Abs(wight))
|
||||
{
|
||||
//左对齐
|
||||
if (wight < 0)
|
||||
{
|
||||
wight += count;
|
||||
}
|
||||
else
|
||||
{//右对齐
|
||||
wight -= count;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
string format = "{0," + wight + "}{1}";
|
||||
|
||||
return string.Format(format, key, value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 发送post包裹
|
||||
/// </summary>
|
||||
/// <param name="url">请求地址</param>
|
||||
/// <param name="body">数据</param>
|
||||
/// <param name="sty"></param>
|
||||
/// <param name="result">结果</param>
|
||||
/// <param name="timeout">超时时间 默认10秒钟</param>
|
||||
/// <param name="ua">UA</param>
|
||||
/// <returns></returns>
|
||||
public static HttpWebResponse HttpPost(string url, string body, ref string result, int sty = 1, int timeout = -1,string ua = null)
|
||||
{
|
||||
var tm0 = DateTime.Now;
|
||||
|
||||
if (timeout == -1)
|
||||
timeout = 10000;
|
||||
|
||||
HttpWebResponse response = null;
|
||||
|
||||
Encoding encoding = Encoding.UTF8;
|
||||
// url=HttpUtility.UrlEncode(url,encoding);
|
||||
try
|
||||
{
|
||||
//ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);
|
||||
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
|
||||
request.Method = "POST";
|
||||
request.KeepAlive = false;
|
||||
request.Accept = "text/html, application/xhtml+xml, */*";
|
||||
if (!string.IsNullOrEmpty(ua))
|
||||
{
|
||||
request.UserAgent = ua;
|
||||
}
|
||||
switch (sty)
|
||||
{
|
||||
case 0:
|
||||
request.ContentType = "application/x-www-form-urlencoded";
|
||||
break;
|
||||
case 1:
|
||||
request.ContentType = "application/json";
|
||||
break;
|
||||
case 2:
|
||||
request.ContentType = "application/octet-stream";
|
||||
break;
|
||||
}
|
||||
request.Proxy = null;
|
||||
request.CookieContainer = new CookieContainer();//接收cookies
|
||||
if (timeout != -1)
|
||||
request.Timeout = timeout;
|
||||
request.ReadWriteTimeout = 15000;
|
||||
byte[] buffer = encoding.GetBytes(body);
|
||||
request.ContentLength = buffer.Length;
|
||||
using (Stream stream = request.GetRequestStream())
|
||||
{
|
||||
stream.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
response = (HttpWebResponse)request.GetResponse();
|
||||
|
||||
using (StreamReader reader = new StreamReader(response.GetResponseStream(), encoding))
|
||||
{
|
||||
result = reader.ReadToEnd();
|
||||
}
|
||||
request.Abort();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine($"得到数据:{e.Message}");
|
||||
return null;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// httpGet请求
|
||||
/// </summary>
|
||||
/// <param name="url">地址</param>
|
||||
/// <param name="result">结果</param>
|
||||
/// <param name="timeout">超时时间</param>
|
||||
/// <returns></returns>
|
||||
public static HttpWebResponse HttpGet(string url, ref string result, int timeout = -1)
|
||||
{
|
||||
|
||||
if (timeout == -1)
|
||||
timeout = 10000;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
HttpWebResponse response = null;
|
||||
|
||||
Encoding encoding = Encoding.UTF8;
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
|
||||
// request.KeepAlive=false;
|
||||
request.Proxy = null;
|
||||
request.Method = "GET";
|
||||
request.Accept = "text/html, application/xhtml+xml, */*";
|
||||
request.ContentType = "application/json";
|
||||
// switch(sty){
|
||||
// case 0:request.ContentType = "application/x-www-form-urlencoded";break;
|
||||
// case 1:request.ContentType = "application/json";break;
|
||||
// case 2:request.ContentType = "application/octet-stream";break;
|
||||
// }
|
||||
// request.CookieContainer = new CookieContainer();//接收cookies
|
||||
if (timeout > 0)
|
||||
request.Timeout = timeout;
|
||||
request.ReadWriteTimeout = 15000;
|
||||
|
||||
response = (HttpWebResponse)request.GetResponse();
|
||||
|
||||
using (StreamReader reader = new StreamReader(response.GetResponseStream(), encoding))
|
||||
{
|
||||
result = reader.ReadToEnd();
|
||||
}
|
||||
request.Abort();
|
||||
return response;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 数据表是否为 null 或空表
|
||||
/// </summary>
|
||||
/// <param name="table">表</param>
|
||||
/// <returns>返回结果</returns>
|
||||
public static bool DataTableIsNullOrEmpty(DataTable table)
|
||||
{
|
||||
return table == null || table.Rows == null || table.Rows.Count == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断数据集是否是 null 或者空数据集
|
||||
/// </summary>
|
||||
/// <param name="dataset"></param>
|
||||
/// <returns></returns>
|
||||
public static bool DataSetIsNullOrEmpty(DataSet dataset)
|
||||
{
|
||||
return dataset == null || dataset.Tables == null || dataset.Tables.Count == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 元素是否重复
|
||||
/// </summary>
|
||||
/// <param name="list"></param>
|
||||
/// <returns></returns>
|
||||
public static bool ItemRepeat<T>(IList<T> list)
|
||||
{
|
||||
int len = list.Count;
|
||||
IDictionary dic = new Dictionary<T, T>();
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (dic.Contains(list[i]))
|
||||
return true;
|
||||
dic.Add(list[i], list[i]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 元素是否重复
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="list"></param>
|
||||
/// <param name="thesame">比较器</param>
|
||||
/// <returns></returns>
|
||||
public static bool ItemRepeat<T>(IList<T> list, Func<T, T, bool> thesame)
|
||||
{
|
||||
int len = list.Count;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
for (int j = i + 1; j < len; j++)
|
||||
{
|
||||
if(thesame(list[i],list[j]))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
178
MrWu/Binary/BinaryConvert.cs
Normal file
178
MrWu/Binary/BinaryConvert.cs
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* 作者:吴隆健
|
||||
* 日期: 2019-04-07
|
||||
* 时间: 20:05
|
||||
*
|
||||
*/
|
||||
|
||||
#if BINARY
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace MrWu.Binary {
|
||||
/// <summary>
|
||||
/// 二进制装换类
|
||||
/// </summary>
|
||||
public static class BinaryConvert {
|
||||
|
||||
/// <summary>
|
||||
/// 结构体转 bytes
|
||||
/// </summary>
|
||||
/// <param name="structObj">结构体</param>
|
||||
/// <param name="bytes">接收体</param>
|
||||
/// <param name="index">从第几个位置开始接收</param>
|
||||
public static void StructToBytes(object structObj, byte[] bytes, int index = 0) {
|
||||
int size = Marshal.SizeOf(structObj.GetType());
|
||||
IntPtr p = Marshal.AllocHGlobal(size);
|
||||
//将结构体拷到分配好的内存空间
|
||||
Marshal.StructureToPtr(structObj, p, false);
|
||||
//从内存空间拷贝到byte 数组
|
||||
Marshal.Copy(p, bytes, index, size);
|
||||
//释放内存空间
|
||||
Marshal.FreeHGlobal(p);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 结构体转bytes
|
||||
/// </summary>
|
||||
/// <param name="structObj"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] StructToBytes(object structObj){
|
||||
int size = Marshal.SizeOf(structObj.GetType());
|
||||
byte[] bts = new byte[size];
|
||||
StructToBytes(structObj,bts);
|
||||
return bts;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// bytes转结构体
|
||||
/// </summary>
|
||||
/// <typeparam name="T">结构体类型</typeparam>
|
||||
/// <param name="bytes">数据</param>
|
||||
/// <param name="idx">从第几个位置开始转换</param>
|
||||
/// <returns>转成的结构体</returns>
|
||||
public static T BytesToStruct<T>(byte[] bytes, int idx = 0) where T : struct{
|
||||
Type type = typeof(T);
|
||||
int size = Marshal.SizeOf(type);
|
||||
//分配结构体内存空间
|
||||
IntPtr p = Marshal.AllocHGlobal(size);
|
||||
//将byte数组拷贝到分配好的内存空间
|
||||
Marshal.Copy(bytes, idx, p, size);
|
||||
|
||||
//将内存空间转换为目标结构体
|
||||
object obj = Marshal.PtrToStructure(p, type);
|
||||
//释放内存空间
|
||||
Marshal.FreeHGlobal(p);
|
||||
return (T)obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// bytes转字符串
|
||||
/// </summary>
|
||||
/// <param name="bytes">字节数</param>
|
||||
/// <param name="count"></param>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="charset">编码 默认utf8</param>
|
||||
/// <returns>字符串</returns>
|
||||
public static string BytesToString(byte[] bytes,int index = 0,int count = 0,Encoding charset = null) {
|
||||
if (charset == null)
|
||||
charset = Encoding.UTF8;
|
||||
if (count <= 0)
|
||||
count = bytes.Length;
|
||||
|
||||
return charset.GetString(bytes,index,count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 字符串转bytes
|
||||
/// </summary>
|
||||
/// <param name="chars">字符串</param>
|
||||
/// <param name="charset">编码 默认utf8</param>
|
||||
/// <returns>结果</returns>
|
||||
public static byte[] StringToBytes(string chars,Encoding charset = null) {
|
||||
if (charset == null)
|
||||
charset = Encoding.UTF8;
|
||||
|
||||
return charset.GetBytes(chars);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 字符串转bytes
|
||||
/// </summary>
|
||||
/// <param name="chars">字符串</param>
|
||||
/// <param name="bts">接收的字节</param>
|
||||
/// <param name="index">起始位置</param>
|
||||
/// <param name="charset">编码 默认utf8</param>
|
||||
/// <returns>返回byte的长度</returns>
|
||||
public static int StringToBytes(string chars,byte[] bts,int index,Encoding charset = null){
|
||||
if(charset == null)
|
||||
charset = Encoding.UTF8;
|
||||
|
||||
byte[] tmp_bts = StringToBytes(chars,charset);
|
||||
Buffer.BlockCopy(tmp_bts,0,bts,index,tmp_bts.Length);
|
||||
return tmp_bts.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// bytes 短字符串 第一个字节表示字符串长度
|
||||
/// </summary>
|
||||
/// <param name="bts">byte数组</param>
|
||||
/// <param name="charset">编码</param>
|
||||
/// <returns>字符串</returns>
|
||||
public static string BytesToShortString(byte[] bts,Encoding charset = null){
|
||||
if(bts[0] == 0)
|
||||
return string.Empty;
|
||||
|
||||
return BytesToString(bts,1,bts[0],charset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 短字符串bytes
|
||||
/// </summary>
|
||||
/// <param name="str">字符串</param>
|
||||
/// <param name="charset">编码</param>
|
||||
/// <returns></returns>
|
||||
public static byte[] ShortStringToBytes(string str,Encoding charset = null){
|
||||
if(charset == null)
|
||||
charset = Encoding.UTF8;
|
||||
|
||||
byte[] bts = StringToBytes(str,charset);
|
||||
int len = bts.Length;
|
||||
byte[] result = new byte[len+1];
|
||||
result[0] = checked((byte)len);
|
||||
Buffer.BlockCopy(bts,0,result,1,len);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 短字符串转bytes
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <param name="bts"></param>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="charset"></param>
|
||||
/// <returns></returns>
|
||||
public static int ShortStringToBytes(string str,byte[] bts,int index = 0,Encoding charset = null){
|
||||
if(charset == null)
|
||||
charset = Encoding.UTF8;
|
||||
|
||||
byte[] rs = StringToBytes(str,charset);
|
||||
int len = rs.Length;
|
||||
bts[index]=checked((byte)len);
|
||||
Buffer.BlockCopy(rs,0,bts,index+1,len);
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dephi存储使用的编码
|
||||
/// </summary>
|
||||
public static Encoding GB2312{
|
||||
get{
|
||||
return Encoding.GetEncoding("GB2312");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
250
MrWu/Configuration/Configuration.cs
Normal file
250
MrWu/Configuration/Configuration.cs
Normal file
@ -0,0 +1,250 @@
|
||||
using System;
|
||||
using System.Xml;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
|
||||
#if CONFIG
|
||||
|
||||
namespace MrWu.Configuration {
|
||||
/// <summary>
|
||||
/// 配置类
|
||||
/// </summary>
|
||||
public static class Configuration {
|
||||
|
||||
/// <summary>
|
||||
/// 设置基本数据类型字段值
|
||||
/// </summary>
|
||||
/// <param name="obj">对象</param>
|
||||
/// <param name="field">字段</param>
|
||||
/// <param name="node">节点</param>
|
||||
private static void SetBasicType(object obj, FieldInfo field, XmlNode node) {
|
||||
XmlNode element = node.SelectSingleNode(field.Name);
|
||||
if (element != null) { //有值
|
||||
field.SetValue(obj, GetValue(field.FieldType, element.InnerText));
|
||||
} else { //没值,默认或引发异常
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置基本数据类型属性值
|
||||
/// </summary>
|
||||
/// <param name="obj">对象</param>
|
||||
/// <param name="property">属性</param>
|
||||
/// <param name="node">节点</param>
|
||||
private static void SetBasicType(object obj, PropertyInfo property, XmlNode node) {
|
||||
XmlNode element = node.SelectSingleNode(property.Name);
|
||||
if (element != null) { //有值
|
||||
property.SetValue(obj, GetValue(property.PropertyType, element.InnerText), null);
|
||||
} else { //没值,默认或引发异常
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置属性值
|
||||
/// </summary>
|
||||
/// <param name="obj">对象</param>
|
||||
/// <param name="field">字段</param>
|
||||
/// <param name="node">节点</param>
|
||||
private static void SetArray(object obj, FieldInfo field, XmlNode node) {
|
||||
XmlNodeList nodelist = node.SelectNodes(field.Name);
|
||||
if (nodelist.Count > 0) {
|
||||
Type elementType = field.FieldType.GetElementType();
|
||||
Array array = Array.CreateInstance(elementType, nodelist.Count);
|
||||
for (int i = 0; i < nodelist.Count; i++) {
|
||||
if (isBasicType(elementType)) { //是基本数据类型
|
||||
array.SetValue(GetValue(elementType, nodelist[i].InnerText), i);
|
||||
} else if (elementType.IsArray) {
|
||||
throw new ArgumentException("不支持二维数组配置!");
|
||||
} else { //是类
|
||||
array.SetValue(LoadConfiguration(elementType, nodelist[i]), i);
|
||||
}
|
||||
}
|
||||
field.SetValue(obj, array);
|
||||
|
||||
} else { // 默认值或引发异常
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置数组
|
||||
/// </summary>
|
||||
/// <param name="obj">对象</param>
|
||||
/// <param name="property">属性</param>
|
||||
/// <param name="node">节点</param>
|
||||
private static void SetArray(object obj, PropertyInfo property, XmlNode node) {
|
||||
XmlNodeList nodelist = node.SelectNodes(property.Name);
|
||||
if (nodelist.Count > 0) {
|
||||
Type elementType = property.PropertyType.GetElementType();
|
||||
Array array = Array.CreateInstance(elementType, nodelist.Count);
|
||||
for (int i = 0; i < nodelist.Count; i++) {
|
||||
if (isBasicType(elementType)) { //是基本数据类型
|
||||
array.SetValue(GetValue(elementType, nodelist[i].InnerText), i);
|
||||
} else if (elementType.IsArray) {
|
||||
throw new ArgumentException("不支持二维数组配置!");
|
||||
} else { //是类
|
||||
array.SetValue(LoadConfiguration(elementType, nodelist[i]), i);
|
||||
}
|
||||
}
|
||||
property.SetValue(obj, array, null);
|
||||
|
||||
} else { // 默认值或引发异常
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置字段数据
|
||||
/// </summary>
|
||||
/// <param name="obj">对象</param>
|
||||
/// <param name="field">字段</param>
|
||||
/// <param name="node">节点</param>
|
||||
private static void SetObject(object obj, FieldInfo field, XmlNode node) {
|
||||
XmlNode element = node.SelectSingleNode(field.Name);
|
||||
if (element != null) { //有值
|
||||
field.SetValue(obj, LoadConfiguration(field.FieldType, element));
|
||||
} else { //默认值 或引发异常
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置属性数据
|
||||
/// </summary>
|
||||
/// <param name="obj">对象</param>
|
||||
/// <param name="property">属性</param>
|
||||
/// <param name="node">节点</param>
|
||||
private static void SetObject(object obj, PropertyInfo property, XmlNode node) {
|
||||
XmlNode element = node.SelectSingleNode(property.Name);
|
||||
if (element != null) { //有值
|
||||
property.SetValue(obj, LoadConfiguration(property.PropertyType, element), null);
|
||||
} else { //默认值 或引发异常
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载字段
|
||||
/// </summary>
|
||||
/// <param name="obj">对象</param>
|
||||
/// <param name="type">类型</param>
|
||||
/// <param name="node">配置节点</param>
|
||||
private static void LoadField(object obj, Type type, XmlNode node) {
|
||||
FieldInfo[] fields = type.GetFields();
|
||||
int len = fields.Length;
|
||||
int i = 0;
|
||||
try {
|
||||
for (; i < len; i++) {
|
||||
if (isBasicType(fields[i].FieldType)) {//基本数据类型
|
||||
SetBasicType(obj, fields[i], node);
|
||||
} else if (fields[i].FieldType.IsArray) { //是数组
|
||||
SetArray(obj, fields[i], node);
|
||||
} else {//是类
|
||||
SetObject(obj, fields[i], node);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Debug.Debug.Error("加载字段时引发异常:" + fields[i].Name);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载属性
|
||||
/// </summary>
|
||||
/// <param name="obj">对象</param>
|
||||
/// <param name="type">类型</param>
|
||||
/// <param name="node">配置节点</param>
|
||||
private static void LoadProperty(object obj, Type type, XmlNode node) {
|
||||
PropertyInfo[] properties = type.GetProperties();
|
||||
int len = properties.Length;
|
||||
int i = 0;
|
||||
try {
|
||||
for (; i < len; i++) {
|
||||
if (isBasicType(properties[i].PropertyType)) {//基本数据类型
|
||||
SetBasicType(obj, properties[i], node);
|
||||
} else if (properties[i].PropertyType.IsArray) { //是数组
|
||||
SetArray(obj, properties[i], node);
|
||||
} else { //是类
|
||||
SetObject(obj, properties[i], node);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Debug.Debug.Error("加载属性时出现错误:" + properties[i].Name);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载配置
|
||||
/// </summary>
|
||||
/// <param name="type">配置类型</param>
|
||||
/// <param name="node">xml节点</param>
|
||||
/// <returns></returns>
|
||||
public static object LoadConfiguration(Type type, XmlNode node) {
|
||||
object t = type.Assembly.CreateInstance(type.FullName);
|
||||
|
||||
LoadField(t, type, node);
|
||||
LoadProperty(t, type, node);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载配置
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="node"></param>
|
||||
/// <returns></returns>
|
||||
public static T LoadConfiguration<T>(XmlNode node) {
|
||||
return (T)LoadConfiguration(typeof(T), node);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 字符串转基本数据类型
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
private static object GetValue(Type type, string value) {
|
||||
switch (type.FullName) {
|
||||
case "System.String":
|
||||
return value;
|
||||
case "System.SByte":
|
||||
return Convert.ToSByte(value);
|
||||
case "System.Int16":
|
||||
return Convert.ToInt16(value);
|
||||
case "System.Byte":
|
||||
return Convert.ToByte(value);
|
||||
case "System.UInt16":
|
||||
return Convert.ToUInt16(value);
|
||||
case "System.Short":
|
||||
case "System.Int32":
|
||||
return Convert.ToInt32(value);
|
||||
case "System.UInt32":
|
||||
return Convert.ToUInt32(value);
|
||||
case "System.Int64":
|
||||
return Convert.ToInt64(value);
|
||||
case "System.UInt64":
|
||||
return Convert.ToUInt64(value);
|
||||
case "System.Boolean":
|
||||
return !(value == "0" || string.IsNullOrEmpty(value));
|
||||
default:
|
||||
throw new ArgumentException("配置错误,类型不符合!" + type.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取某类型是不是基本数据类型
|
||||
/// </summary>
|
||||
/// <param name="type">类型</param>
|
||||
/// <returns>true 表示基本数据类型 false 表示不是</returns>
|
||||
public static bool isBasicType(Type type) {
|
||||
return type.IsPrimitive || type.FullName == "System.String";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
59
MrWu/DB/DataResult.cs
Normal file
59
MrWu/DB/DataResult.cs
Normal file
@ -0,0 +1,59 @@
|
||||
/********************************
|
||||
*
|
||||
* 作者:吴隆健
|
||||
* 创建时间: 2019-06-06 16:07:29
|
||||
*
|
||||
********************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
|
||||
namespace MrWu.DB {
|
||||
/// <summary>
|
||||
/// 定时器执行得到的数据结果集
|
||||
/// </summary>
|
||||
public class DataResult {
|
||||
/// <summary>
|
||||
/// 参数
|
||||
/// </summary>
|
||||
public Dictionary<string, object> pms { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 返回的数据集
|
||||
/// </summary>
|
||||
public DataSet data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数据结果集
|
||||
/// </summary>
|
||||
public DataResult() { }
|
||||
|
||||
/// <summary>
|
||||
/// 数据结果集
|
||||
/// </summary>
|
||||
/// <param name="pms"></param>
|
||||
public DataResult(Dictionary<string, object> pms) {
|
||||
this.pms = pms;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 数据结果集
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
public DataResult(DataSet data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 数据结果集
|
||||
/// </summary>
|
||||
/// <param name="pms">存储过程参数</param>
|
||||
/// <param name="data">返回的数据</param>
|
||||
public DataResult(Dictionary<string, object> pms, DataSet data) {
|
||||
this.pms = pms;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
7
MrWu/DB/DbConst.cs
Normal file
7
MrWu/DB/DbConst.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace MrWu.DB
|
||||
{
|
||||
public static class DbConst
|
||||
{
|
||||
public const string RowsAffected = "RowsAffected";
|
||||
}
|
||||
}
|
||||
31
MrWu/DB/IConnectionPool.cs
Normal file
31
MrWu/DB/IConnectionPool.cs
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 作者:吴隆健
|
||||
* 日期: 2019-04-23
|
||||
* 时间: 13:17
|
||||
*
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using MrWu.Model;
|
||||
|
||||
namespace MrWu.DB {
|
||||
/// <summary>
|
||||
/// 连接池 T是链接类型
|
||||
/// </summary>
|
||||
public interface IConnectionPool<T> : IPool<T>{
|
||||
/// <summary>
|
||||
/// 链接字符串
|
||||
/// </summary>
|
||||
string connecStr {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 链接池
|
||||
/// </summary>
|
||||
ConcurrentQueue<T> conns {
|
||||
get;
|
||||
}
|
||||
}
|
||||
}
|
||||
45
MrWu/DB/IProdureParameter.cs
Normal file
45
MrWu/DB/IProdureParameter.cs
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 作者:吴隆健
|
||||
* 日期: 2019-04-23
|
||||
* 时间: 13:17
|
||||
*
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace MrWu.DB {
|
||||
/// <summary>
|
||||
/// 存储过程参数 T是指令类型
|
||||
/// </summary>
|
||||
public interface IProdureParameter<T> : IDisposable {
|
||||
|
||||
/// <summary>
|
||||
/// 数据库名称
|
||||
/// </summary>
|
||||
string dbName {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// sql命令
|
||||
/// </summary>
|
||||
T command {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 错误信息
|
||||
/// </summary>
|
||||
Exception exception {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
bool isException {
|
||||
get;
|
||||
}
|
||||
}
|
||||
}
|
||||
49
MrWu/DB/ISqlAloneParameter.cs
Normal file
49
MrWu/DB/ISqlAloneParameter.cs
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 作者:吴隆健
|
||||
* 日期: 2019-04-23
|
||||
* 时间: 13:21
|
||||
*
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Data;
|
||||
|
||||
namespace MrWu.DB {
|
||||
/// <summary>
|
||||
/// sql参数 T是数据类型
|
||||
/// </summary>
|
||||
public interface ISqlAloneParameter<T> {
|
||||
/// <summary>
|
||||
/// 参数名称
|
||||
/// </summary>
|
||||
string paramname {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 参数值
|
||||
/// </summary>
|
||||
object paramvalue {
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 参数类型
|
||||
/// </summary>
|
||||
T type {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 值的长度
|
||||
/// </summary>
|
||||
int valueLength {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Direction
|
||||
/// </summary>
|
||||
ParameterDirection Direction { get; set; }
|
||||
}
|
||||
}
|
||||
169
MrWu/DB/ISqlEx.cs
Normal file
169
MrWu/DB/ISqlEx.cs
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 作者:吴隆健
|
||||
* 日期: 2019-04-23
|
||||
* 时间: 13:17
|
||||
*
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MrWu.DB {
|
||||
/// <summary>
|
||||
/// sql链接 T是sql的链接类型 K是指令类型 M是参数类型
|
||||
/// </summary>
|
||||
public interface ISqlEx<T,K,M,N> {
|
||||
/// <summary>
|
||||
/// 链接池
|
||||
/// </summary>
|
||||
IConnectionPool<T> connPool {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个连接
|
||||
/// </summary>
|
||||
/// <param name="dbbase"></param>
|
||||
/// <returns></returns>
|
||||
T GetConnection(string dbbase);
|
||||
|
||||
/// <summary>
|
||||
/// 开始执行存储过程
|
||||
/// </summary>
|
||||
/// <param name="dbbase">数据库名称</param>
|
||||
/// <param name="procedure">存储过程名称</param>
|
||||
/// <returns></returns>
|
||||
IProdureParameter<K> BeginProcedure(string dbbase,string procedure);
|
||||
|
||||
/// <summary>
|
||||
/// 给存储过程添加参数
|
||||
/// </summary>
|
||||
/// <param name="spp">存储过程命令</param>
|
||||
/// <param name="paramName">参数名</param>
|
||||
/// <param name="value">参数值</param>
|
||||
/// <param name="type">参数类型</param>
|
||||
/// <param name="pd">参数种类</param>
|
||||
void AddParam(IProdureParameter<K> spp,string paramName,object value,M type,ParameterDirection pd = ParameterDirection.Input);
|
||||
|
||||
/// <summary>
|
||||
/// 提交存储过程
|
||||
/// </summary>
|
||||
/// <param name="spp">存储过程命令</param>
|
||||
/// <param name="ds">查询的结果集</param>
|
||||
/// <param name="againCount">重试次数 小于0表示使用配置值 大于等于0表示自定义</param>
|
||||
/// <returns>执行存储过程所有参数</returns>
|
||||
Dictionary<string, object> SubProcedure(IProdureParameter<K> spp, DataSet ds = null,int againCount = -1);
|
||||
|
||||
/// <summary>
|
||||
/// 执行sql语句
|
||||
/// </summary>
|
||||
/// <param name="dbbase">数据库名称</param>
|
||||
/// <param name="sql">sql语句</param>
|
||||
/// <param name="sqlparams">sql的额外参数</param>
|
||||
/// <param name="ds">查询的结果集</param>
|
||||
/// <param name="againCount">重试次数 小于0表示使用配置值 大于等于0表示自定义</param>
|
||||
/// <returns>受影响的行数</returns>
|
||||
int ExecuteSql(string dbbase,string sql,IEnumerable<N> sqlparams = null,DataSet ds = null,int againCount = -1);
|
||||
|
||||
/// <summary>
|
||||
/// 执行一个普通的查询
|
||||
/// </summary>
|
||||
/// <param name="dbbase">数据库名称</param>
|
||||
/// <param name="table">数据库表名</param>
|
||||
/// <param name="column">查询的列</param>
|
||||
/// <param name="where">查询条件</param>
|
||||
/// <param name="count">查询的行数</param>
|
||||
/// <param name="againCount">重试次数 小于0表示使用配置值 大于等于0表示自定义</param>
|
||||
/// <param name="sqlparams">sql参数</param>
|
||||
/// <returns>查询的结果集</returns>
|
||||
DataTable Select(string dbbase, string table, string[] column = null, string where = null, int count = 0,int againCount = -1, IEnumerable<N> sqlparams = null);
|
||||
|
||||
/// <summary>
|
||||
/// 执行cmd
|
||||
/// </summary>
|
||||
/// <param name="cmd">数据库指令</param>
|
||||
/// <param name="dbname">数据库名称</param>
|
||||
/// <param name="ds">结果集</param>
|
||||
/// <param name="againCount">重试次数 小于0表示使用配置值 大于等于0表示自定义</param>
|
||||
/// <returns>受影响的行数</returns>
|
||||
int Execute(K cmd,string dbname, DataSet ds = null,int againCount = -1);
|
||||
|
||||
/// <summary>
|
||||
/// 执行一个事务
|
||||
/// </summary>
|
||||
/// <param name="dbbase">数据库名称</param>
|
||||
/// <returns>存储过程指令</returns>
|
||||
IProdureParameter<K> BeginTransaction(string dbbase);
|
||||
|
||||
/// <summary>
|
||||
/// 为事务添加sql语句
|
||||
/// </summary>
|
||||
/// <param name="spp">存储过程指令</param>
|
||||
/// <param name="sql">sql语句</param>
|
||||
/// <param name="sqlparams">额外参数</param>
|
||||
int TransactionAddSql(IProdureParameter<K> spp, string sql, List<ISqlAloneParameter<M>> sqlparams = null);
|
||||
|
||||
/// <summary>
|
||||
/// 提交一个事务
|
||||
/// </summary>
|
||||
/// <param name="spp">存储过程指令</param>
|
||||
/// <returns></returns>
|
||||
bool SubTransaction(IProdureParameter<K> spp);
|
||||
|
||||
/// <summary>
|
||||
/// 回滚事务
|
||||
/// </summary>
|
||||
/// <param name="spp"></param>
|
||||
void RollbackTransaction(IProdureParameter<K> spp);
|
||||
|
||||
#region 异步方法
|
||||
|
||||
/// <summary>
|
||||
/// 异步提交存储过程
|
||||
/// </summary>
|
||||
/// <param name="spp">存储过程命令</param>
|
||||
/// <param name="ds">查询的结果集</param>
|
||||
/// <param name="againCount">重试次数 小于0表示使用配置值 大于等于0表示自定义</param>
|
||||
/// <returns>执行存储过程所有参数</returns>
|
||||
Task<Dictionary<string, object>> SubProcedureAsync(IProdureParameter<K> spp, DataSet ds = null, int againCount = -1);
|
||||
|
||||
/// <summary>
|
||||
/// 异步执行sql语句
|
||||
/// </summary>
|
||||
/// <param name="dbbase">数据库名称</param>
|
||||
/// <param name="sql">sql语句</param>
|
||||
/// <param name="sqlparams">sql的额外参数</param>
|
||||
/// <param name="ds">查询的结果集</param>
|
||||
/// <param name="againCount">重试次数 小于0表示使用配置值 大于等于0表示自定义</param>
|
||||
/// <returns>受影响的行数</returns>
|
||||
Task<int> ExecuteSqlAsync(string dbbase, string sql, IEnumerable<N> sqlparams = null, DataSet ds = null, int againCount = -1);
|
||||
|
||||
/// <summary>
|
||||
/// 异步执行一个普通的查询
|
||||
/// </summary>
|
||||
/// <param name="dbbase">数据库名称</param>
|
||||
/// <param name="table">数据库表名</param>
|
||||
/// <param name="column">查询的列</param>
|
||||
/// <param name="where">查询条件</param>
|
||||
/// <param name="count">查询的行数</param>
|
||||
/// <param name="againCount">重试次数 小于0表示使用配置值 大于等于0表示自定义</param>
|
||||
/// <param name="sqlparams">sql参数</param>
|
||||
/// <returns>查询的结果集</returns>
|
||||
Task<DataTable> SelectAsync(string dbbase, string table, string[] column = null, string where = null, int count = 0, int againCount = -1, IEnumerable<N> sqlparams = null);
|
||||
|
||||
/// <summary>
|
||||
/// 异步执行cmd
|
||||
/// </summary>
|
||||
/// <param name="cmd">数据库指令</param>
|
||||
/// <param name="dbname">数据库名称</param>
|
||||
/// <param name="ds">结果集</param>
|
||||
/// <param name="againCount">重试次数 小于0表示使用配置值 大于等于0表示自定义</param>
|
||||
/// <returns>受影响的行数</returns>
|
||||
Task<int> ExecuteAsync(K cmd, string dbname, DataSet ds = null, int againCount = -1);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
600
MrWu/DB/MMSSQL.cs
Normal file
600
MrWu/DB/MMSSQL.cs
Normal file
@ -0,0 +1,600 @@
|
||||
/*
|
||||
* 由SharpDevelop创建。
|
||||
* 用户:吴隆健
|
||||
* 日期: 2019-03-21
|
||||
* 时间: 10:07
|
||||
*
|
||||
*/
|
||||
|
||||
#if MMSQL
|
||||
|
||||
using MrWu.Model;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MrWu.DB
|
||||
{
|
||||
/// <summary>
|
||||
/// sqlServer 操作
|
||||
/// </summary>
|
||||
public class MMSSQL : SqlEx<SqlConnection, SqlCommand, SqlDbType, SqlParameter>
|
||||
{
|
||||
#region 属性与字段
|
||||
|
||||
/// <summary>
|
||||
/// 链接池
|
||||
/// </summary>
|
||||
public override IConnectionPool<SqlConnection> connPool
|
||||
{
|
||||
get => null; // _ConnPool;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string connectionString { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region CTOR
|
||||
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
/// <param name="config"></param>
|
||||
public MMSSQL(SqlConfig config)
|
||||
{
|
||||
//this._ConnPool = new ConnectionPool(config);
|
||||
this.connectionString =
|
||||
string.Format("Data Source={0},{1};user id={2};pwd={3};Max Pool Size=512;initial catalog=", config.host,
|
||||
config.port, config.username, config.pwd);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region SQL存储过程
|
||||
|
||||
/// <summary>
|
||||
/// 开始存储过程
|
||||
/// </summary>
|
||||
/// <param name="dbbase">存储过程所在的数据库</param>
|
||||
/// <param name="procedure">存储过程名</param>
|
||||
/// <returns></returns>
|
||||
public override IProdureParameter<SqlCommand> BeginProcedure(string dbbase, string procedure)
|
||||
{
|
||||
//if (_ConnPool == null)
|
||||
// throw new Exception("you dont hava init");
|
||||
|
||||
SqlCommand cmd = new SqlCommand(procedure); //, GetConnection(dbbase)
|
||||
SqlProcedureParameter result = new SqlProcedureParameter(cmd, dbbase);
|
||||
//存储过程模式
|
||||
cmd.CommandType = System.Data.CommandType.StoredProcedure;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 添加参数
|
||||
/// </summary>
|
||||
/// <param name="spp">存储过程查询键</param>
|
||||
/// <param name="paramName">参数名</param>
|
||||
/// <param name="value">参数值</param>
|
||||
/// <param name="type">参数类型</param>
|
||||
/// <param name="pd">参数种类</param>
|
||||
public override void AddParam(IProdureParameter<SqlCommand> spp, string paramName, object value, SqlDbType type,
|
||||
ParameterDirection pd = ParameterDirection.Input)
|
||||
{
|
||||
if (spp == null || spp.command == null)
|
||||
throw new Exception("param spp null");
|
||||
SqlParameter param = spp.command.Parameters.Add(paramName, type);
|
||||
param.Direction = pd;
|
||||
if (value == null)
|
||||
param.Value = DBNull.Value;
|
||||
else
|
||||
param.Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 提交存储过程
|
||||
/// </summary>
|
||||
/// <param name="spp">存储过程查询键</param>
|
||||
/// <param name="ds">如果 存储过程中有查询表操作,则需要传递DataSet实例</param>
|
||||
/// <param name="againCount">重试次数 小于0表示使用配置值 大于等于0表示自定义</param>
|
||||
/// <returns>参数值,输出参数从字典中获取</returns>
|
||||
public override Dictionary<string, object> SubProcedure(IProdureParameter<SqlCommand> spp, DataSet ds = null,
|
||||
int againCount = -1)
|
||||
{
|
||||
if (spp == null || spp.command == null)
|
||||
throw new Exception("param spp null");
|
||||
|
||||
int cnt = Execute(spp.command, spp.dbName, ds, againCount);
|
||||
|
||||
Dictionary<string, object> dic = new Dictionary<string, object>();
|
||||
var pms = spp.command.Parameters;
|
||||
int len = pms.Count;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
dic.Add(pms[i].ParameterName, pms[i].Value);
|
||||
}
|
||||
|
||||
if (!dic.ContainsKey(DbConst.RowsAffected))
|
||||
{
|
||||
dic[DbConst.RowsAffected] = cnt;
|
||||
}
|
||||
|
||||
spp.Dispose();
|
||||
return dic;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region SQL Query
|
||||
|
||||
/// <summary>
|
||||
/// 执行sql语句
|
||||
/// </summary>
|
||||
/// <param name="dbbase">数据库</param>
|
||||
/// <param name="sql">sql语句</param>
|
||||
/// <param name="sqlparams">单独设置sql语句的参数 一般用来设置二进制参数 或者过长的文字字符串</param>
|
||||
/// <param name="againCount">重试次数 小于0表示使用配置值 大于等于0表示自定义</param>
|
||||
/// <param name="ds">如果是查询 使用传入ds实例</param>
|
||||
public override int ExecuteSql(string dbbase, string sql, IEnumerable<SqlParameter> sqlparams = null,
|
||||
DataSet ds = null, int againCount = -1)
|
||||
{
|
||||
SqlCommand cmd = new SqlCommand(sql); //, GetConnection(dbbase)
|
||||
cmd.CommandType = CommandType.Text;
|
||||
|
||||
if (sqlparams != null)
|
||||
{
|
||||
//int len = sqlparams.Count();
|
||||
//for (int i = 0; i < len; i++) {
|
||||
// ISqlAloneParameter<SqlDbType> sap = sqlparams[i];
|
||||
// cmd.Parameters.Add(sap.paramname, sap.type, sap.valueLength);
|
||||
// cmd.Parameters[sap.paramname].Value = sap.paramvalue;
|
||||
//}
|
||||
foreach (var param in sqlparams)
|
||||
{
|
||||
cmd.Parameters.Add(param);
|
||||
}
|
||||
}
|
||||
|
||||
return Execute(cmd, dbbase, ds, againCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 普通查询一个表
|
||||
/// </summary>
|
||||
/// <param name="dbbase">数据库名称</param>
|
||||
/// <param name="table">表名</param>
|
||||
/// <param name="column">列名</param>
|
||||
/// <param name="where">条件</param>
|
||||
/// <param name="count">查询的数量</param>
|
||||
/// <param name="againCount">重试次数 小于0表示使用配置值 大于等于0表示自定义</param>
|
||||
/// <param name="sqlparams">sql参数</param>
|
||||
/// <returns>查询到的表</returns>
|
||||
public override DataTable Select(string dbbase, string table, string[] column = null, string where = null,
|
||||
int count = 0, int againCount = -1, IEnumerable<SqlParameter> sqlparams = null)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append("select ");
|
||||
if (count > 0)
|
||||
{
|
||||
sb.Append(string.Format("Top {0} ", count));
|
||||
}
|
||||
|
||||
if (column != null && column.Length > 0)
|
||||
{
|
||||
int len = column.Length;
|
||||
for (int i = len - 1; i >= 0; i--)
|
||||
{
|
||||
sb.Append(column[i]);
|
||||
if (i > 0)
|
||||
sb.Append(",");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append("*");
|
||||
}
|
||||
|
||||
sb.Append(" from ");
|
||||
sb.Append(table);
|
||||
if (!string.IsNullOrEmpty(where))
|
||||
{
|
||||
sb.Append(" where ");
|
||||
sb.Append(where);
|
||||
}
|
||||
|
||||
DataSet ds = new DataSet();
|
||||
|
||||
//Debug.Debug.Info("sql:" + sb.ToString());
|
||||
ExecuteSql(dbbase, sb.ToString(), sqlparams, ds, againCount);
|
||||
if (ds.Tables == null || ds.Tables.Count == 0)
|
||||
return null;
|
||||
return ds.Tables[0];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region SQL 事务
|
||||
|
||||
/// <summary>
|
||||
/// 开始一个事务
|
||||
/// </summary>
|
||||
/// <param name="dbbase">数据库</param>
|
||||
/// <returns></returns>
|
||||
public override IProdureParameter<SqlCommand> BeginTransaction(string dbbase)
|
||||
{
|
||||
SqlCommand cmd = new SqlCommand();
|
||||
SqlProcedureParameter spp = new SqlProcedureParameter(cmd, dbbase);
|
||||
try
|
||||
{
|
||||
var conn = GetConnection(dbbase);
|
||||
{
|
||||
conn.Open();
|
||||
cmd.Connection = conn;
|
||||
cmd.Connection.ChangeDatabase(dbbase);
|
||||
cmd.Transaction = cmd.Connection.BeginTransaction(); //开始事务,还可以设置隔离等级
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
spp.exception = e;
|
||||
if (cmd.Connection != null)
|
||||
{
|
||||
cmd.Connection.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
return spp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 事务添加sql语句
|
||||
/// </summary>
|
||||
/// <param name="spp"></param>
|
||||
/// <param name="sql"></param>
|
||||
/// <param name="sqlparams"></param>
|
||||
public override int TransactionAddSql(IProdureParameter<SqlCommand> spp, string sql,
|
||||
List<ISqlAloneParameter<SqlDbType>> sqlparams = null)
|
||||
{
|
||||
if (spp.isException)
|
||||
return 0;
|
||||
try
|
||||
{
|
||||
SqlCommand cmd = spp.command;
|
||||
cmd.CommandText = sql;
|
||||
|
||||
cmd.Parameters.Clear();
|
||||
if (sqlparams != null)
|
||||
{
|
||||
int len = sqlparams.Count;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
ISqlAloneParameter<SqlDbType> sap = sqlparams[i];
|
||||
if (sap.valueLength > 0)
|
||||
cmd.Parameters.Add(sap.paramname, sap.type, sap.valueLength);
|
||||
else
|
||||
cmd.Parameters.Add(sap.paramname, sap.type);
|
||||
cmd.Parameters[sap.paramname].Value = sap.paramvalue;
|
||||
cmd.Parameters[sap.paramname].Direction = sap.Direction;
|
||||
}
|
||||
}
|
||||
|
||||
var result = cmd.ExecuteNonQuery();
|
||||
if (sqlparams != null)
|
||||
{
|
||||
for (int i = 0; i < sqlparams.Count; i++)
|
||||
{
|
||||
sqlparams[i].paramvalue = cmd.Parameters[i].Value;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
spp.command.Transaction.Rollback(); //事务步骤异常,主动回滚事务
|
||||
spp.exception = e;
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 提交事务
|
||||
/// </summary>
|
||||
/// <param name="spp"></param>
|
||||
/// <returns>true 表示提交成功 false 表示提交失败</returns>
|
||||
public override bool SubTransaction(IProdureParameter<SqlCommand> spp)
|
||||
{
|
||||
using (spp)
|
||||
{
|
||||
using (spp.command)
|
||||
{
|
||||
using (spp.command.Connection)
|
||||
{
|
||||
using (spp.command.Transaction)
|
||||
{
|
||||
try
|
||||
{
|
||||
spp.command.Transaction.Commit();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
spp.exception = e;
|
||||
spp.command.Transaction.Rollback();
|
||||
}
|
||||
|
||||
return spp.exception == null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回滚事务
|
||||
/// </summary>
|
||||
/// <param name="spp"></param>
|
||||
public override void RollbackTransaction(IProdureParameter<SqlCommand> spp)
|
||||
{
|
||||
try
|
||||
{
|
||||
spp.command.Transaction.Rollback(); //事务步骤异常,主动回滚事务
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
spp.command.Transaction?.Dispose();
|
||||
spp.command.Connection?.Dispose();
|
||||
spp.command.Dispose();
|
||||
spp.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region 异步方法
|
||||
|
||||
/// <summary>
|
||||
/// 异步提交存储过程
|
||||
/// </summary>
|
||||
public override async Task<Dictionary<string, object>> SubProcedureAsync(IProdureParameter<SqlCommand> spp,
|
||||
DataSet ds = null, int againCount = -1)
|
||||
{
|
||||
if (spp == null || spp.command == null)
|
||||
throw new Exception("param spp null");
|
||||
|
||||
int cnt = await ExecuteAsync(spp.command, spp.dbName, ds, againCount);
|
||||
|
||||
Dictionary<string, object> dic = new Dictionary<string, object>();
|
||||
var pms = spp.command.Parameters;
|
||||
int len = pms.Count;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
dic.Add(pms[i].ParameterName, pms[i].Value);
|
||||
}
|
||||
|
||||
if (!dic.ContainsKey(DbConst.RowsAffected))
|
||||
{
|
||||
dic[DbConst.RowsAffected] = cnt;
|
||||
}
|
||||
|
||||
spp.Dispose();
|
||||
return dic;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步执行sql语句
|
||||
/// </summary>
|
||||
public override async Task<int> ExecuteSqlAsync(string dbbase, string sql,
|
||||
IEnumerable<SqlParameter> sqlparams = null, DataSet ds = null, int againCount = -1)
|
||||
{
|
||||
SqlCommand cmd = new SqlCommand(sql);
|
||||
cmd.CommandType = CommandType.Text;
|
||||
|
||||
if (sqlparams != null)
|
||||
{
|
||||
foreach (var param in sqlparams)
|
||||
{
|
||||
cmd.Parameters.Add(param);
|
||||
}
|
||||
}
|
||||
|
||||
return await ExecuteAsync(cmd, dbbase, ds, againCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步查询
|
||||
/// </summary>
|
||||
public override async Task<DataTable> SelectAsync(string dbbase, string table, string[] column = null,
|
||||
string where = null, int count = 0, int againCount = -1, IEnumerable<SqlParameter> sqlparams = null)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append("select ");
|
||||
if (count > 0)
|
||||
{
|
||||
sb.Append(string.Format("Top {0} ", count));
|
||||
}
|
||||
|
||||
if (column != null && column.Length > 0)
|
||||
{
|
||||
int len = column.Length;
|
||||
for (int i = len - 1; i >= 0; i--)
|
||||
{
|
||||
sb.Append(column[i]);
|
||||
if (i > 0)
|
||||
sb.Append(",");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append("*");
|
||||
}
|
||||
|
||||
sb.Append(" from ");
|
||||
sb.Append(table);
|
||||
if (!string.IsNullOrEmpty(where))
|
||||
{
|
||||
sb.Append(" where ");
|
||||
sb.Append(where);
|
||||
}
|
||||
|
||||
DataSet ds = new DataSet();
|
||||
|
||||
await ExecuteSqlAsync(dbbase, sb.ToString(), sqlparams, ds, againCount);
|
||||
if (ds.Tables == null || ds.Tables.Count == 0)
|
||||
return null;
|
||||
return ds.Tables[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步执行cmd
|
||||
/// </summary>
|
||||
public override async Task<int> ExecuteAsync(SqlCommand cmd, string dbname, DataSet ds = null,
|
||||
int againCount = -1)
|
||||
{
|
||||
int result = 0;
|
||||
int exceCnt = 0;
|
||||
if (againCount < 0)
|
||||
againCount = errorTryAgainCount;
|
||||
|
||||
while (exceCnt <= againCount)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (cmd)
|
||||
{
|
||||
using (var conn = GetConnection(dbname))
|
||||
{
|
||||
await conn.OpenAsync();
|
||||
cmd.Connection = conn;
|
||||
cmd.Connection.ChangeDatabase(dbname);
|
||||
if (ds == null)
|
||||
{
|
||||
result = await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var sda = new SqlDataAdapter())
|
||||
{
|
||||
sda.SelectCommand = cmd;
|
||||
sda.Fill(ds);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (exceCnt < againCount)
|
||||
{
|
||||
Debug.Debug.Warning("数据库异步执行错误重试次数:" + exceCnt + ",错误类型:" + e.Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
int time = errorAgainTime + errorAgainTimeInc * exceCnt;
|
||||
exceCnt++;
|
||||
await Task.Delay(time);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region SQL Common
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个连接
|
||||
/// </summary>
|
||||
/// <param name="dbbase"></param>
|
||||
/// <returns></returns>
|
||||
public override SqlConnection GetConnection(string dbbase) => new SqlConnection(connectionString + dbbase);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 执行sql命令
|
||||
/// </summary>
|
||||
/// <param name="cmd"></param>
|
||||
/// <param name="dbname">数据库名称</param>
|
||||
/// <param name="ds"></param>
|
||||
/// <param name="againCount">重试次数 小于0表示使用配置值 大于等于0表示自定义</param>
|
||||
public override int Execute(SqlCommand cmd, string dbname, DataSet ds = null, int againCount = -1)
|
||||
{
|
||||
int result = 0;
|
||||
int exceCnt = 0;
|
||||
if (againCount < 0)
|
||||
againCount = errorTryAgainCount;
|
||||
|
||||
while (exceCnt <= againCount)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (cmd)
|
||||
{
|
||||
// Debug.Debug.Info("链接字符串:" + connectionString);
|
||||
using (var conn = GetConnection(dbname))
|
||||
{
|
||||
conn.Open();
|
||||
cmd.Connection = conn;
|
||||
cmd.Connection.ChangeDatabase(dbname);
|
||||
if (ds == null)
|
||||
{
|
||||
result = cmd.ExecuteNonQuery();
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var sda = new SqlDataAdapter())
|
||||
{
|
||||
//一定要关闭,不然下次执行报错;
|
||||
sda.SelectCommand = cmd;
|
||||
sda.Fill(ds); //调用会自动执行 ExecuteNonQuery(); 不要重复调用,很容易出错!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (exceCnt < againCount)
|
||||
{
|
||||
Debug.Debug.Warning("数据库执行错误重试次数:" + exceCnt + ",错误类型:" + e.Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
int time = errorAgainTime + errorAgainTimeInc * exceCnt;
|
||||
exceCnt++; //执行次数+1
|
||||
Thread.Sleep(time);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
27
MrWu/DB/MethodType.cs
Normal file
27
MrWu/DB/MethodType.cs
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 作者:吴隆健
|
||||
* 日期: 2019-04-07
|
||||
* 时间: 20:05
|
||||
*
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace MrWu.DB
|
||||
{
|
||||
/// <summary>
|
||||
/// 方法类型
|
||||
/// </summary>
|
||||
public enum MethodType
|
||||
{
|
||||
/// <summary>
|
||||
/// 过程
|
||||
/// </summary>
|
||||
Procedure = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 函数
|
||||
/// </summary>
|
||||
Function = 1,
|
||||
}
|
||||
}
|
||||
691
MrWu/DB/MySQL.cs
Normal file
691
MrWu/DB/MySQL.cs
Normal file
@ -0,0 +1,691 @@
|
||||
#if MySQL
|
||||
|
||||
using System;
|
||||
using System.Data;
|
||||
using MySql.Data.MySqlClient;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading.Tasks;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using MrWu.Model;
|
||||
using System.Threading;
|
||||
using MrWu.Debug;
|
||||
using System.Data.SqlClient;
|
||||
|
||||
namespace MrWu.DB {
|
||||
/// <summary>
|
||||
/// mysql操作
|
||||
/// </summary>
|
||||
public class MySQL : SqlEx<MySqlConnection, MySqlCommand, MySqlDbType,MySqlParameter> {
|
||||
/// <summary>
|
||||
/// 链接池
|
||||
/// </summary>
|
||||
private class ConnectionPool : IConnectionPool<MySqlConnection> {
|
||||
/// <summary>
|
||||
/// 链接字符串
|
||||
/// </summary>
|
||||
private readonly string _connstr;
|
||||
|
||||
string IConnectionPool<MySqlConnection>.connecStr => _connstr;
|
||||
|
||||
private readonly int _capsize;
|
||||
/// <summary>
|
||||
/// 最大的缓存链接数量
|
||||
/// </summary>
|
||||
int IPool<MySqlConnection>.capSize => _capsize;
|
||||
|
||||
private readonly int _surviveCountSize;
|
||||
|
||||
/// <summary>
|
||||
/// 同时存在最大的链接数量
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
int IPool<MySqlConnection>.surviveCountSize => _surviveCountSize;
|
||||
|
||||
/// <summary>
|
||||
/// 当前池中的数量
|
||||
/// </summary>
|
||||
int IPool<MySqlConnection>.capCount => _conns.Count;
|
||||
|
||||
long _surviveCount = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 当前存活数量
|
||||
/// </summary>
|
||||
int IPool<MySqlConnection>.surviveCount {
|
||||
get {
|
||||
return (int)Interlocked.Read(ref _surviveCount);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 所有的链接
|
||||
/// </summary>
|
||||
private readonly ConcurrentQueue<MySqlConnection> _conns = new ConcurrentQueue<MySqlConnection>();
|
||||
|
||||
ConcurrentQueue<MySqlConnection> IConnectionPool<MySqlConnection>.conns => _conns;
|
||||
|
||||
/// <summary>
|
||||
/// 用于异步等待连接可用的信号量
|
||||
/// </summary>
|
||||
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1);
|
||||
|
||||
public ConnectionPool(SqlConfig config) {
|
||||
this._capsize = config.cacheCount;
|
||||
this._surviveCountSize = config.surviveCountSize;
|
||||
this._connstr = string.Format("server={0};port={1};user={2};password={3};AllowUserVariables={4};pooling=true;database=",
|
||||
config.host, config.port, config.username, config.pwd, config.AllowUserVariables);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个连接
|
||||
/// </summary>
|
||||
/// <param name="dbbase"></param>
|
||||
/// <returns></returns>
|
||||
public MySqlConnection TakeOut(params object[] dbbase) {
|
||||
if (dbbase.Length <= 0) return null;
|
||||
string dbname = dbbase[0] as string;
|
||||
if (dbname == null) return null;
|
||||
MySqlConnection conn;
|
||||
if (_conns.TryDequeue(out conn)) {
|
||||
if (conn != null) {
|
||||
return conn;
|
||||
}
|
||||
}
|
||||
if (this._surviveCountSize > 0 && Interlocked.Read(ref _surviveCount) >= this._surviveCountSize)
|
||||
return null;
|
||||
Interlocked.Increment(ref _surviveCount);
|
||||
return new MySqlConnection(this._connstr + dbname);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取一个连接(连接池满时不会阻塞线程)
|
||||
/// </summary>
|
||||
public async Task<MySqlConnection> TakeOutAsync(string dbname) {
|
||||
if (dbname == null) return null;
|
||||
while (true) {
|
||||
MySqlConnection conn;
|
||||
if (_conns.TryDequeue(out conn)) {
|
||||
if (conn != null) return conn;
|
||||
}
|
||||
if (this._surviveCountSize == 0 || Interlocked.Read(ref _surviveCount) < this._surviveCountSize) {
|
||||
Interlocked.Increment(ref _surviveCount);
|
||||
return new MySqlConnection(this._connstr + dbname);
|
||||
}
|
||||
// 连接池满,异步等待
|
||||
await _semaphore.WaitAsync();
|
||||
_semaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 放入一个连接
|
||||
/// </summary>
|
||||
/// <param name="conn"></param>
|
||||
public void PutInto(MySqlConnection conn) {
|
||||
if (_capsize == 0 || _conns.Count < _capsize) {
|
||||
_conns.Enqueue(conn);
|
||||
} else {
|
||||
conn.Dispose();
|
||||
Interlocked.Decrement(ref _surviveCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 1.执行sql语句 获得执行sql语句后的结果
|
||||
* 2.执行存储过程 获得存储过程的结果
|
||||
*
|
||||
* 每个库都有一个链接
|
||||
*
|
||||
* */
|
||||
|
||||
private ConnectionPool _ConnPool = null;
|
||||
|
||||
/// <summary>
|
||||
/// 链接池
|
||||
/// </summary>
|
||||
public override IConnectionPool<MySqlConnection> connPool => _ConnPool;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
/// <param name="config"></param>
|
||||
public MySQL(SqlConfig config) {
|
||||
this._ConnPool = new ConnectionPool(config);
|
||||
errorTryAgainCount = config.errorTryAgainCount;
|
||||
errorAgainTimeInc = config.errorAgainTimeInc;
|
||||
errorAgainTime = config.errorAgainTime;
|
||||
|
||||
if (errorTryAgainCount < 0)
|
||||
throw new ArgumentException("errorTryAgainCount 不能小于 0");
|
||||
if (errorAgainTimeInc < 0)
|
||||
throw new ArgumentException("errorAgainTimeInc 不能小于 0");
|
||||
if (errorAgainTime < 0)
|
||||
throw new ArgumentException("errorAgainTime 不能小于 0");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个连接
|
||||
/// </summary>
|
||||
/// <param name="dbbase"></param>
|
||||
/// <returns></returns>
|
||||
public override MySqlConnection GetConnection(string dbbase) {
|
||||
MySqlConnection tmp;
|
||||
while (true) {
|
||||
tmp = _ConnPool.TakeOut(dbbase);
|
||||
if (tmp != null)
|
||||
return tmp;
|
||||
Debug.Debug.Warning("链接数超过上限,等待重试");
|
||||
Thread.Sleep(2);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取一个连接(不会阻塞线程)
|
||||
/// </summary>
|
||||
public async Task<MySqlConnection> GetConnectionAsync(string dbbase) {
|
||||
return await _ConnPool.TakeOutAsync(dbbase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始存储过程
|
||||
/// </summary>
|
||||
/// <param name="dbbase">存储过程所在的数据库</param>
|
||||
/// <param name="procedure">存储过程名</param>
|
||||
/// <returns></returns>
|
||||
public override IProdureParameter<MySqlCommand> BeginProcedure(string dbbase, string procedure) {
|
||||
if (_ConnPool == null)
|
||||
throw new Exception("you dont hava init");
|
||||
|
||||
MySqlCommand cmd = new MySqlCommand(procedure, GetConnection(dbbase));
|
||||
MySqlProcedureParameter result = new MySqlProcedureParameter(cmd, dbbase);
|
||||
//存储过程模式
|
||||
cmd.CommandType = CommandType.StoredProcedure;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加参数
|
||||
/// </summary>
|
||||
/// <param name="spp">存储过程查询键</param>
|
||||
/// <param name="paramName">参数名</param>
|
||||
/// <param name="value">参数值</param>
|
||||
/// <param name="type">参数类型</param>
|
||||
/// <param name="pd">参数种类</param>
|
||||
public override void AddParam(IProdureParameter<MySqlCommand> spp, string paramName, object value, MySqlDbType type, ParameterDirection pd = ParameterDirection.Input) {
|
||||
if (spp == null || spp.command == null)
|
||||
throw new Exception("param spp null");
|
||||
MySqlParameter param = spp.command.Parameters.Add(paramName, type);
|
||||
param.Direction = pd;
|
||||
if (value == null)
|
||||
param.Value = DBNull.Value;
|
||||
else
|
||||
param.Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 提交存储过程
|
||||
/// </summary>
|
||||
/// <param name="spp">存储过程查询键</param>
|
||||
/// <param name="ds">如果 存储过程中有查询表操作,则需要传递DataSet实例</param>
|
||||
/// <param name="againCount">重试次数 小于0表示使用配置值 大于等于0表示自定义</param>
|
||||
/// <returns>参数值,输出参数从字典中获取</returns>
|
||||
public override Dictionary<string, object> SubProcedure(IProdureParameter<MySqlCommand> spp, DataSet ds = null, int againCount = -1) {
|
||||
if (spp == null || spp.command == null)
|
||||
throw new Exception("param spp null");
|
||||
|
||||
int cnt = Execute(spp.command, spp.dbName, ds, againCount);
|
||||
|
||||
Dictionary<string, object> dic = new Dictionary<string, object>();
|
||||
var pms = spp.command.Parameters;
|
||||
int len = pms.Count;
|
||||
for (int i = 0; i < len; i++) {
|
||||
dic.Add(pms[i].ParameterName, pms[i].Value);
|
||||
}
|
||||
|
||||
if (!dic.ContainsKey(DbConst.RowsAffected))
|
||||
{
|
||||
dic[DbConst.RowsAffected] = cnt;
|
||||
}
|
||||
|
||||
spp.Dispose();
|
||||
return dic;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行sql语句
|
||||
/// </summary>
|
||||
/// <param name="dbbase">数据库</param>
|
||||
/// <param name="sql">sql语句</param>
|
||||
/// <param name="mysqlparams"></param>
|
||||
/// <param name="againCount">重试次数 小于0表示使用配置值 大于等于0表示自定义</param>
|
||||
/// <param name="ds">如果是查询 使用传入ds实例</param>
|
||||
public override int ExecuteSql(string dbbase, string sql, IEnumerable<MySqlParameter> mysqlparams = null, DataSet ds = null, int againCount = -1) {
|
||||
MySqlCommand cmd = new MySqlCommand(sql, GetConnection(dbbase));
|
||||
cmd.CommandType = CommandType.Text;
|
||||
|
||||
if (mysqlparams != null) {
|
||||
//int len = mysqlparams.Count;
|
||||
//for (int i = 0; i < len; i++) {
|
||||
// ISqlAloneParameter<MySqlDbType> msap = mysqlparams[i];
|
||||
// cmd.Parameters.Add(msap.paramname, msap.type, msap.valueLength);
|
||||
// cmd.Parameters[msap.paramname].Value = msap.paramvalue;
|
||||
//}
|
||||
foreach (var param in mysqlparams) {
|
||||
cmd.Parameters.Add(param);
|
||||
}
|
||||
}
|
||||
|
||||
return Execute(cmd, dbbase, ds, againCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行sql语句
|
||||
/// </summary>
|
||||
/// <param name="dbbase">数据库</param>
|
||||
/// <param name="sql">sql语句</param>
|
||||
/// <param name="mysqlparams">附加参数</param>
|
||||
/// <returns></returns>
|
||||
public MySqlCommand GetCommandBySql(string dbbase, string sql, IEnumerable<MySqlParameter> mysqlparams = null) {
|
||||
MySqlCommand cmd = new MySqlCommand(sql, GetConnection(dbbase));
|
||||
cmd.CommandType = CommandType.Text;
|
||||
|
||||
if (mysqlparams != null) {
|
||||
//int len = mysqlparams.Count;
|
||||
//for (int i = 0; i < len; i++) {
|
||||
// ISqlAloneParameter<MySqlDbType> msap = mysqlparams[i];
|
||||
// cmd.Parameters.Add(msap.paramname, msap.type, msap.valueLength);
|
||||
// cmd.Parameters[msap.paramname].Value = msap.paramvalue;
|
||||
//}
|
||||
foreach (var param in mysqlparams) {
|
||||
cmd.Parameters.Add(param);
|
||||
}
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 普通查询一个表
|
||||
/// </summary>
|
||||
/// <param name="dbbase">数据库名称</param>
|
||||
/// <param name="table">表名</param>
|
||||
/// <param name="column">列名</param>
|
||||
/// <param name="where">条件</param>
|
||||
/// <param name="count">查询的数量</param>
|
||||
/// <param name="againCount">重试次数 小于0表示使用配置值 大于等于0表示自定义</param>
|
||||
/// <param name="sqlparams">sqlparams</param>
|
||||
/// <returns>查询到的表</returns>
|
||||
public override DataTable Select(string dbbase, string table, string[] column = null, string where = null, int count = 0, int againCount = -1, IEnumerable<MySqlParameter> sqlparams = null) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append("select ");
|
||||
if (column != null && column.Length > 0) {
|
||||
int len = column.Length;
|
||||
for (int i = len - 1; i >= 0; i--) {
|
||||
sb.Append(column[i]);
|
||||
if (i > 0)
|
||||
sb.Append(",");
|
||||
}
|
||||
} else {
|
||||
sb.Append("*");
|
||||
}
|
||||
|
||||
sb.Append(" from ");
|
||||
sb.Append(table);
|
||||
if (!string.IsNullOrEmpty(where)) {
|
||||
sb.Append(" where ");
|
||||
sb.Append(where);
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
sb.AppendFormat(" limit {0}", count);
|
||||
}
|
||||
|
||||
DataSet ds = new DataSet();
|
||||
|
||||
ExecuteSql(dbbase, sb.ToString(), sqlparams, ds, againCount);
|
||||
if (ds.Tables == null || ds.Tables.Count == 0)
|
||||
return null;
|
||||
return ds.Tables[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行sql命令
|
||||
/// </summary>
|
||||
/// <param name="cmd">指令</param>
|
||||
/// <param name="dbname">数据库名称</param>
|
||||
/// <param name="ds">null表示不需要查询结果集 有值将会把结果集放到 ds中</param>
|
||||
/// <param name="againCount">重试次数 小于0表示使用配置值 大于等于0表示自定义</param>
|
||||
public override int Execute(MySqlCommand cmd, string dbname, DataSet ds = null, int againCount = -1) {
|
||||
|
||||
// Debug.Debug.Log("执行sql指令!");
|
||||
|
||||
int result = 0;
|
||||
int exceCnt = 0; //执行次数
|
||||
//重试次数
|
||||
if (againCount < 0)
|
||||
againCount = errorTryAgainCount;
|
||||
try {
|
||||
while (exceCnt <= againCount) {
|
||||
try {
|
||||
if (cmd.Connection.State == ConnectionState.Closed)
|
||||
cmd.Connection.Open();
|
||||
cmd.Connection.ChangeDatabase(dbname);
|
||||
|
||||
if (ds == null)
|
||||
result = cmd.ExecuteNonQuery();
|
||||
else {
|
||||
using(MySqlDataAdapter sda = new MySqlDataAdapter()) {
|
||||
sda.SelectCommand = cmd;
|
||||
sda.Fill(ds);
|
||||
}
|
||||
}
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
cmd.Connection.Close();
|
||||
if (exceCnt < againCount)
|
||||
Debug.Debug.Error("数据库执行错误重试次数:" + exceCnt + ",错误类型:" + e.Message);
|
||||
else
|
||||
throw e;
|
||||
}
|
||||
|
||||
int time = errorAgainTime + errorAgainTimeInc * exceCnt;
|
||||
exceCnt++; //执行次数+1
|
||||
Thread.Sleep(time);
|
||||
}
|
||||
} finally {
|
||||
_ConnPool.PutInto(cmd.Connection); //放入链接
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始一个事务
|
||||
/// </summary>
|
||||
/// <param name="dbbase">数据库</param>
|
||||
/// <returns></returns>
|
||||
public override IProdureParameter<MySqlCommand> BeginTransaction(string dbbase) {
|
||||
MySqlCommand cmd = new MySqlCommand();
|
||||
MySqlProcedureParameter spp = new MySqlProcedureParameter(cmd, dbbase);
|
||||
try {
|
||||
cmd.Connection = GetConnection(dbbase);
|
||||
if (cmd.Connection.State == ConnectionState.Closed)
|
||||
cmd.Connection.Open();
|
||||
cmd.Connection.ChangeDatabase(dbbase);
|
||||
cmd.Transaction = cmd.Connection.BeginTransaction(); //开始事务,还可以设置隔离等级
|
||||
} catch (Exception e) {
|
||||
spp.exception = e;
|
||||
}
|
||||
return spp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 事务添加sql语句
|
||||
/// </summary>
|
||||
/// <param name="spp"></param>
|
||||
/// <param name="sql"></param>
|
||||
/// <param name="sqlparams"></param>
|
||||
public override int TransactionAddSql(IProdureParameter<MySqlCommand> spp, string sql, List<ISqlAloneParameter<MySqlDbType>> sqlparams = null) {
|
||||
if (spp.isException)
|
||||
return 0;
|
||||
try {
|
||||
MySqlCommand cmd = spp.command;
|
||||
cmd.CommandText = sql;
|
||||
cmd.Parameters.Clear();
|
||||
|
||||
if (sqlparams != null) {
|
||||
int len = sqlparams.Count;
|
||||
for (int i = 0; i < len; i++) {
|
||||
ISqlAloneParameter<MySqlDbType> sap = sqlparams[i];
|
||||
cmd.Parameters.Add(sap.paramname, sap.type, sap.valueLength);
|
||||
cmd.Parameters[sap.paramname].Value = sap.paramvalue;
|
||||
}
|
||||
}
|
||||
|
||||
return cmd.ExecuteNonQuery();
|
||||
} catch (Exception e) {
|
||||
spp.exception = e;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 提交事务
|
||||
/// </summary>
|
||||
/// <param name="spp"></param>
|
||||
/// <returns>true 表示提交成功 false 表示提交失败</returns>
|
||||
public override bool SubTransaction(IProdureParameter<MySqlCommand> spp) {
|
||||
try {
|
||||
spp.command.Transaction.Commit();
|
||||
} catch (Exception e) {
|
||||
spp.exception = e;
|
||||
spp.command.Transaction.Rollback();
|
||||
} finally {
|
||||
_ConnPool.PutInto(spp.command.Connection);
|
||||
spp.Dispose();
|
||||
}
|
||||
return spp.exception == null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回滚事务
|
||||
/// </summary>
|
||||
/// <param name="spp"></param>
|
||||
public override void RollbackTransaction(IProdureParameter<MySqlCommand> spp) {
|
||||
try {
|
||||
spp.command.Transaction.Rollback();
|
||||
} catch (Exception ex) {
|
||||
Debug.Debug.Error("RollbackTransaction ex: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
#region 异步方法
|
||||
|
||||
/// <summary>
|
||||
/// 异步提交存储过程
|
||||
/// </summary>
|
||||
public override async Task<Dictionary<string, object>> SubProcedureAsync(IProdureParameter<MySqlCommand> spp, DataSet ds = null, int againCount = -1) {
|
||||
if (spp == null || spp.command == null)
|
||||
throw new Exception("param spp null");
|
||||
|
||||
int cnt = await ExecuteAsync(spp.command, spp.dbName, ds, againCount);
|
||||
|
||||
Dictionary<string, object> dic = new Dictionary<string, object>();
|
||||
var pms = spp.command.Parameters;
|
||||
int len = pms.Count;
|
||||
for (int i = 0; i < len; i++) {
|
||||
dic.Add(pms[i].ParameterName, pms[i].Value);
|
||||
}
|
||||
|
||||
if (!dic.ContainsKey(DbConst.RowsAffected)) {
|
||||
dic[DbConst.RowsAffected] = cnt;
|
||||
}
|
||||
|
||||
spp.Dispose();
|
||||
return dic;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步执行sql语句
|
||||
/// </summary>
|
||||
public override async Task<int> ExecuteSqlAsync(string dbbase, string sql, IEnumerable<MySqlParameter> mysqlparams = null, DataSet ds = null, int againCount = -1) {
|
||||
MySqlCommand cmd = new MySqlCommand(sql, await GetConnectionAsync(dbbase));
|
||||
cmd.CommandType = CommandType.Text;
|
||||
|
||||
if (mysqlparams != null) {
|
||||
foreach (var param in mysqlparams) {
|
||||
cmd.Parameters.Add(param);
|
||||
}
|
||||
}
|
||||
|
||||
return await ExecuteAsync(cmd, dbbase, ds, againCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步查询
|
||||
/// </summary>
|
||||
public override async Task<DataTable> SelectAsync(string dbbase, string table, string[] column = null, string where = null, int count = 0, int againCount = -1, IEnumerable<MySqlParameter> sqlparams = null) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append("select ");
|
||||
if (column != null && column.Length > 0) {
|
||||
int len = column.Length;
|
||||
for (int i = len - 1; i >= 0; i--) {
|
||||
sb.Append(column[i]);
|
||||
if (i > 0)
|
||||
sb.Append(",");
|
||||
}
|
||||
} else {
|
||||
sb.Append("*");
|
||||
}
|
||||
|
||||
sb.Append(" from ");
|
||||
sb.Append(table);
|
||||
if (!string.IsNullOrEmpty(where)) {
|
||||
sb.Append(" where ");
|
||||
sb.Append(where);
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
sb.AppendFormat(" limit {0}", count);
|
||||
}
|
||||
|
||||
DataSet ds = new DataSet();
|
||||
|
||||
await ExecuteSqlAsync(dbbase, sb.ToString(), sqlparams, ds, againCount);
|
||||
if (ds.Tables == null || ds.Tables.Count == 0)
|
||||
return null;
|
||||
return ds.Tables[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步执行sql命令
|
||||
/// </summary>
|
||||
public override async Task<int> ExecuteAsync(MySqlCommand cmd, string dbname, DataSet ds = null, int againCount = -1) {
|
||||
int result = 0;
|
||||
int exceCnt = 0;
|
||||
if (againCount < 0)
|
||||
againCount = errorTryAgainCount;
|
||||
try {
|
||||
while (exceCnt <= againCount) {
|
||||
try {
|
||||
if (cmd.Connection.State == ConnectionState.Closed)
|
||||
await cmd.Connection.OpenAsync();
|
||||
cmd.Connection.ChangeDatabase(dbname);
|
||||
|
||||
if (ds == null)
|
||||
result = await cmd.ExecuteNonQueryAsync();
|
||||
else {
|
||||
using(MySqlDataAdapter sda = new MySqlDataAdapter()) {
|
||||
sda.SelectCommand = cmd;
|
||||
sda.Fill(ds);
|
||||
}
|
||||
}
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
cmd.Connection.Close();
|
||||
if (exceCnt < againCount)
|
||||
Debug.Debug.Error("数据库异步执行错误重试次数:" + exceCnt + ",错误类型:" + e.Message);
|
||||
else
|
||||
throw;
|
||||
}
|
||||
|
||||
int time = errorAgainTime + errorAgainTimeInc * exceCnt;
|
||||
exceCnt++;
|
||||
await Task.Delay(time);
|
||||
}
|
||||
} finally {
|
||||
_ConnPool.PutInto(cmd.Connection);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 保存每个存储过程为单个文件
|
||||
/// </summary>
|
||||
/// <param name="dbbase">那个数据库的</param>
|
||||
/// <param name="path">存储的路径</param>
|
||||
/// <param name="method">如果导出其中一个存储过程,则填存储过程名称</param>
|
||||
public void SaveMethod(string dbbase, string path, string method = null) {
|
||||
|
||||
DataTable dt;
|
||||
|
||||
if (method == null)
|
||||
dt = Select("information_schema", "routines", new string[] { "routine_name", "routine_type" }, string.Format("routine_schema='{0}'", dbbase));
|
||||
else
|
||||
dt = Select("information_schema", "routines", new string[] { "routine_name", "routine_type" }, string.Format("routine_schema='{0}' and routine_name='{1}'", dbbase, method));
|
||||
|
||||
foreach (DataColumn item in dt.Columns) {
|
||||
Console.WriteLine(item.ColumnName);
|
||||
}
|
||||
int len = dt.Rows.Count;
|
||||
for (int i = 0; i < len; i++) {
|
||||
SaveMethod(dbbase, path, dt.Rows[i]["routine_name"] as string, dt.Rows[i]["routine_type"] as string);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存方法
|
||||
/// </summary>
|
||||
/// <param name="dbbase"></param>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="methodName"></param>
|
||||
/// <param name="methodType"></param>
|
||||
private void SaveMethod(string dbbase, string path, string methodName, string methodType) {
|
||||
if (!Directory.Exists(path)) {
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
|
||||
string sql = string.Format("show create {0} {1};", methodType, methodName);
|
||||
DataSet ds = new DataSet();
|
||||
ExecuteSql(dbbase, sql, null, ds);
|
||||
|
||||
if (ds.Tables == null || ds.Tables.Count == 0 || ds.Tables[0].Rows == null || ds.Tables[0].Rows.Count == 0)
|
||||
return;
|
||||
path = string.Format("{0}/{1}.sql", path, methodName);
|
||||
|
||||
string sqlCode = string.Format("Drop {0} if exists {1};{2}", methodType, methodName, Environment.NewLine);
|
||||
sqlCode += "Commit;" + Environment.NewLine;
|
||||
sqlCode += ds.Tables[0].Rows[0][string.Format("Create {0}", methodType)] as string;
|
||||
|
||||
File.WriteAllText(path, sqlCode, Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行sql文件
|
||||
/// </summary>
|
||||
/// <param name="dbbase"></param>
|
||||
/// <param name="path"></param>
|
||||
public void Execute(string dbbase, string path) {
|
||||
if (File.Exists(path)) {
|
||||
try {
|
||||
string sql = File.ReadAllText(path);
|
||||
ExecuteSql(dbbase, sql);
|
||||
} catch (Exception e) {
|
||||
Console.WriteLine("运行出错:" + path + Environment.NewLine + e.ToString());
|
||||
}
|
||||
} else if (Directory.Exists(path)) {
|
||||
DirectoryInfo dinfo = new DirectoryInfo(path);
|
||||
|
||||
string[] paths = Directory.GetDirectories(path);
|
||||
|
||||
foreach (var item in paths) {
|
||||
|
||||
Console.WriteLine(item);
|
||||
|
||||
Execute(dbbase, item);
|
||||
}
|
||||
|
||||
FileInfo[] finfos = dinfo.GetFiles();
|
||||
|
||||
foreach (var fl in finfos) {
|
||||
Execute(dbbase, fl.FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
130
MrWu/DB/MySqlParameterData.cs
Normal file
130
MrWu/DB/MySqlParameterData.cs
Normal file
@ -0,0 +1,130 @@
|
||||
#if MySQL
|
||||
|
||||
using System;
|
||||
using System.Data;
|
||||
using MySql.Data.MySqlClient;
|
||||
|
||||
namespace MrWu.DB {
|
||||
/// <summary>
|
||||
/// 存储过程查询键
|
||||
/// </summary>
|
||||
public class MySqlProcedureParameter : IProdureParameter<MySqlCommand> {
|
||||
|
||||
MySqlCommand _command;
|
||||
|
||||
/// <summary>
|
||||
/// 指令
|
||||
/// </summary>
|
||||
public MySqlCommand command {
|
||||
get => _command;
|
||||
}
|
||||
|
||||
private string _dbName;
|
||||
|
||||
/// <summary>
|
||||
/// 数据库名称
|
||||
/// </summary>
|
||||
public string dbName {
|
||||
get => _dbName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 错误信息
|
||||
/// </summary>
|
||||
public Exception exception {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否报错
|
||||
/// </summary>
|
||||
public bool isException {
|
||||
get {
|
||||
return exception != null;
|
||||
}
|
||||
}
|
||||
|
||||
private MySqlProcedureParameter() { }
|
||||
|
||||
|
||||
internal MySqlProcedureParameter(MySqlCommand mysqlcommand,string dbName) {
|
||||
this._command = mysqlcommand;
|
||||
this._dbName = dbName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放
|
||||
/// </summary>
|
||||
public void Dispose() {
|
||||
if (_command != null) {
|
||||
_command.Dispose();
|
||||
_command = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单独设置参数
|
||||
/// </summary>
|
||||
public class MySqlAloneParameter : ISqlAloneParameter<MySqlDbType> {
|
||||
|
||||
string _paramname;
|
||||
|
||||
/// <summary>
|
||||
/// 参数名称
|
||||
/// </summary>
|
||||
public string paramname {
|
||||
get => _paramname;
|
||||
}
|
||||
|
||||
object _parmavalue;
|
||||
|
||||
/// <summary>
|
||||
/// 参数值
|
||||
/// </summary>
|
||||
public object paramvalue {
|
||||
get => _parmavalue;
|
||||
set => _parmavalue = value;
|
||||
}
|
||||
|
||||
MySqlDbType _type;
|
||||
|
||||
/// <summary>
|
||||
/// 参数类型
|
||||
/// </summary>
|
||||
public MySqlDbType type {
|
||||
get => _type;
|
||||
}
|
||||
|
||||
int _valueLength;
|
||||
|
||||
/// <summary>
|
||||
/// 参数长度
|
||||
/// </summary>
|
||||
public int valueLength {
|
||||
get => _valueLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// sql语句单独设置参数
|
||||
/// </summary>
|
||||
/// <param name="paramname">参数名称</param>
|
||||
/// <param name="paramvalue">参数值</param>
|
||||
/// <param name="valuelength">参数值的长度</param>
|
||||
/// <param name="type">参数类型</param>
|
||||
public MySqlAloneParameter(string paramname, object paramvalue, int valuelength, MySqlDbType type = MySqlDbType.Binary) {
|
||||
this._paramname = paramname;
|
||||
this._parmavalue = paramvalue;
|
||||
this._valueLength = valuelength;
|
||||
this._type = type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Direction
|
||||
/// </summary>
|
||||
public ParameterDirection Direction { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
146
MrWu/DB/SqlConfig.cs
Normal file
146
MrWu/DB/SqlConfig.cs
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 由SharpDevelop创建。
|
||||
* 用户: Administrator
|
||||
* 日期: 2019-03-21
|
||||
* 时间: 10:10
|
||||
*
|
||||
* 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件
|
||||
*/
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
namespace MrWu.DB {
|
||||
/// <summary>
|
||||
/// sql 配置
|
||||
/// </summary>
|
||||
public class SqlConfig {
|
||||
/// <summary>
|
||||
/// 配置名称
|
||||
/// </summary>
|
||||
public string name {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string host {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 端口
|
||||
/// </summary>
|
||||
public int port {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用户名
|
||||
/// </summary>
|
||||
public string username {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 密码
|
||||
/// </summary>
|
||||
public string pwd {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 版本
|
||||
/// </summary>
|
||||
public string verison {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否允许使用会话变量
|
||||
/// </summary>
|
||||
public bool AllowUserVariables {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private int errorTryAgainCount_ = 5;
|
||||
|
||||
/// <summary>
|
||||
/// 出错重试次数 0表示不重试
|
||||
/// </summary>
|
||||
public int errorTryAgainCount {
|
||||
get {
|
||||
return errorTryAgainCount_;
|
||||
}
|
||||
set {
|
||||
errorTryAgainCount_ = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 出错重试时间增量 单位毫秒 0表示以固定时间间隔重试
|
||||
/// </summary>
|
||||
public int errorAgainTimeInc {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 不启用SSL模式
|
||||
/// </summary>
|
||||
public string sslMode = "None";
|
||||
|
||||
private int _errorAginTime = 100;
|
||||
|
||||
/// <summary>
|
||||
/// 执行出错,第一次多少毫秒后进行重试
|
||||
/// </summary>
|
||||
public int errorAgainTime {
|
||||
get {
|
||||
return _errorAginTime;
|
||||
}
|
||||
set {
|
||||
_errorAginTime = value;
|
||||
}
|
||||
}
|
||||
|
||||
private int _cacheCount = 10;
|
||||
|
||||
/// <summary>
|
||||
/// 缓存链接数量
|
||||
/// </summary>
|
||||
public int cacheCount {
|
||||
get {
|
||||
return _cacheCount;
|
||||
}
|
||||
set {
|
||||
_cacheCount = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 限制链接数量
|
||||
/// </summary>
|
||||
private int _surviveCountSize = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 最大存活链接数
|
||||
/// </summary>
|
||||
public int surviveCountSize {
|
||||
get {
|
||||
return _surviveCountSize;
|
||||
}
|
||||
set {
|
||||
_surviveCountSize = value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
173
MrWu/DB/SqlEx.cs
Normal file
173
MrWu/DB/SqlEx.cs
Normal file
@ -0,0 +1,173 @@
|
||||
/********************************
|
||||
*
|
||||
* 作者:吴隆健
|
||||
* 创建时间: 2019-06-06 13:34:46
|
||||
*
|
||||
********************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MrWu.DB {
|
||||
/// <summary>
|
||||
/// sql链接
|
||||
/// </summary>
|
||||
/// <typeparam name="T">T是sql的链接类型</typeparam>
|
||||
/// <typeparam name="K">K是指令类型</typeparam>
|
||||
/// <typeparam name="M">M是参数类型</typeparam>
|
||||
/// <typeparam name="N">M是参数类型</typeparam>
|
||||
public abstract class SqlEx<T, K, M,N> : ISqlEx<T, K, M,N> {
|
||||
|
||||
/// <summary>
|
||||
/// 出错的重试次数 0表示不重试
|
||||
/// </summary>
|
||||
public int errorTryAgainCount {
|
||||
get;
|
||||
protected set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 出错重试时间增量 单位毫秒 0表示以固定时间间隔重试
|
||||
/// </summary>
|
||||
public int errorAgainTimeInc {
|
||||
get;
|
||||
protected set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行出错 第一次多少秒后进行重试
|
||||
/// </summary>
|
||||
public int errorAgainTime {
|
||||
get;
|
||||
protected set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 链接池
|
||||
/// </summary>
|
||||
public abstract IConnectionPool<T> connPool { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 给存储过程添加参数
|
||||
/// </summary>
|
||||
/// <param name="spp">存储过程命令</param>
|
||||
/// <param name="paramName">参数名</param>
|
||||
/// <param name="value">参数值</param>
|
||||
/// <param name="type">参数类型</param>
|
||||
/// <param name="pd">参数种类</param>
|
||||
public abstract void AddParam(IProdureParameter<K> spp, string paramName, object value, M type, ParameterDirection pd = ParameterDirection.Input);
|
||||
|
||||
/// <summary>
|
||||
/// 开始执行存储过程
|
||||
/// </summary>
|
||||
/// <param name="dbbase">数据库名称</param>
|
||||
/// <param name="procedure">存储过程名称</param>
|
||||
/// <returns>存储过程指令</returns>
|
||||
public abstract IProdureParameter<K> BeginProcedure(string dbbase, string procedure);
|
||||
|
||||
/// <summary>
|
||||
/// 执行一个事务
|
||||
/// </summary>
|
||||
/// <param name="dbbase">数据库名称</param>
|
||||
/// <returns>存储过程指令</returns>
|
||||
public abstract IProdureParameter<K> BeginTransaction(string dbbase);
|
||||
|
||||
/// <summary>
|
||||
/// 执行cmd
|
||||
/// </summary>
|
||||
/// <param name="cmd">数据库指令</param>
|
||||
/// <param name="dbname">数据库名称</param>
|
||||
/// <param name="ds">结果集</param>
|
||||
/// <param name="againCount">重试次数 小于0表示使用配置值 大于等于0表示自定义</param>
|
||||
/// <returns>受影响的行数</returns>
|
||||
public abstract int Execute(K cmd,string dbname, DataSet ds = null,int againCount = -1);
|
||||
|
||||
/// <summary>
|
||||
/// 执行sql语句
|
||||
/// </summary>
|
||||
/// <param name="dbbase">数据库名称</param>
|
||||
/// <param name="sql">sql语句</param>
|
||||
/// <param name="sqlparams">sql的额外参数</param>
|
||||
/// <param name="ds">查询的结果集</param>
|
||||
/// <param name="againCount">重试次数 小于0表示使用配置值 大于等于0表示自定义</param>
|
||||
/// <returns>受影响的行数</returns>
|
||||
public abstract int ExecuteSql(string dbbase, string sql, IEnumerable<N> sqlparams = null, DataSet ds = null,int againCount = -1);
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个连接
|
||||
/// </summary>
|
||||
/// <param name="dbbase">数据库名称</param>
|
||||
/// <returns>返回链接</returns>
|
||||
public abstract T GetConnection(string dbbase);
|
||||
|
||||
/// <summary>
|
||||
/// 执行一个普通的查询
|
||||
/// </summary>
|
||||
/// <param name="dbbase">数据库名称</param>
|
||||
/// <param name="table">数据库表名</param>
|
||||
/// <param name="column">查询的列</param>
|
||||
/// <param name="where">查询条件</param>
|
||||
/// <param name="count">查询的行数</param>
|
||||
/// <param name="againCount">重试次数 小于0表示使用配置值 大于等于0表示自定义</param>
|
||||
/// <param name="sqlparams">Sql参数</param>
|
||||
/// <returns>查询的结果集</returns>
|
||||
public abstract DataTable Select(string dbbase, string table, string[] column = null, string where = null, int count = 0,int againCount = -1, IEnumerable<N> sqlparams = null);
|
||||
|
||||
/// <summary>
|
||||
/// 提交存储过程
|
||||
/// </summary>
|
||||
/// <param name="spp">存储过程命令</param>
|
||||
/// <param name="ds">查询的结果集</param>
|
||||
/// <param name="againCount">重试次数 小于0表示使用配置值 大于等于0表示自定义</param>
|
||||
/// <returns>执行存储过程所有参数</returns>
|
||||
public abstract Dictionary<string, object> SubProcedure(IProdureParameter<K> spp, DataSet ds = null,int againCount = -1);
|
||||
|
||||
/// <summary>
|
||||
/// 提交一个事务
|
||||
/// </summary>
|
||||
/// <param name="spp">存储过程指令</param>
|
||||
/// <returns>是否执行成功</returns>
|
||||
public abstract bool SubTransaction(IProdureParameter<K> spp);
|
||||
|
||||
/// <summary>
|
||||
/// 为事务添加sql语句
|
||||
/// </summary>
|
||||
/// <param name="spp">存储过程指令</param>
|
||||
/// <param name="sql">sql语句</param>
|
||||
/// <param name="sqlparams">额外参数</param>
|
||||
public abstract int TransactionAddSql(IProdureParameter<K> spp, string sql, List<ISqlAloneParameter<M>> sqlparams = null);
|
||||
|
||||
/// <summary>
|
||||
/// 回滚事务
|
||||
/// </summary>
|
||||
/// <param name="spp"></param>
|
||||
public abstract void RollbackTransaction(IProdureParameter<K> spp);
|
||||
|
||||
#region 异步方法
|
||||
|
||||
/// <summary>
|
||||
/// 异步提交存储过程
|
||||
/// </summary>
|
||||
public abstract Task<Dictionary<string, object>> SubProcedureAsync(IProdureParameter<K> spp, DataSet ds = null, int againCount = -1);
|
||||
|
||||
/// <summary>
|
||||
/// 异步执行sql语句
|
||||
/// </summary>
|
||||
public abstract Task<int> ExecuteSqlAsync(string dbbase, string sql, IEnumerable<N> sqlparams = null, DataSet ds = null, int againCount = -1);
|
||||
|
||||
/// <summary>
|
||||
/// 异步执行一个普通的查询
|
||||
/// </summary>
|
||||
public abstract Task<DataTable> SelectAsync(string dbbase, string table, string[] column = null, string where = null, int count = 0, int againCount = -1, IEnumerable<N> sqlparams = null);
|
||||
|
||||
/// <summary>
|
||||
/// 异步执行cmd
|
||||
/// </summary>
|
||||
public abstract Task<int> ExecuteAsync(K cmd, string dbname, DataSet ds = null, int againCount = -1);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
138
MrWu/DB/SqlParameterData.cs
Normal file
138
MrWu/DB/SqlParameterData.cs
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 作者:吴隆健
|
||||
* 日期: 2019-04-07
|
||||
* 时间: 20:05
|
||||
*
|
||||
*/
|
||||
|
||||
#if MMSQL
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
|
||||
namespace MrWu.DB
|
||||
{
|
||||
/// <summary>
|
||||
/// 存储过程查询键
|
||||
/// </summary>
|
||||
public class SqlProcedureParameter : IProdureParameter<SqlCommand>
|
||||
{
|
||||
SqlCommand _sqlcommand;
|
||||
|
||||
/// <summary>
|
||||
/// 查询键
|
||||
/// </summary>
|
||||
public SqlCommand command
|
||||
{
|
||||
get => _sqlcommand;
|
||||
}
|
||||
|
||||
private string _dbName;
|
||||
|
||||
/// <summary>
|
||||
/// 数据库名称
|
||||
/// </summary>
|
||||
public string dbName
|
||||
{
|
||||
get => _dbName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 错误信息
|
||||
/// </summary>
|
||||
public Exception exception { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否报错
|
||||
/// </summary>
|
||||
public bool isException
|
||||
{
|
||||
get { return exception != null; }
|
||||
}
|
||||
|
||||
private SqlProcedureParameter()
|
||||
{
|
||||
}
|
||||
|
||||
internal SqlProcedureParameter(SqlCommand sqlcommand, string dbName)
|
||||
{
|
||||
this._sqlcommand = sqlcommand;
|
||||
this._dbName = dbName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_sqlcommand != null)
|
||||
{
|
||||
_sqlcommand.Dispose();
|
||||
_sqlcommand = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单独设置参数
|
||||
/// </summary>
|
||||
public class SqlAloneParameter : ISqlAloneParameter<SqlDbType>
|
||||
{
|
||||
string _paramname;
|
||||
|
||||
/// <summary>
|
||||
/// 参数名称
|
||||
/// </summary>
|
||||
public string paramname
|
||||
{
|
||||
get => _paramname;
|
||||
}
|
||||
|
||||
object _paramvalue;
|
||||
|
||||
/// <summary>
|
||||
/// 参数值
|
||||
/// </summary>
|
||||
public object paramvalue
|
||||
{
|
||||
get => _paramvalue;
|
||||
set => _paramvalue = value;
|
||||
}
|
||||
|
||||
SqlDbType _type;
|
||||
|
||||
/// <summary>
|
||||
/// 参数类型
|
||||
/// </summary>
|
||||
public SqlDbType type => _type;
|
||||
|
||||
int _valueLength;
|
||||
|
||||
/// <summary>
|
||||
/// 参数长度
|
||||
/// </summary>
|
||||
public int valueLength => _valueLength;
|
||||
|
||||
/// <summary>
|
||||
/// sql语句单独设置参数
|
||||
/// </summary>
|
||||
/// <param name="paramname">参数名称</param>
|
||||
/// <param name="paramvalue">参数值</param>
|
||||
/// <param name="valuelength">参数值的长度</param>
|
||||
/// <param name="type">参数类型</param>
|
||||
public SqlAloneParameter(string paramname, object paramvalue, int valuelength = 0,
|
||||
SqlDbType type = SqlDbType.Binary)
|
||||
{
|
||||
this._paramname = paramname;
|
||||
this._paramvalue = paramvalue;
|
||||
this._valueLength = valuelength;
|
||||
this._type = type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Direction
|
||||
/// </summary>
|
||||
public ParameterDirection Direction { get; set; }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
48
MrWu/DB/SqlTimer.cs
Normal file
48
MrWu/DB/SqlTimer.cs
Normal file
@ -0,0 +1,48 @@
|
||||
/********************************
|
||||
*
|
||||
* 作者:吴隆健
|
||||
* 创建时间: 2019-06-06 16:34:39
|
||||
*
|
||||
********************************/
|
||||
|
||||
using System;
|
||||
|
||||
namespace MrWu.DB {
|
||||
/// <summary>
|
||||
/// 数据库定时器
|
||||
/// </summary>
|
||||
public class SqlTimer {
|
||||
|
||||
/// <summary>
|
||||
/// 时间间隔单位秒
|
||||
/// </summary>
|
||||
public int interval;
|
||||
|
||||
/// <summary>
|
||||
/// 下次运行的时间点
|
||||
/// </summary>
|
||||
public long runTime;
|
||||
|
||||
/// <summary>
|
||||
/// 是否运行中
|
||||
/// </summary>
|
||||
public bool isRuning {
|
||||
get;
|
||||
internal set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动定时器
|
||||
/// </summary>
|
||||
public void Start() {
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止定时器
|
||||
/// </summary>
|
||||
public void Stop() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
369
MrWu/Debug/Debug.cs
Normal file
369
MrWu/Debug/Debug.cs
Normal file
@ -0,0 +1,369 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using MrWu.Time;
|
||||
|
||||
namespace MrWu.Debug
|
||||
{
|
||||
/// <summary>
|
||||
/// 日志系统
|
||||
/// </summary>
|
||||
public class Debug : IDebug
|
||||
{
|
||||
|
||||
private Debug() {
|
||||
|
||||
}
|
||||
|
||||
private static IDebug m_instance;
|
||||
private static IDebug instance {
|
||||
get {
|
||||
if (m_instance == null) {
|
||||
instanceCnt++;
|
||||
m_instance = new Debug();
|
||||
}
|
||||
return m_instance;
|
||||
}
|
||||
set {
|
||||
m_instance = value;
|
||||
instanceCnt++;
|
||||
}
|
||||
}
|
||||
|
||||
//程序运行时间
|
||||
private static long _timevalue;
|
||||
|
||||
/// <summary>
|
||||
/// 从运行Start方法开始 记录的程序运行时间秒钟数
|
||||
/// </summary>
|
||||
public static long timevalue {
|
||||
get {
|
||||
return Interlocked.Read(ref _timevalue);
|
||||
}
|
||||
}
|
||||
|
||||
private static Timer timer;
|
||||
|
||||
/// <summary>
|
||||
/// 实例数量
|
||||
/// </summary>
|
||||
private static int instanceCnt;
|
||||
|
||||
static Debug() {
|
||||
timer = new Timer(OnTimer, null, 0, 1000);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
~Debug() {
|
||||
instanceCnt--;
|
||||
if (instanceCnt <= 0)
|
||||
if (timer != null)
|
||||
timer.Dispose();
|
||||
}
|
||||
|
||||
private static volatile DebugLevel _logLevel = DebugLevel.Log;
|
||||
|
||||
/// <summary>
|
||||
/// 事件
|
||||
/// </summary>
|
||||
public static event Action<string, DebugLevel> logEvent;
|
||||
|
||||
/// <summary>
|
||||
/// 缓存等级
|
||||
/// </summary>
|
||||
public static DebugLevel logLevel {
|
||||
get {
|
||||
return _logLevel;
|
||||
}
|
||||
set {
|
||||
_logLevel = value;
|
||||
if (LogLevelChange != null)
|
||||
LogLevelChange(_logLevel);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 日志显示等级变化事件
|
||||
/// </summary>
|
||||
public static event Action<DebugLevel> LogLevelChange;
|
||||
|
||||
/// <summary>
|
||||
/// 启动
|
||||
/// </summary>
|
||||
/// <param name="debug">日志接收体</param>
|
||||
/// <param name="logLevel">日志等级</param>
|
||||
public static void Start(IDebug debug, DebugLevel logLevel = DebugLevel.Log | DebugLevel.Warning | DebugLevel.Error) {
|
||||
if (debug != null) {
|
||||
instance = debug;
|
||||
_logLevel = logLevel;
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnTimer(object sender) {
|
||||
//if (Thread.CurrentThread.Name != "Debug线程") {
|
||||
// Thread.CurrentThread.Name = "Debug线程";
|
||||
// Debug.Log("Debug线程:" + Thread.CurrentThread.IsBackground);
|
||||
//}
|
||||
//Debug.Log("Debug 定时器!");
|
||||
Interlocked.Increment(ref _timevalue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 输出日志
|
||||
/// </summary>
|
||||
/// <param name="obj">物体</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Log(object obj) {
|
||||
Print(obj, DebugLevel.Log);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 输出日志
|
||||
/// </summary>
|
||||
/// <param name="format">需要格式的日志</param>
|
||||
/// <param name="args"></param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Log(string format, params object[] args) {
|
||||
Print(format, DebugLevel.Log, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 输出信息
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Info(object obj) {
|
||||
Print(obj, DebugLevel.Info);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 输出信息
|
||||
/// </summary>
|
||||
/// <param name="format"></param>
|
||||
/// <param name="args"></param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Info(string format, params object[] args) {
|
||||
Print(format, DebugLevel.Info, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 警告
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Warning(object obj) {
|
||||
Print(obj, DebugLevel.Warning);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 警告
|
||||
/// </summary>
|
||||
/// <param name="format"></param>
|
||||
/// <param name="args"></param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Warning(string format, params object[] args) {
|
||||
Print(format, DebugLevel.Warning, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重要日志
|
||||
/// </summary>
|
||||
/// <param name="obj">物体信息</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ImportantLog(object obj) {
|
||||
Print(obj, DebugLevel.ImportantLog);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重要日志
|
||||
/// </summary>
|
||||
/// <param name="format"></param>
|
||||
/// <param name="args"></param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ImportantLog(string format, params object[] args) {
|
||||
Print(format, DebugLevel.ImportantLog, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 错误
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Error(object obj) {
|
||||
Print(obj, DebugLevel.Error);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 错误
|
||||
/// </summary>
|
||||
/// <param name="format"></param>
|
||||
/// <param name="args"></param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Error(string format, params object[] args) {
|
||||
Print(format, DebugLevel.Error, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 致命的错误
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
public static void Fatal(object obj) {
|
||||
Print(obj, DebugLevel.Fatal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 致命的错误
|
||||
/// </summary>
|
||||
/// <param name="format"></param>
|
||||
/// <param name="args"></param>
|
||||
public static void Fatal(string format, params object[] args) {
|
||||
Print(format, DebugLevel.Fatal, args);
|
||||
}
|
||||
|
||||
private const string LOGNAME = "LOG";
|
||||
private const string INFONAME = "INFO";
|
||||
private const string WARNINGNAME = "WRANING";
|
||||
private const string IMPORTANTLOGNAME = "IMPORTANTLOG";
|
||||
private const string ERRORNAME = "ERROR";
|
||||
private const string FATALNAME = "FATAL";
|
||||
|
||||
/// <summary>
|
||||
/// 设置消息
|
||||
/// </summary>
|
||||
/// <param name="msg">消息</param>
|
||||
/// <param name="level">等级</param>
|
||||
private static void setMsg(StringBuilder msg, DebugLevel level) {
|
||||
|
||||
string GetLevelStr(DebugLevel _level) {
|
||||
switch (_level) {
|
||||
case DebugLevel.Log:
|
||||
return LOGNAME;
|
||||
case DebugLevel.Info:
|
||||
return INFONAME;
|
||||
case DebugLevel.Warning:
|
||||
return WARNINGNAME;
|
||||
case DebugLevel.ImportantLog:
|
||||
return IMPORTANTLOGNAME;
|
||||
case DebugLevel.Error:
|
||||
return ERRORNAME;
|
||||
case DebugLevel.Fatal:
|
||||
return FATALNAME;
|
||||
default:
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
msg.Append("[");
|
||||
msg.Append(DateTime.Now.TimeToString(TimeStyle.MMddhhmmssfff));
|
||||
msg.Append("]");
|
||||
msg.Append("[");
|
||||
msg.Append(GetLevelStr(level));
|
||||
msg.Append("]:");
|
||||
}
|
||||
|
||||
private static void Print(object obj, DebugLevel level) {
|
||||
if ((logLevel & level) == 0)
|
||||
return;
|
||||
StringBuilder msg = StringBuilderPool.GetStringBuilder();
|
||||
setMsg(msg, level);
|
||||
msg.Append(obj?.ToString() ?? "NULL");
|
||||
PrintAdd(msg, level);
|
||||
}
|
||||
|
||||
private static void Print(string format, DebugLevel level, params object[] args) {
|
||||
if ((logLevel & level) == 0)
|
||||
return;
|
||||
StringBuilder msg = StringBuilderPool.GetStringBuilder();
|
||||
setMsg(msg, level);
|
||||
if (args.Length > 0)
|
||||
msg.Append(string.Format(format, args));
|
||||
else
|
||||
msg.Append(format);
|
||||
|
||||
// #if DEBUG
|
||||
// StackTrace stackTrace = new StackTrace();
|
||||
// msg.Append(stackTrace.ToString());
|
||||
// #endif
|
||||
|
||||
PrintAdd(msg, level);
|
||||
}
|
||||
|
||||
private static void PrintAdd(StringBuilder msg, DebugLevel level) {
|
||||
string logstr = msg.ToString();
|
||||
instance.AddLog(logstr, level);
|
||||
if (logEvent != null)
|
||||
logEvent(logstr, level);
|
||||
StringBuilderPool.PutStringBuilder(msg);
|
||||
}
|
||||
|
||||
void IDebug.AddLog(string str, DebugLevel level) {
|
||||
Console.WriteLine(str);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测量运行时间的池
|
||||
/// </summary>
|
||||
private readonly static ConcurrentDictionary<long, System.Diagnostics.Stopwatch> sws = new ConcurrentDictionary<long, System.Diagnostics.Stopwatch>();
|
||||
|
||||
/// <summary>
|
||||
/// 运行时间id
|
||||
/// </summary>
|
||||
private static long runtimeid = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 测速数量
|
||||
/// </summary>
|
||||
public int StopwatchCount => sws.Count;
|
||||
|
||||
/// <summary>
|
||||
/// 程序开始运行计时
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static long StartTiming() {
|
||||
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
|
||||
long uid = Interlocked.Increment(ref runtimeid);
|
||||
sws.TryAdd(uid, sw);
|
||||
sw.Start();
|
||||
return uid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取代码的运行时间
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public static double GetRunTime(long key) {
|
||||
System.Diagnostics.Stopwatch sw;
|
||||
if (sws.TryRemove(key, out sw)) {
|
||||
sw.Stop();
|
||||
return sw.Elapsed.TotalMilliseconds;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
class StringBuilderPool
|
||||
{
|
||||
private static ConcurrentQueue<StringBuilder> builds = new ConcurrentQueue<StringBuilder>();
|
||||
|
||||
public static StringBuilder GetStringBuilder() {
|
||||
StringBuilder result = null;
|
||||
if (builds.TryDequeue(out result))
|
||||
return result;
|
||||
result = new StringBuilder();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void PutStringBuilder(StringBuilder build) {
|
||||
if (build != null) {
|
||||
build.Clear();
|
||||
builds.Enqueue(build);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
258
MrWu/Debug/DebugControl.Designer.cs
generated
Normal file
258
MrWu/Debug/DebugControl.Designer.cs
generated
Normal file
@ -0,0 +1,258 @@
|
||||
namespace MrWu.Debug {
|
||||
partial class DebugControl {
|
||||
/// <summary>
|
||||
/// 必需的设计器变量。
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// 清理所有正在使用的资源。
|
||||
/// </summary>
|
||||
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
|
||||
protected override void Dispose(bool disposing) {
|
||||
if (disposing && (components != null)) {
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region 组件设计器生成的代码
|
||||
|
||||
/// <summary>
|
||||
/// 设计器支持所需的方法 - 不要修改
|
||||
/// 使用代码编辑器修改此方法的内容。
|
||||
/// </summary>
|
||||
private void InitializeComponent() {
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.textBox1 = new System.Windows.Forms.TextBox();
|
||||
this.timer1 = new System.Windows.Forms.Timer(this.components);
|
||||
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
|
||||
this.ShowToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.logPrintToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.InfoPrintToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.WarningPrintToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.ImportantLogPrintToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.ErrorPrintToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.FatalPrintToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.SaveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.LogSaveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.InfoSaveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.WarningSaveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.ImportantLogSaveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.ErrorSaveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.FatalSaveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.刷新日志文件ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.menuStrip1.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// textBox1
|
||||
//
|
||||
this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.textBox1.Location = new System.Drawing.Point(3, 35);
|
||||
this.textBox1.Multiline = true;
|
||||
this.textBox1.Name = "textBox1";
|
||||
this.textBox1.RightToLeft = System.Windows.Forms.RightToLeft.No;
|
||||
this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
|
||||
this.textBox1.Size = new System.Drawing.Size(604, 192);
|
||||
this.textBox1.TabIndex = 1;
|
||||
//
|
||||
// timer1
|
||||
//
|
||||
this.timer1.Enabled = true;
|
||||
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
|
||||
//
|
||||
// menuStrip1
|
||||
//
|
||||
this.menuStrip1.AutoSize = false;
|
||||
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.ShowToolStripMenuItem,
|
||||
this.SaveToolStripMenuItem,
|
||||
this.刷新日志文件ToolStripMenuItem});
|
||||
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
|
||||
this.menuStrip1.Name = "menuStrip1";
|
||||
this.menuStrip1.Size = new System.Drawing.Size(611, 24);
|
||||
this.menuStrip1.TabIndex = 2;
|
||||
this.menuStrip1.Text = "menuStrip1";
|
||||
//
|
||||
// ShowToolStripMenuItem
|
||||
//
|
||||
this.ShowToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.logPrintToolStripMenuItem,
|
||||
this.InfoPrintToolStripMenuItem,
|
||||
this.WarningPrintToolStripMenuItem,
|
||||
this.ImportantLogPrintToolStripMenuItem,
|
||||
this.ErrorPrintToolStripMenuItem,
|
||||
this.FatalPrintToolStripMenuItem});
|
||||
this.ShowToolStripMenuItem.Name = "ShowToolStripMenuItem";
|
||||
this.ShowToolStripMenuItem.Size = new System.Drawing.Size(44, 20);
|
||||
this.ShowToolStripMenuItem.Text = "显示";
|
||||
//
|
||||
// logPrintToolStripMenuItem
|
||||
//
|
||||
this.logPrintToolStripMenuItem.Name = "logPrintToolStripMenuItem";
|
||||
this.logPrintToolStripMenuItem.Size = new System.Drawing.Size(100, 22);
|
||||
this.logPrintToolStripMenuItem.Tag = "logPrint";
|
||||
this.logPrintToolStripMenuItem.Text = "日志";
|
||||
this.logPrintToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click);
|
||||
//
|
||||
// InfoPrintToolStripMenuItem
|
||||
//
|
||||
this.InfoPrintToolStripMenuItem.Name = "InfoPrintToolStripMenuItem";
|
||||
this.InfoPrintToolStripMenuItem.Size = new System.Drawing.Size(100, 22);
|
||||
this.InfoPrintToolStripMenuItem.Tag = "infoPrint";
|
||||
this.InfoPrintToolStripMenuItem.Text = "信息";
|
||||
this.InfoPrintToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click);
|
||||
//
|
||||
// WarningPrintToolStripMenuItem
|
||||
//
|
||||
this.WarningPrintToolStripMenuItem.Name = "WarningPrintToolStripMenuItem";
|
||||
this.WarningPrintToolStripMenuItem.Size = new System.Drawing.Size(100, 22);
|
||||
this.WarningPrintToolStripMenuItem.Tag = "WarningPrint";
|
||||
this.WarningPrintToolStripMenuItem.Text = "警告";
|
||||
this.WarningPrintToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click);
|
||||
//
|
||||
// ImportantLogPrintToolStripMenuItem
|
||||
//
|
||||
this.ImportantLogPrintToolStripMenuItem.Name = "ImportantLogPrintToolStripMenuItem";
|
||||
this.ImportantLogPrintToolStripMenuItem.Size = new System.Drawing.Size(100, 22);
|
||||
this.ImportantLogPrintToolStripMenuItem.Tag = "ImportantLogPrint";
|
||||
this.ImportantLogPrintToolStripMenuItem.Text = "要志";
|
||||
this.ImportantLogPrintToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click);
|
||||
//
|
||||
// ErrorPrintToolStripMenuItem
|
||||
//
|
||||
this.ErrorPrintToolStripMenuItem.Name = "ErrorPrintToolStripMenuItem";
|
||||
this.ErrorPrintToolStripMenuItem.Size = new System.Drawing.Size(100, 22);
|
||||
this.ErrorPrintToolStripMenuItem.Tag = "ErrorPrint";
|
||||
this.ErrorPrintToolStripMenuItem.Text = "错误";
|
||||
this.ErrorPrintToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click);
|
||||
//
|
||||
// FatalPrintToolStripMenuItem
|
||||
//
|
||||
this.FatalPrintToolStripMenuItem.Name = "FatalPrintToolStripMenuItem";
|
||||
this.FatalPrintToolStripMenuItem.Size = new System.Drawing.Size(100, 22);
|
||||
this.FatalPrintToolStripMenuItem.Tag = "FatalPrint";
|
||||
this.FatalPrintToolStripMenuItem.Text = "致命";
|
||||
this.FatalPrintToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click);
|
||||
//
|
||||
// SaveToolStripMenuItem
|
||||
//
|
||||
this.SaveToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.LogSaveToolStripMenuItem,
|
||||
this.InfoSaveToolStripMenuItem,
|
||||
this.WarningSaveToolStripMenuItem,
|
||||
this.ImportantLogSaveToolStripMenuItem,
|
||||
this.ErrorSaveToolStripMenuItem,
|
||||
this.FatalSaveToolStripMenuItem});
|
||||
this.SaveToolStripMenuItem.Name = "SaveToolStripMenuItem";
|
||||
this.SaveToolStripMenuItem.Size = new System.Drawing.Size(44, 20);
|
||||
this.SaveToolStripMenuItem.Text = "保存";
|
||||
//
|
||||
// LogSaveToolStripMenuItem
|
||||
//
|
||||
this.LogSaveToolStripMenuItem.Name = "LogSaveToolStripMenuItem";
|
||||
this.LogSaveToolStripMenuItem.Size = new System.Drawing.Size(100, 22);
|
||||
this.LogSaveToolStripMenuItem.Tag = "logSave";
|
||||
this.LogSaveToolStripMenuItem.Text = "日志";
|
||||
this.LogSaveToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click);
|
||||
//
|
||||
// InfoSaveToolStripMenuItem
|
||||
//
|
||||
this.InfoSaveToolStripMenuItem.Name = "InfoSaveToolStripMenuItem";
|
||||
this.InfoSaveToolStripMenuItem.Size = new System.Drawing.Size(100, 22);
|
||||
this.InfoSaveToolStripMenuItem.Tag = "InfoSave";
|
||||
this.InfoSaveToolStripMenuItem.Text = "信息";
|
||||
this.InfoSaveToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click);
|
||||
//
|
||||
// WarningSaveToolStripMenuItem
|
||||
//
|
||||
this.WarningSaveToolStripMenuItem.Name = "WarningSaveToolStripMenuItem";
|
||||
this.WarningSaveToolStripMenuItem.Size = new System.Drawing.Size(100, 22);
|
||||
this.WarningSaveToolStripMenuItem.Tag = "WarningSave";
|
||||
this.WarningSaveToolStripMenuItem.Text = "警告";
|
||||
this.WarningSaveToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click);
|
||||
//
|
||||
// ImportantLogSaveToolStripMenuItem
|
||||
//
|
||||
this.ImportantLogSaveToolStripMenuItem.Name = "ImportantLogSaveToolStripMenuItem";
|
||||
this.ImportantLogSaveToolStripMenuItem.Size = new System.Drawing.Size(100, 22);
|
||||
this.ImportantLogSaveToolStripMenuItem.Tag = "ImportantLogSave";
|
||||
this.ImportantLogSaveToolStripMenuItem.Text = "要志";
|
||||
this.ImportantLogSaveToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click);
|
||||
//
|
||||
// ErrorSaveToolStripMenuItem
|
||||
//
|
||||
this.ErrorSaveToolStripMenuItem.Name = "ErrorSaveToolStripMenuItem";
|
||||
this.ErrorSaveToolStripMenuItem.Size = new System.Drawing.Size(100, 22);
|
||||
this.ErrorSaveToolStripMenuItem.Tag = "ErrorSave";
|
||||
this.ErrorSaveToolStripMenuItem.Text = "错误";
|
||||
this.ErrorSaveToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click);
|
||||
//
|
||||
// FatalSaveToolStripMenuItem
|
||||
//
|
||||
this.FatalSaveToolStripMenuItem.Name = "FatalSaveToolStripMenuItem";
|
||||
this.FatalSaveToolStripMenuItem.Size = new System.Drawing.Size(100, 22);
|
||||
this.FatalSaveToolStripMenuItem.Tag = "FatalSave";
|
||||
this.FatalSaveToolStripMenuItem.Text = "致命";
|
||||
this.FatalSaveToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click);
|
||||
//
|
||||
// 刷新日志文件ToolStripMenuItem
|
||||
//
|
||||
this.刷新日志文件ToolStripMenuItem.Name = "刷新日志文件ToolStripMenuItem";
|
||||
this.刷新日志文件ToolStripMenuItem.Size = new System.Drawing.Size(92, 20);
|
||||
this.刷新日志文件ToolStripMenuItem.Text = "刷新日志文件";
|
||||
this.刷新日志文件ToolStripMenuItem.Click += new System.EventHandler(this.FlushToolStripMenuItem_Click);
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.label1.Location = new System.Drawing.Point(213, 3);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(394, 23);
|
||||
this.label1.TabIndex = 3;
|
||||
this.label1.Text = "label1";
|
||||
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||
//
|
||||
// DebugControl
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||
this.Controls.Add(this.label1);
|
||||
this.Controls.Add(this.textBox1);
|
||||
this.Controls.Add(this.menuStrip1);
|
||||
this.Name = "DebugControl";
|
||||
this.Size = new System.Drawing.Size(611, 230);
|
||||
this.Load += new System.EventHandler(this.DebugControl_Load);
|
||||
this.menuStrip1.ResumeLayout(false);
|
||||
this.menuStrip1.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
private System.Windows.Forms.TextBox textBox1;
|
||||
private System.Windows.Forms.Timer timer1;
|
||||
private System.Windows.Forms.MenuStrip menuStrip1;
|
||||
private System.Windows.Forms.ToolStripMenuItem ShowToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem logPrintToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem InfoPrintToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem WarningPrintToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem ImportantLogPrintToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem ErrorPrintToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem FatalPrintToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem SaveToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem LogSaveToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem InfoSaveToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem WarningSaveToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem ImportantLogSaveToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem ErrorSaveToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem FatalSaveToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem 刷新日志文件ToolStripMenuItem;
|
||||
private System.Windows.Forms.Label label1;
|
||||
}
|
||||
}
|
||||
303
MrWu/Debug/DebugControl.cs
Normal file
303
MrWu/Debug/DebugControl.cs
Normal file
@ -0,0 +1,303 @@
|
||||
using MrWu.Time;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace MrWu.Debug
|
||||
{
|
||||
/// <summary>
|
||||
/// 日志控件
|
||||
/// </summary>
|
||||
public partial class DebugControl : UserControl
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 创建日志控件
|
||||
/// </summary>
|
||||
public DebugControl() : this(null) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 日志配置
|
||||
/// </summary>
|
||||
public LogConfig config {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 日志控件
|
||||
/// </summary>
|
||||
public DebugControl(LogConfig config) {
|
||||
this.config = config;
|
||||
if (this.config == null)
|
||||
this.config = getDefaultConfig();
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取默认配置
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private LogConfig getDefaultConfig() {
|
||||
return new LogConfig() {
|
||||
//logpath = DateTime.Now.TimeToString(TimeStyle.MMddhhmmss, string.Empty, string.Empty, string.Empty) + ".log"
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 日志等级
|
||||
/// </summary>
|
||||
private DebugLevel level {
|
||||
get {
|
||||
return Debug.logLevel;
|
||||
}
|
||||
}
|
||||
|
||||
private void LogLevelChange(object sender, EventArgs e) {
|
||||
//CheckBox cb = sender as CheckBox;
|
||||
//if (cb != null) {
|
||||
// string tag = cb.Tag as string;
|
||||
// switch (tag) {
|
||||
// case "log":
|
||||
// if (cb.Checked)
|
||||
// level = level | DebugLevel.Log;
|
||||
// else
|
||||
// level = level & ~DebugLevel.Log;
|
||||
// break;
|
||||
// case "warning":
|
||||
// if (cb.Checked)
|
||||
// level = level | DebugLevel.Warning;
|
||||
// else
|
||||
// level = level & ~DebugLevel.Warning;
|
||||
// break;
|
||||
// case "error":
|
||||
// if (cb.Checked)
|
||||
// level = level | DebugLevel.Error;
|
||||
// else
|
||||
// level = level & ~DebugLevel.Error;
|
||||
// break;
|
||||
// default:
|
||||
// return;
|
||||
// }
|
||||
// textBox1.Clear();
|
||||
// //ClearMemory();
|
||||
//}
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// SetProcessWorkingSetSize
|
||||
///// </summary>
|
||||
///// <param name="process"></param>
|
||||
///// <param name="minSize"></param>
|
||||
///// <param name="maxSize"></param>
|
||||
///// <returns></returns>
|
||||
//[DllImport("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize")]
|
||||
//public static extern int SetProcessWorkingSetSize(IntPtr process, int minSize, int maxSize);
|
||||
|
||||
///// <summary>
|
||||
///// 释放内存
|
||||
///// </summary>
|
||||
//public static void ClearMemory() {
|
||||
// GC.Collect();
|
||||
// GC.WaitForPendingFinalizers();
|
||||
// if(Environment.OSVersion.Platform == PlatformID.Win32NT) {
|
||||
// SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
|
||||
// }
|
||||
//}
|
||||
|
||||
private int logMaxLength = 20000;
|
||||
|
||||
private void timer1_Tick(object sender, EventArgs e) {
|
||||
textBox1.AppendText(LogPool.GetLog(level));
|
||||
if (textBox1.TextLength > logMaxLength) {
|
||||
textBox1.Text = textBox1.Text.Remove(0, textBox1.TextLength - logMaxLength / 2);
|
||||
}
|
||||
RefreshLog();
|
||||
|
||||
label1.Text = $"警告:{LogPool.warningCount},错误:{LogPool.errorCount},致命错误:{LogPool.fatalCount}";
|
||||
}
|
||||
|
||||
private void RefreshLog() {
|
||||
//foreach(var ctl in flowLayoutPanel1.Controls) {
|
||||
// var cc = ctl as Control;
|
||||
|
||||
// if(cc != null) {
|
||||
// string tag = cc.Tag as string;
|
||||
// switch(tag) {
|
||||
// case "logCount":
|
||||
// cc.Text = LogPool.logCount.ToString("N0");
|
||||
// break;
|
||||
// case "warningCount":
|
||||
// cc.Text = LogPool.warningCount.ToString("N0");
|
||||
// break;
|
||||
// case "errorCount":
|
||||
// cc.Text = LogPool.errorCount.ToString("N0");
|
||||
// break;
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
//}
|
||||
}
|
||||
|
||||
private void DebugControl_Load(object sender, EventArgs e) {
|
||||
LogPool.Start(config);
|
||||
RefreshLogLevel(null); //启动的时候刷新
|
||||
Debug.LogLevelChange += LogLevelChange;
|
||||
LogPool.SaveLevelChange += LogLevelChange;
|
||||
}
|
||||
|
||||
private void LogLevelChange(DebugLevel level) {
|
||||
Action< ToolStripMenuItem> action = RefreshLogLevel;
|
||||
this.BeginInvoke(action,new object[]{ null});
|
||||
}
|
||||
|
||||
private void ToolStripMenuItem_Click(object sender, EventArgs e) {
|
||||
var item = sender as ToolStripMenuItem;
|
||||
if (item == null)
|
||||
return;
|
||||
var tag = item.Tag as string;
|
||||
if (string.IsNullOrEmpty(tag))
|
||||
return;
|
||||
//item.Checked = !item.Checked;
|
||||
var lastlevel = Debug.logLevel;
|
||||
switch (tag) {
|
||||
case "logPrint":
|
||||
if (!item.Checked)
|
||||
Debug.logLevel = Debug.logLevel | DebugLevel.Log;
|
||||
else
|
||||
Debug.logLevel = Debug.logLevel & ~DebugLevel.Log;
|
||||
break;
|
||||
case "logSave":
|
||||
if (!item.Checked)
|
||||
LogPool.saveLevel = LogPool.saveLevel | DebugLevel.Log;
|
||||
else
|
||||
LogPool.saveLevel = LogPool.saveLevel & ~DebugLevel.Log;
|
||||
break;
|
||||
case "infoPrint":
|
||||
if (!item.Checked)
|
||||
Debug.logLevel = Debug.logLevel | DebugLevel.Info;
|
||||
else
|
||||
Debug.logLevel = Debug.logLevel & ~DebugLevel.Info;
|
||||
break;
|
||||
case "InfoSave":
|
||||
if (!item.Checked)
|
||||
LogPool.saveLevel = LogPool.saveLevel | DebugLevel.Info;
|
||||
else
|
||||
LogPool.saveLevel = LogPool.saveLevel & ~DebugLevel.Info;
|
||||
break;
|
||||
case "WarningPrint":
|
||||
if (!item.Checked)
|
||||
Debug.logLevel = Debug.logLevel | DebugLevel.Warning;
|
||||
else
|
||||
Debug.logLevel = Debug.logLevel & ~DebugLevel.Warning;
|
||||
break;
|
||||
case "WarningSave":
|
||||
if (!item.Checked)
|
||||
LogPool.saveLevel = LogPool.saveLevel | DebugLevel.Warning;
|
||||
else
|
||||
LogPool.saveLevel = LogPool.saveLevel & ~DebugLevel.Warning;
|
||||
break;
|
||||
case "ImportantLogPrint":
|
||||
if (!item.Checked)
|
||||
Debug.logLevel = Debug.logLevel | DebugLevel.ImportantLog;
|
||||
else
|
||||
Debug.logLevel = Debug.logLevel & ~DebugLevel.ImportantLog;
|
||||
break;
|
||||
case "ImportantLogSave":
|
||||
if (!item.Checked)
|
||||
LogPool.saveLevel = LogPool.saveLevel | DebugLevel.ImportantLog;
|
||||
else
|
||||
LogPool.saveLevel = LogPool.saveLevel & ~DebugLevel.ImportantLog;
|
||||
break;
|
||||
case "ErrorPrint":
|
||||
if (!item.Checked)
|
||||
Debug.logLevel = Debug.logLevel | DebugLevel.Error;
|
||||
else
|
||||
Debug.logLevel = Debug.logLevel & ~DebugLevel.Error;
|
||||
break;
|
||||
case "ErrorSave":
|
||||
if (!item.Checked)
|
||||
LogPool.saveLevel = LogPool.saveLevel | DebugLevel.Error;
|
||||
else
|
||||
LogPool.saveLevel = LogPool.saveLevel & ~DebugLevel.Error;
|
||||
break;
|
||||
case "FatalPrint":
|
||||
if (!item.Checked)
|
||||
Debug.logLevel = Debug.logLevel | DebugLevel.Fatal;
|
||||
else
|
||||
Debug.logLevel = Debug.logLevel & ~DebugLevel.Fatal;
|
||||
break;
|
||||
case "FatalSave":
|
||||
if (!item.Checked)
|
||||
LogPool.saveLevel = LogPool.saveLevel | DebugLevel.Fatal;
|
||||
else
|
||||
LogPool.saveLevel = LogPool.saveLevel & ~DebugLevel.Fatal;
|
||||
break;
|
||||
}
|
||||
if (Debug.logLevel != lastlevel)
|
||||
textBox1.Clear();
|
||||
}
|
||||
|
||||
private void RefreshLogLevel(ToolStripMenuItem myTsm) {
|
||||
ToolStripItemCollection tsic = null;
|
||||
if (myTsm != null) {
|
||||
tsic = myTsm.DropDownItems;
|
||||
} else {
|
||||
tsic = menuStrip1.Items;
|
||||
}
|
||||
|
||||
foreach (var item in tsic) {
|
||||
var tsm = item as ToolStripMenuItem;
|
||||
|
||||
if (tsm != null) {
|
||||
string tag = tsm.Tag as string;
|
||||
switch (tag) {
|
||||
case "logPrint":
|
||||
tsm.Checked = (Debug.logLevel & DebugLevel.Log) != 0;
|
||||
break;
|
||||
case "logSave":
|
||||
tsm.Checked = (LogPool.saveLevel & DebugLevel.Log) != 0;
|
||||
break;
|
||||
case "infoPrint":
|
||||
tsm.Checked = (Debug.logLevel & DebugLevel.Info) != 0;
|
||||
break;
|
||||
case "InfoSave":
|
||||
tsm.Checked = (LogPool.saveLevel & DebugLevel.Info) != 0;
|
||||
break;
|
||||
case "WarningPrint":
|
||||
tsm.Checked = (Debug.logLevel & DebugLevel.Warning) != 0;
|
||||
break;
|
||||
case "WarningSave":
|
||||
tsm.Checked = (LogPool.saveLevel & DebugLevel.Warning) != 0;
|
||||
break;
|
||||
case "ImportantLogPrint":
|
||||
tsm.Checked = (Debug.logLevel & DebugLevel.ImportantLog) != 0;
|
||||
break;
|
||||
case "ImportantLogSave":
|
||||
tsm.Checked = (LogPool.saveLevel & DebugLevel.ImportantLog) != 0;
|
||||
break;
|
||||
case "ErrorPrint":
|
||||
tsm.Checked = (Debug.logLevel & DebugLevel.Error) != 0;
|
||||
break;
|
||||
case "ErrorSave":
|
||||
tsm.Checked = (LogPool.saveLevel & DebugLevel.Error) != 0;
|
||||
break;
|
||||
case "FatalPrint":
|
||||
tsm.Checked = (Debug.logLevel & DebugLevel.Fatal) != 0;
|
||||
break;
|
||||
case "FatalSave":
|
||||
tsm.Checked = (LogPool.saveLevel & DebugLevel.Fatal) != 0;
|
||||
break;
|
||||
}
|
||||
RefreshLogLevel(tsm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void FlushToolStripMenuItem_Click(object sender, EventArgs e) {
|
||||
LogPool.Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
126
MrWu/Debug/DebugControl.resx
Normal file
126
MrWu/Debug/DebugControl.resx
Normal file
@ -0,0 +1,126 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="timer1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>107, 17</value>
|
||||
</metadata>
|
||||
</root>
|
||||
47
MrWu/Debug/DebugLevel.cs
Normal file
47
MrWu/Debug/DebugLevel.cs
Normal file
@ -0,0 +1,47 @@
|
||||
/********************************
|
||||
*
|
||||
* 作者:吴隆健
|
||||
* 创建时间: 2019-06-09 10:57:34
|
||||
*
|
||||
********************************/
|
||||
|
||||
using System;
|
||||
|
||||
namespace MrWu.Debug {
|
||||
/// <summary>
|
||||
/// 日志等级
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum DebugLevel {
|
||||
|
||||
/// <summary>
|
||||
/// 日志
|
||||
/// </summary>
|
||||
Log = 4,
|
||||
|
||||
/// <summary>
|
||||
/// 信息
|
||||
/// </summary>
|
||||
Info = 32,
|
||||
|
||||
/// <summary>
|
||||
/// 警告
|
||||
/// </summary>
|
||||
Warning = 64,
|
||||
|
||||
/// <summary>
|
||||
/// 重要日志
|
||||
/// </summary>
|
||||
ImportantLog = 256,
|
||||
|
||||
/// <summary>
|
||||
/// 错误
|
||||
/// </summary>
|
||||
Error = 1024,
|
||||
|
||||
/// <summary>
|
||||
/// 致命的
|
||||
/// </summary>
|
||||
Fatal = 2048,
|
||||
}
|
||||
}
|
||||
20
MrWu/Debug/IDebug.cs
Normal file
20
MrWu/Debug/IDebug.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MrWu.Debug {
|
||||
|
||||
/// <summary>
|
||||
/// 日志接口
|
||||
/// </summary>
|
||||
public interface IDebug {
|
||||
|
||||
/// <summary>
|
||||
/// 接收到日志
|
||||
/// </summary>
|
||||
/// <param name="str">日志内容</param>
|
||||
/// <param name="level">日志等级 1 普通日志 2 4 8 16 32 64警告 128 256 512 1024错误日志</param>
|
||||
void AddLog(string str,DebugLevel level);
|
||||
}
|
||||
}
|
||||
81
MrWu/Debug/LogConfig.cs
Normal file
81
MrWu/Debug/LogConfig.cs
Normal file
@ -0,0 +1,81 @@
|
||||
/********************************
|
||||
*
|
||||
* 作者:吴隆健
|
||||
* 创建时间: 2019-06-09 10:45:09
|
||||
*
|
||||
********************************/
|
||||
|
||||
using System;
|
||||
|
||||
namespace MrWu.Debug {
|
||||
|
||||
/// <summary>
|
||||
/// 日志配置
|
||||
/// </summary>
|
||||
public class LogConfig {
|
||||
/// <summary>
|
||||
/// 日志缓存数量
|
||||
/// </summary>
|
||||
public int logCacheCount = 1000;
|
||||
|
||||
/// <summary>
|
||||
/// 信息缓存数量
|
||||
/// </summary>
|
||||
public int InfoCacheCount = 800;
|
||||
|
||||
/// <summary>
|
||||
/// 警告缓存数量
|
||||
/// </summary>
|
||||
public int warningCacheCount = 500;
|
||||
|
||||
/// <summary>
|
||||
/// 重要日志
|
||||
/// </summary>
|
||||
public int ImportantLogCacheCount = 300;
|
||||
|
||||
/// <summary>
|
||||
/// 错误缓存数量
|
||||
/// </summary>
|
||||
public int errorCacheCount = 100;
|
||||
|
||||
/// <summary>
|
||||
/// 致命错误
|
||||
/// </summary>
|
||||
public int FatalCacheCount = 1000;
|
||||
|
||||
///// <summary>
|
||||
///// 日志路径
|
||||
///// </summary>
|
||||
//public string logpath { get; set; }
|
||||
|
||||
#if DEBUG
|
||||
/// <summary>
|
||||
/// 日志等级
|
||||
/// </summary>
|
||||
public DebugLevel logLevel = DebugLevel.Info | DebugLevel.Warning | DebugLevel.Error | DebugLevel.ImportantLog | DebugLevel.Fatal;
|
||||
|
||||
#else
|
||||
/// <summary>
|
||||
/// 日志等级
|
||||
/// </summary>
|
||||
public DebugLevel logLevel = DebugLevel.Error | DebugLevel.ImportantLog | DebugLevel.Fatal;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// 保存日志等级
|
||||
/// </summary>
|
||||
public DebugLevel saveLevel = DebugLevel.Info | DebugLevel.Warning | DebugLevel.Error | DebugLevel.ImportantLog | DebugLevel.Fatal;
|
||||
|
||||
/// <summary>
|
||||
/// 构造器
|
||||
/// </summary>
|
||||
public LogConfig() {
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保留天数
|
||||
/// </summary>
|
||||
public int remainDays { get; set; } = 30;
|
||||
}
|
||||
}
|
||||
66
MrWu/Debug/LogMessage.cs
Normal file
66
MrWu/Debug/LogMessage.cs
Normal file
@ -0,0 +1,66 @@
|
||||
/********************************
|
||||
*
|
||||
* 作者:吴隆健
|
||||
* 创建时间: 2019-06-09 10:33:10
|
||||
*
|
||||
********************************/
|
||||
|
||||
using System;
|
||||
using MrWu.Time;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace MrWu.Debug {
|
||||
/// <summary>
|
||||
/// 日志消息
|
||||
/// </summary>
|
||||
public struct LogMessage {
|
||||
/// <summary>
|
||||
/// 消息id
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public ulong logid;
|
||||
|
||||
/// <summary>
|
||||
/// 日志等级
|
||||
/// </summary>
|
||||
public DebugLevel logLevel;
|
||||
|
||||
/// <summary>
|
||||
/// log字符串
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public string logstr;
|
||||
|
||||
///// <summary>
|
||||
/////
|
||||
///// </summary>
|
||||
//public string rawContent;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public DateTime DateTime;
|
||||
|
||||
///// <summary>
|
||||
///// 创建一个日志消息
|
||||
///// </summary>
|
||||
//public LogMessage() {
|
||||
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个日志消息
|
||||
/// </summary>
|
||||
/// <param name="logid">日志id</param>
|
||||
/// <param name="logLevel">日志等级</param>
|
||||
/// <param name="logstr">消息内容</param>
|
||||
public LogMessage(ulong logid, DebugLevel logLevel, string logstr) {
|
||||
this.logid = logid;
|
||||
this.logLevel = logLevel;
|
||||
this.logstr = logstr;
|
||||
|
||||
this.DateTime = DateTime.Now;
|
||||
//this.rawContent = logstr;
|
||||
}
|
||||
}
|
||||
}
|
||||
873
MrWu/Debug/LogPool.cs
Normal file
873
MrWu/Debug/LogPool.cs
Normal file
@ -0,0 +1,873 @@
|
||||
/********************************
|
||||
*
|
||||
* 作者:吴隆健
|
||||
* 创建时间: 2019-06-09 10:31:40
|
||||
*
|
||||
********************************/
|
||||
|
||||
using MrWu.Time;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MrWu.Debug
|
||||
{
|
||||
/// <summary>
|
||||
/// 日志池
|
||||
/// </summary>
|
||||
public class LogPool : IDebug
|
||||
{
|
||||
|
||||
private LogPool(LogConfig config) {
|
||||
this.config = config;
|
||||
var now = DateTime.Now;
|
||||
//if(!string.IsNullOrEmpty(config.logpath)) {
|
||||
logWriter = GetWriter(now);
|
||||
//}
|
||||
|
||||
// 下一版再启用
|
||||
var dueTime = (int)(now.Date.AddDays(1) - now).TotalMilliseconds;
|
||||
//var dueTime = (int)(now.AddMinutes(1) - now).TotalMilliseconds; //测试
|
||||
var period = (int)TimeSpan.FromDays(1).TotalMilliseconds;
|
||||
this.logTaskTimer = new Timer(ZeroTask, null, dueTime, period);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 零时任务
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
void ZeroTask(object obj) {
|
||||
var now = DateTime.Now;
|
||||
SwitchWriter(now);
|
||||
DeleteOutdateLogFiles(now);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更换 logWriter
|
||||
/// </summary>
|
||||
/// <param name="now">当前时间</param>
|
||||
void SwitchWriter(DateTime now) {
|
||||
using (var oldWriter = this.logWriter) {
|
||||
var newWriter = GetWriter(now);
|
||||
this.logWriter = newWriter;
|
||||
oldWriter.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除过期日志文件
|
||||
/// </summary>
|
||||
/// <param name="now">当前时间</param>
|
||||
void DeleteOutdateLogFiles(DateTime now) {
|
||||
var logFiles = new DirectoryInfo(Directory.GetCurrentDirectory() + "/logs").GetFiles("*.log");
|
||||
var outDateLogs = logFiles.Where(p => p.CreationTime.AddDays(config.remainDays) < now).ToList();
|
||||
|
||||
outDateLogs.ForEach(p => {
|
||||
try {
|
||||
p.Delete();
|
||||
} catch (Exception ex) {
|
||||
AddLog($"删除过时日志文件时发生异常!\t{ex.Message}", DebugLevel.Warning);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
StreamWriter GetWriter(DateTime dateTime) {
|
||||
|
||||
string path = Directory.GetCurrentDirectory() + "/logs";
|
||||
if (!Directory.Exists(path))
|
||||
Directory.CreateDirectory(path);
|
||||
|
||||
return new StreamWriter(path + "/" + getLogFileName(dateTime)/*, false, Encoding.UTF8, 300 * 1024*/);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 定时任务 Timer
|
||||
/// </summary>
|
||||
Timer logTaskTimer {
|
||||
get;
|
||||
}
|
||||
|
||||
string getLogFileName(DateTime curDatetime) {
|
||||
return curDatetime.TimeToString(TimeStyle.MMddhhmmss, string.Empty, string.Empty, string.Empty) + ".log";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 实例
|
||||
/// </summary>
|
||||
private static LogPool instance;
|
||||
|
||||
/// <summary>
|
||||
/// 启动
|
||||
/// </summary>
|
||||
/// <param name="config"></param>
|
||||
public static void Start(LogConfig config) {
|
||||
if (config != null) {
|
||||
instance = new LogPool(config);
|
||||
saveLevel = config.saveLevel;
|
||||
Debug.Start(instance, config.logLevel);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 日志流
|
||||
/// </summary>
|
||||
private StreamWriter logWriter {
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 配置
|
||||
/// </summary>
|
||||
public readonly LogConfig config;
|
||||
|
||||
/// <summary>
|
||||
/// 日志
|
||||
/// </summary>
|
||||
// private readonly List<LogMessage> logs = new List<LogMessage>();
|
||||
|
||||
private readonly Queue<LogMessage> logs = new Queue<LogMessage>();
|
||||
|
||||
/// <summary>
|
||||
/// 重要信息
|
||||
/// </summary>
|
||||
private readonly Queue<LogMessage> Infos = new Queue<LogMessage>();
|
||||
|
||||
/// <summary>
|
||||
/// 警告
|
||||
/// </summary>
|
||||
private readonly Queue<LogMessage> warnings = new Queue<LogMessage>();
|
||||
|
||||
/// <summary>
|
||||
/// 重要日志
|
||||
/// </summary>
|
||||
private readonly Queue<LogMessage> ImportantLogs = new Queue<LogMessage>();
|
||||
|
||||
/// <summary>
|
||||
/// 错误
|
||||
/// </summary>
|
||||
private readonly Queue<LogMessage> errors = new Queue<LogMessage>();
|
||||
|
||||
/// <summary>
|
||||
/// 致命的错误
|
||||
/// </summary>
|
||||
private readonly Queue<LogMessage> Fatals = new Queue<LogMessage>();
|
||||
|
||||
private readonly object logidLock = new object();
|
||||
private ulong _logid = 0;
|
||||
private ulong logid {
|
||||
get {
|
||||
ulong value;
|
||||
lock (logidLock) {
|
||||
_logid++;
|
||||
value = _logid;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
private ulong GetLogId() {
|
||||
lock (logidLock) {
|
||||
return _logid;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 日志缓存数量
|
||||
/// </summary>
|
||||
public int logCacheCount => config.logCacheCount;
|
||||
|
||||
/// <summary>
|
||||
/// 信息缓存数量
|
||||
/// </summary>
|
||||
public int InfoCacheCount => config.InfoCacheCount;
|
||||
|
||||
/// <summary>
|
||||
/// 警告缓存数量
|
||||
/// </summary>
|
||||
public int warningCacheCount => config.warningCacheCount;
|
||||
|
||||
/// <summary>
|
||||
/// 重要日志数量
|
||||
/// </summary>
|
||||
public int ImportantLogCaheCount => config.ImportantLogCacheCount;
|
||||
|
||||
/// <summary>
|
||||
/// 错误数量
|
||||
/// </summary>
|
||||
public int errorCacheCount => config.errorCacheCount;
|
||||
|
||||
/// <summary>
|
||||
/// 致命错误
|
||||
/// </summary>
|
||||
public int FatalCacheCount => config.FatalCacheCount;
|
||||
|
||||
private static ulong _logCount;
|
||||
|
||||
/// <summary>
|
||||
/// 日志数量
|
||||
/// </summary>
|
||||
public static ulong logCount {
|
||||
get {
|
||||
return _logCount;
|
||||
}
|
||||
private set {
|
||||
_logCount = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong _InfoCount;
|
||||
|
||||
/// <summary>
|
||||
/// 信息数量
|
||||
/// </summary>
|
||||
public static ulong InfoCount {
|
||||
get {
|
||||
return _InfoCount;
|
||||
}
|
||||
private set {
|
||||
_InfoCount = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong _warningCount;
|
||||
|
||||
/// <summary>
|
||||
/// 警告数量
|
||||
/// </summary>
|
||||
public static ulong warningCount {
|
||||
get => _warningCount;
|
||||
private set {
|
||||
_warningCount = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong _ImportantlogCount;
|
||||
|
||||
/// <summary>
|
||||
/// 重要日志数量
|
||||
/// </summary>
|
||||
public static ulong ImportantlogCount {
|
||||
get => _ImportantlogCount;
|
||||
private set {
|
||||
_ImportantlogCount = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong _errorCount;
|
||||
|
||||
/// <summary>
|
||||
/// 错误总数量
|
||||
/// </summary>
|
||||
public static ulong errorCount {
|
||||
get => _errorCount;
|
||||
private set {
|
||||
_errorCount = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong _fatalCount;
|
||||
|
||||
/// <summary>
|
||||
/// 致命错误数量
|
||||
/// </summary>
|
||||
public static ulong fatalCount {
|
||||
get => _fatalCount;
|
||||
private set {
|
||||
_fatalCount = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 锁定物体
|
||||
/// </summary>
|
||||
private readonly object lockObj = new object();
|
||||
|
||||
/// <summary>
|
||||
/// 锁写日志
|
||||
/// </summary>
|
||||
private readonly object lockWrite = new object();
|
||||
|
||||
/// <summary>
|
||||
/// 锁定日志临时缓存
|
||||
/// </summary>
|
||||
private readonly object locktmpCache = new object();
|
||||
|
||||
/// <summary>
|
||||
/// 临时缓存日志
|
||||
/// </summary>
|
||||
private Queue<LogMessage> tmpCachelog = new Queue<LogMessage>();
|
||||
|
||||
/// <summary>
|
||||
/// 保存日志
|
||||
/// </summary>
|
||||
public static DebugLevel saveLevel {
|
||||
get {
|
||||
return m_saveLevel;
|
||||
}
|
||||
set {
|
||||
m_saveLevel = value;
|
||||
if(SaveLevelChange != null)
|
||||
SaveLevelChange(m_saveLevel);
|
||||
}
|
||||
}
|
||||
|
||||
private static DebugLevel m_saveLevel;
|
||||
|
||||
/// <summary>
|
||||
/// 保存日志的等级变化事件
|
||||
/// </summary>
|
||||
public static event Action<DebugLevel> SaveLevelChange;
|
||||
|
||||
/// <summary>
|
||||
/// 手动刷新日志文本
|
||||
/// </summary>
|
||||
public static void Flush() {
|
||||
if (instance != null) {
|
||||
instance.FlushFile();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新日志文件
|
||||
/// </summary>
|
||||
private void FlushFile() {
|
||||
var writer = this.logWriter;
|
||||
if (writer != null) {
|
||||
lock (lockWrite) {
|
||||
writer.Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 接收到日志
|
||||
/// </summary>
|
||||
/// <param name="str">日志内容</param>
|
||||
/// <param name="level">日志等级 1 普通日志 5警告 10错误日志</param>
|
||||
public void AddLog(string str, DebugLevel level) {
|
||||
LogMessage msg = new LogMessage(logid, level, str);
|
||||
var writer = this.logWriter;
|
||||
if (writer != null && ((level & saveLevel) != 0)) {
|
||||
lock (lockWrite) {
|
||||
writer.WriteLine(msg.logstr);
|
||||
writer.Flush();
|
||||
}
|
||||
}
|
||||
//Task.Factory.StartNew(
|
||||
// () => {
|
||||
//下一版再启用! 挪到外部处理走事件
|
||||
//CollectLoggings.Enqueue(msg);
|
||||
//if (CollectLoggings.Count > 500000) {
|
||||
// //暂存周期为5分钟,日志容器容量为150000,预估最大日志量:3000条 / 每分钟, 足够容纳10个周期的日志量,超期的将不会提交到日志收集模块而被丢弃,但本地保存
|
||||
// CollectLoggings.TryDequeue(out var _);
|
||||
//}
|
||||
lock (locktmpCache) {
|
||||
tmpCachelog.Enqueue(msg);
|
||||
}
|
||||
|
||||
|
||||
lock (lockObj) {
|
||||
switch (level) { //设置日志数量
|
||||
case DebugLevel.Log:
|
||||
logCount++;
|
||||
ReleaseFrist(logs, msg, logCacheCount);
|
||||
break;
|
||||
case DebugLevel.Info:
|
||||
logCount++;
|
||||
ReleaseFrist(Infos, msg, InfoCacheCount);
|
||||
break;
|
||||
case DebugLevel.Warning:
|
||||
warningCount++;
|
||||
ReleaseFrist(warnings, msg, warningCacheCount);
|
||||
break;
|
||||
case DebugLevel.ImportantLog:
|
||||
ImportantlogCount++;
|
||||
ReleaseFrist(ImportantLogs, msg, ImportantLogCaheCount);
|
||||
break;
|
||||
case DebugLevel.Error:
|
||||
errorCount++;
|
||||
ReleaseFrist(errors, msg, errorCacheCount);
|
||||
break;
|
||||
case DebugLevel.Fatal:
|
||||
fatalCount++;
|
||||
ReleaseFrist(Fatals, msg, FatalCacheCount);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// }
|
||||
//);
|
||||
}
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// 如果超过限制,移除并释放第一个
|
||||
/// </summary>
|
||||
/// <param name="list"></param>
|
||||
/// <param name="newLog"></param>
|
||||
/// <param name="limit"></param>
|
||||
void ReleaseFrist(List<LogMessage> list, LogMessage newLog, int limit) {
|
||||
list.Add(newLog);
|
||||
if (list.Count > limit) {
|
||||
//var log = list[0];
|
||||
list.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// 如果超过限制,移除并释放第一个
|
||||
/// </summary>
|
||||
/// <param name="list"></param>
|
||||
/// <param name="newLog"></param>
|
||||
/// <param name="limit"></param>
|
||||
void ReleaseFrist(Queue<LogMessage> list, LogMessage newLog, int limit) {
|
||||
list.Enqueue(newLog);
|
||||
if (list.Count > limit) {
|
||||
//var log = list[0];
|
||||
list.Dequeue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 上次获取日志的等级
|
||||
/// </summary>
|
||||
private DebugLevel lastGetLevel = (DebugLevel)(-1);
|
||||
|
||||
/// <summary>
|
||||
/// 日志id
|
||||
/// </summary>
|
||||
private ulong log_id = 0;
|
||||
|
||||
private ulong Info_id = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 警告id
|
||||
/// </summary>
|
||||
private ulong warning_id = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 重要日志
|
||||
/// </summary>
|
||||
private ulong ImportantLog_id = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 错误id
|
||||
/// </summary>
|
||||
private ulong error_id = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 致命的id
|
||||
/// </summary>
|
||||
private ulong fatal_id = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 获取这段时间的日志
|
||||
/// </summary>
|
||||
/// <param name="tmplogs"></param>
|
||||
/// <param name="tmpinfos"></param>
|
||||
/// <param name="tmpwarnings"></param>
|
||||
/// <param name="tmpimportantLogs"></param>
|
||||
/// <param name="tmperrors"></param>
|
||||
/// <param name="tmpfatal"></param>
|
||||
/// <param name="level"></param>
|
||||
/// <returns></returns>
|
||||
private string ResetlogStrs(List<LogMessage> tmplogs, List<LogMessage> tmpinfos, List<LogMessage> tmpwarnings, List<LogMessage> tmpimportantLogs, List<LogMessage> tmperrors, List<LogMessage> tmpfatal, DebugLevel level) {
|
||||
|
||||
void setlogsId(List<LogMessage> _logs, List<LogMessage> _infos, List<LogMessage> _warnings,
|
||||
List<LogMessage> _importantLogs, List<LogMessage> _errors, List<LogMessage> _fatals) {
|
||||
|
||||
if (_logs.Count == 0)
|
||||
log_id = 0;
|
||||
else
|
||||
log_id = _logs[_logs.Count - 1].logid;
|
||||
|
||||
if (_infos.Count == 0)
|
||||
Info_id = 0;
|
||||
else
|
||||
Info_id = _infos[_infos.Count - 1].logid;
|
||||
|
||||
if (_warnings.Count == 0)
|
||||
warning_id = 0;
|
||||
else
|
||||
warning_id = _warnings[_warnings.Count - 1].logid;
|
||||
|
||||
if (_importantLogs.Count == 0)
|
||||
ImportantLog_id = 0;
|
||||
else
|
||||
ImportantLog_id = _importantLogs[_importantLogs.Count - 1].logid;
|
||||
|
||||
if (_errors.Count == 0)
|
||||
error_id = 0;
|
||||
else
|
||||
error_id = _errors[_errors.Count - 1].logid;
|
||||
|
||||
if (_fatals.Count == 0)
|
||||
fatal_id = 0;
|
||||
else
|
||||
fatal_id = _fatals[_fatals.Count - 1].logid;
|
||||
}
|
||||
|
||||
StringBuilder logStrs = new StringBuilder();
|
||||
lastGetLevel = level;
|
||||
|
||||
bool islog = (tmplogs.Count > 0) && ((lastGetLevel & DebugLevel.Log) != 0);
|
||||
bool isInfo = (tmpinfos.Count > 0) && ((lastGetLevel & DebugLevel.Info) != 0);
|
||||
bool isWarning = (tmpwarnings.Count > 0) && ((lastGetLevel & DebugLevel.Warning) != 0);
|
||||
bool isImportantLog = (tmpimportantLogs.Count > 0) && ((lastGetLevel & DebugLevel.ImportantLog) != 0);
|
||||
bool isError = (tmperrors.Count > 0) && ((lastGetLevel & DebugLevel.Error) != 0);
|
||||
bool isFatal = (tmpfatal.Count > 0) && ((lastGetLevel & DebugLevel.Fatal) != 0);
|
||||
|
||||
int logidx = 0;
|
||||
int infoIdx = 0;
|
||||
int warningidx = 0;
|
||||
int ImportantLogIdx = 0;
|
||||
int erroridx = 0;
|
||||
int fatalIdx = 0;
|
||||
|
||||
DebugLevel mingDebugLevel = (DebugLevel)0;
|
||||
ulong minlogidValue = ulong.MaxValue;
|
||||
|
||||
while (true) {
|
||||
if (islog) {
|
||||
if (tmplogs[logidx].logid < minlogidValue) {
|
||||
minlogidValue = tmplogs[logidx].logid;
|
||||
mingDebugLevel = DebugLevel.Log;
|
||||
}
|
||||
}
|
||||
if (isInfo) {
|
||||
if (tmpinfos[infoIdx].logid < minlogidValue) {
|
||||
minlogidValue = tmpinfos[infoIdx].logid;
|
||||
mingDebugLevel = DebugLevel.Info;
|
||||
}
|
||||
}
|
||||
if (isWarning) {
|
||||
if (tmpwarnings[warningidx].logid < minlogidValue) {
|
||||
minlogidValue = tmpwarnings[warningidx].logid;
|
||||
mingDebugLevel = DebugLevel.Warning;
|
||||
}
|
||||
}
|
||||
if (isImportantLog) {
|
||||
if (tmpimportantLogs[ImportantLogIdx].logid < minlogidValue) {
|
||||
minlogidValue = tmpimportantLogs[ImportantLogIdx].logid;
|
||||
mingDebugLevel = DebugLevel.ImportantLog;
|
||||
}
|
||||
}
|
||||
if (isError) {
|
||||
if (tmperrors[erroridx].logid < minlogidValue) {
|
||||
minlogidValue = tmperrors[erroridx].logid;
|
||||
mingDebugLevel = DebugLevel.Error;
|
||||
}
|
||||
}
|
||||
if (isFatal) {
|
||||
if (tmpfatal[fatalIdx].logid < minlogidValue) {
|
||||
minlogidValue = tmpfatal[fatalIdx].logid;
|
||||
mingDebugLevel = DebugLevel.Fatal;
|
||||
}
|
||||
}
|
||||
switch (mingDebugLevel) {
|
||||
case DebugLevel.Log:
|
||||
logStrs.AppendLine(tmplogs[logidx++].logstr);
|
||||
break;
|
||||
case DebugLevel.Info:
|
||||
logStrs.AppendLine(tmpinfos[infoIdx++].logstr);
|
||||
break;
|
||||
case DebugLevel.Warning:
|
||||
logStrs.AppendLine(tmpwarnings[warningidx++].logstr);
|
||||
break;
|
||||
case DebugLevel.ImportantLog:
|
||||
logStrs.AppendLine(tmpimportantLogs[ImportantLogIdx++].logstr);
|
||||
break;
|
||||
case DebugLevel.Error:
|
||||
logStrs.AppendLine(tmperrors[erroridx++].logstr);
|
||||
break;
|
||||
case DebugLevel.Fatal:
|
||||
logStrs.AppendLine(tmpfatal[fatalIdx++].logstr);
|
||||
break;
|
||||
}
|
||||
|
||||
if (logidx >= tmplogs.Count)
|
||||
islog = false;
|
||||
if (infoIdx >= tmpinfos.Count)
|
||||
isInfo = false;
|
||||
if (warningidx >= tmpwarnings.Count)
|
||||
isWarning = false;
|
||||
if (ImportantLogIdx >= tmpimportantLogs.Count)
|
||||
isImportantLog = false;
|
||||
if (erroridx >= tmperrors.Count)
|
||||
isError = false;
|
||||
if (fatalIdx >= tmpfatal.Count)
|
||||
isFatal = false;
|
||||
|
||||
if (!islog && !isWarning && !isError && !isInfo && !isImportantLog && !isFatal)
|
||||
break;
|
||||
minlogidValue = long.MaxValue;
|
||||
}
|
||||
setlogsId(tmplogs, tmpinfos, tmpwarnings, tmpimportantLogs, tmperrors, tmpfatal);
|
||||
|
||||
lock (locktmpCache) {
|
||||
tmpCachelog.Clear();
|
||||
}
|
||||
|
||||
return logStrs.ToString();
|
||||
}
|
||||
|
||||
private StringBuilder incLogCache = new StringBuilder();
|
||||
|
||||
/// <summary>
|
||||
/// 增量获取日志
|
||||
/// </summary>
|
||||
private string AddLogs() {
|
||||
Queue<LogMessage> tmp;
|
||||
lock (locktmpCache) {
|
||||
tmp = tmpCachelog;
|
||||
tmpCachelog = new Queue<LogMessage>();
|
||||
}
|
||||
|
||||
while (tmp.Count > 0) {
|
||||
incLogCache.AppendLine(tmp.Dequeue().logstr);
|
||||
}
|
||||
string result = incLogCache.ToString();
|
||||
if (result == null)
|
||||
result = string.Empty;
|
||||
incLogCache.Clear();
|
||||
return result;
|
||||
/*
|
||||
StringBuilder logStrs = new StringBuilder();
|
||||
bool islog = (logs.Count > 0) && ((lastGetLevel & DebugLevel.Log) != 0);
|
||||
bool isInfo = (Infos.Count > 0) && ((lastGetLevel & DebugLevel.Info) != 0);
|
||||
bool isWarning = (warnings.Count > 0) && ((lastGetLevel & DebugLevel.Warning) != 0);
|
||||
bool isImportantLog = (ImportantLogs.Count > 0) && ((lastGetLevel & DebugLevel.ImportantLog) != 0);
|
||||
bool isError = (errors.Count > 0) && ((lastGetLevel & DebugLevel.Error) != 0);
|
||||
bool isFatal = (Fatals.Count > 0) && ((lastGetLevel & DebugLevel.Fatal) != 0);
|
||||
|
||||
//增量如何删除
|
||||
DebugLevel mingDebugLevel = (DebugLevel)0;
|
||||
ulong minlogidValue = ulong.MaxValue;
|
||||
|
||||
int logidx = 0;
|
||||
int infoidx = 0;
|
||||
int warningidx = 0;
|
||||
int importantIdx = 0;
|
||||
int erroridx = 0;
|
||||
int fatalidx = 0;
|
||||
|
||||
if (islog) {
|
||||
for (int i = logs.Count - 1; i >= 0; i--) {
|
||||
if (logs[i].logid == log_id) {
|
||||
logidx = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
islog = logidx < logs.Count;
|
||||
}
|
||||
|
||||
if (isInfo) {
|
||||
for (int i = Infos.Count - 1; i >= 0; i--) {
|
||||
if (Infos[i].logid == Info_id) {
|
||||
logidx = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
isInfo = logidx < Infos.Count;
|
||||
}
|
||||
|
||||
if (isWarning) {
|
||||
for (int i = warnings.Count - 1; i >= 0; i--) {
|
||||
if (warnings[i].logid == warning_id) {
|
||||
warningidx = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
isWarning = warningidx < warnings.Count;
|
||||
}
|
||||
|
||||
if (isImportantLog) {
|
||||
for (int i = ImportantLogs.Count - 1; i >= 0; i--) {
|
||||
if (ImportantLogs[i].logid == ImportantLog_id) {
|
||||
importantIdx = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
isImportantLog = importantIdx < ImportantLogs.Count;
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
for (int i = errors.Count - 1; i >= 0; i--) {
|
||||
if (errors[i].logid == error_id) {
|
||||
erroridx = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
isError = erroridx < errors.Count;
|
||||
}
|
||||
|
||||
if (isFatal) {
|
||||
for (int i = Fatals.Count - 1; i >= 0; i--) {
|
||||
if (Fatals[i].logid == fatal_id) {
|
||||
fatalidx = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
isFatal = fatalidx < Fatals.Count;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (islog) {
|
||||
if (logs[logidx].logid < minlogidValue) {
|
||||
minlogidValue = logs[logidx].logid;
|
||||
mingDebugLevel = DebugLevel.Log;
|
||||
}
|
||||
}
|
||||
if (isInfo) {
|
||||
if (Infos[infoidx].logid < minlogidValue) {
|
||||
minlogidValue = Infos[infoidx].logid;
|
||||
mingDebugLevel = DebugLevel.Info;
|
||||
}
|
||||
}
|
||||
if (isWarning) {
|
||||
if (warnings[warningidx].logid < minlogidValue) {
|
||||
minlogidValue = warnings[warningidx].logid;
|
||||
mingDebugLevel = DebugLevel.Warning;
|
||||
}
|
||||
}
|
||||
if (isImportantLog) {
|
||||
if (ImportantLogs[importantIdx].logid < minlogidValue) {
|
||||
minlogidValue = ImportantLogs[importantIdx].logid;
|
||||
mingDebugLevel = DebugLevel.ImportantLog;
|
||||
}
|
||||
}
|
||||
if (isError) {
|
||||
if (errors[erroridx].logid < minlogidValue) {
|
||||
minlogidValue = errors[erroridx].logid;
|
||||
mingDebugLevel = DebugLevel.Error;
|
||||
}
|
||||
}
|
||||
if (isFatal) {
|
||||
if (Fatals[fatalidx].logid < minlogidValue) {
|
||||
minlogidValue = Fatals[fatalidx].logid;
|
||||
mingDebugLevel = DebugLevel.Fatal;
|
||||
}
|
||||
}
|
||||
|
||||
switch (mingDebugLevel) {
|
||||
case DebugLevel.Log:
|
||||
logStrs.AppendLine(logs[logidx++].logstr);
|
||||
break;
|
||||
case DebugLevel.Info:
|
||||
logStrs.AppendLine(Infos[infoidx++].logstr);
|
||||
break;
|
||||
case DebugLevel.Warning:
|
||||
logStrs.AppendLine(warnings[warningidx++].logstr);
|
||||
break;
|
||||
case DebugLevel.ImportantLog:
|
||||
logStrs.AppendLine(ImportantLogs[importantIdx++].logstr);
|
||||
break;
|
||||
case DebugLevel.Error:
|
||||
logStrs.AppendLine(errors[erroridx++].logstr);
|
||||
break;
|
||||
case DebugLevel.Fatal:
|
||||
logStrs.AppendLine(Fatals[fatalidx++].logstr);
|
||||
break;
|
||||
}
|
||||
|
||||
if (logidx >= logs.Count)
|
||||
islog = false;
|
||||
if (infoidx >= Infos.Count)
|
||||
isInfo = false;
|
||||
if (warningidx >= warnings.Count)
|
||||
isWarning = false;
|
||||
if (importantIdx >= ImportantLogs.Count)
|
||||
isImportantLog = false;
|
||||
if (erroridx >= errors.Count)
|
||||
isError = false;
|
||||
if (fatalidx >= Fatals.Count)
|
||||
isFatal = false;
|
||||
if (!islog && !isWarning && !isError && !isInfo && !isImportantLog && !isFatal)
|
||||
break;
|
||||
minlogidValue = long.MaxValue;
|
||||
}
|
||||
setlogsId();
|
||||
return logStrs.ToString();
|
||||
*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取日志_只获取增量
|
||||
/// </summary>
|
||||
/// <param name="level">日志等级</param>
|
||||
/// <returns></returns>
|
||||
public static string GetLog(DebugLevel level = DebugLevel.Log | DebugLevel.Warning | DebugLevel.Error) {
|
||||
if (instance == null)
|
||||
return string.Empty;
|
||||
string result;
|
||||
if (level != instance.lastGetLevel) {
|
||||
List<LogMessage> tmplogs = null;
|
||||
List<LogMessage> tmpinfos = null;
|
||||
List<LogMessage> tmpwarnings = null;
|
||||
List<LogMessage> tmpimportantLogs = null;
|
||||
List<LogMessage> tmperrors = null;
|
||||
List<LogMessage> tmpfatal = null;
|
||||
lock (instance.lockObj) {
|
||||
tmplogs = new List<LogMessage>(instance.logs);
|
||||
tmpinfos = new List<LogMessage>(instance.Infos);
|
||||
tmpwarnings = new List<LogMessage>(instance.warnings);
|
||||
tmpimportantLogs = new List<LogMessage>(instance.ImportantLogs);
|
||||
tmperrors = new List<LogMessage>(instance.errors);
|
||||
tmpfatal = new List<LogMessage>(instance.Fatals);
|
||||
}
|
||||
result = instance.ResetlogStrs(tmplogs, tmpinfos, tmpwarnings, tmpimportantLogs, tmperrors, tmpfatal, level);
|
||||
} else {
|
||||
result = instance.AddLogs();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// GetLoggings
|
||||
/// </summary>
|
||||
/// <param name="currentSeq">当前日志序号</param>
|
||||
/// <returns></returns>
|
||||
public static LogMessage[] GetLoggings(out ulong currentSeq) {
|
||||
currentSeq = instance.GetLogId();
|
||||
var seq = currentSeq;
|
||||
return CollectLoggings.Where(p => p.logid <= seq).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 日志暂存容器,
|
||||
/// </summary>
|
||||
static ConcurrentQueue<LogMessage> CollectLoggings { get; } = new ConcurrentQueue<LogMessage>();
|
||||
static ConcurrentDictionary<ulong, LogMessage> msgs { get; } = new ConcurrentDictionary<ulong, LogMessage>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// ClearLogging
|
||||
/// </summary>
|
||||
/// <param name="currentSeq">日志序号</param>
|
||||
public static void ClearLogging(ulong currentSeq) {
|
||||
while (CollectLoggings.Count > 0) {
|
||||
if (CollectLoggings.TryPeek(out var log)) {
|
||||
if (log.logid <= currentSeq) {
|
||||
CollectLoggings.TryDequeue(out log);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
114
MrWu/IO/ZipCompressHelp.cs
Normal file
114
MrWu/IO/ZipCompressHelp.cs
Normal file
@ -0,0 +1,114 @@
|
||||
/* ==============================================================================
|
||||
* 功能描述:ZipCompressHelp
|
||||
* 创 建 者:徐高庆
|
||||
* 创建日期:2022/10/18 16:26:09
|
||||
* CLR Version :4.0.30319.42000
|
||||
* ==============================================================================*/
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
|
||||
namespace MrWu.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// ZipCompressHelp
|
||||
/// </summary>
|
||||
public class ZipCompressHelp
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 压缩流
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] CompressByte(byte[] data)
|
||||
{
|
||||
if (data == null || data.Length <= 0) return null;
|
||||
using (MemoryStream im = new MemoryStream(data))
|
||||
{
|
||||
using (MemoryStream om = new MemoryStream())
|
||||
{
|
||||
using (DeflateStream com = new DeflateStream(om, CompressionMode.Compress))
|
||||
{
|
||||
im.CopyTo(com);
|
||||
}
|
||||
return om.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解压流
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] DecompressByte(byte[] data)
|
||||
{
|
||||
if (data == null || data.Length <= 0) return null;
|
||||
using (var im = new MemoryStream(data))
|
||||
{
|
||||
using (var om = new MemoryStream())
|
||||
{
|
||||
using (var dcom = new DeflateStream(im, CompressionMode.Decompress))
|
||||
{
|
||||
dcom.CopyTo(om);
|
||||
}
|
||||
return om.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// GZip压缩方式
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] GZipByte(byte[] str)
|
||||
{
|
||||
if (str == null) return null;
|
||||
using (var output = new MemoryStream())
|
||||
{
|
||||
using (
|
||||
var compressor = new Ionic.Zlib.GZipStream(output, Ionic.Zlib.CompressionMode.Compress,
|
||||
Ionic.Zlib.CompressionLevel.Default))
|
||||
{
|
||||
compressor.Write(str, 0, str.Length);
|
||||
}
|
||||
return output.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解压
|
||||
/// </summary>
|
||||
public static byte[] GDexZipByte(byte[] str)
|
||||
{
|
||||
if (str == null) return null;
|
||||
|
||||
using (var xx = new MemoryStream(str))
|
||||
{
|
||||
using (var de = new Ionic.Zlib.GZipStream(xx, Ionic.Zlib.CompressionMode.Decompress,
|
||||
Ionic.Zlib.CompressionLevel.Default))
|
||||
{
|
||||
using (var output = new MemoryStream())
|
||||
{
|
||||
CopyTo(de, output);
|
||||
return output.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void CopyTo(Stream im, Stream om)
|
||||
{
|
||||
if (im == null || om == null) return;
|
||||
byte[] buffer = new byte[1024 * 1024];
|
||||
int r;
|
||||
while ((r = im.Read(buffer, 0, buffer.Length)) > 0)
|
||||
{
|
||||
om.Write(buffer, 0, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
57
MrWu/Model/IPool.cs
Normal file
57
MrWu/Model/IPool.cs
Normal file
@ -0,0 +1,57 @@
|
||||
/********************************
|
||||
*
|
||||
* 作者:吴隆健
|
||||
* 创建时间: 2019-05-31 19:31:37
|
||||
*
|
||||
********************************/
|
||||
|
||||
using System;
|
||||
|
||||
namespace MrWu.Model {
|
||||
/// <summary>
|
||||
/// 池模型
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public interface IPool<T> {
|
||||
|
||||
/// <summary>
|
||||
/// 池容量 0表示不限制
|
||||
/// </summary>
|
||||
int capSize {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 最大的存活数量 0表示不限制
|
||||
/// </summary>
|
||||
int surviveCountSize {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前池中的数量
|
||||
/// </summary>
|
||||
int capCount {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前存活的两
|
||||
/// </summary>
|
||||
int surviveCount {
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>t
|
||||
/// 取出
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
T TakeOut(params object[] pms);
|
||||
|
||||
/// <summary>
|
||||
/// 放入
|
||||
/// </summary>
|
||||
/// <param name="data">放入池中的数据</param>
|
||||
void PutInto(T data);
|
||||
}
|
||||
}
|
||||
73
MrWu/MrWu.csproj
Normal file
73
MrWu/MrWu.csproj
Normal file
@ -0,0 +1,73 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>MrWu</RootNamespace>
|
||||
<AssemblyName>MrWu</AssemblyName>
|
||||
<LangVersion>8.0</LangVersion>
|
||||
<Deterministic>true</Deterministic>
|
||||
<NoWin32Manifest>false</NoWin32Manifest>
|
||||
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<Platforms>AnyCPU</Platforms>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>Full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>TRACE;DEBUG;MySQL;MMSQL;SQLITE;BINARY;FROM;RABBITMQ;CONFIG;Test</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<DocumentationFile></DocumentationFile>
|
||||
<NoWarn>1591</NoWarn>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
|
||||
<DebugType>PdbOnly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;MySQL;MMSQL;SQLITE;BINARY;FROM;RABBITMQ;CONFIG</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Ionic.Zip">
|
||||
<HintPath>..\dll\Ionic.Zip.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="MySql.Data">
|
||||
<HintPath>..\dll\MySql.Data.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>..\..\packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RabbitMQ.Client">
|
||||
<HintPath>..\dll\RabbitMQ.Client.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="ReadMe.txt" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="RabbitMQ\分析.txt" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
36
MrWu/Properties/AssemblyInfo.cs
Normal file
36
MrWu/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// 有关程序集的一般信息由以下
|
||||
// 控制。更改这些特性值可修改
|
||||
// 与程序集关联的信息。
|
||||
[assembly: AssemblyTitle("MrWu")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Microsoft")]
|
||||
[assembly: AssemblyProduct("MrWu")]
|
||||
[assembly: AssemblyCopyright("Copyright © Microsoft 2019")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// 将 ComVisible 设置为 false 会使此程序集中的类型
|
||||
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
|
||||
//请将此类型的 ComVisible 特性设置为 true。
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
|
||||
[assembly: Guid("d6697f35-99f7-4df7-97e4-9567a4fe25b1")]
|
||||
|
||||
// 程序集的版本信息由下列四个值组成:
|
||||
//
|
||||
// 主版本
|
||||
// 次版本
|
||||
// 生成号
|
||||
// 修订号
|
||||
//
|
||||
// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
|
||||
//通过使用 "*",如下所示:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
68
MrWu/RabbitMQ/Consumer.cs
Normal file
68
MrWu/RabbitMQ/Consumer.cs
Normal file
@ -0,0 +1,68 @@
|
||||
/********************************
|
||||
*
|
||||
* 作者:吴隆健
|
||||
* 创建时间: 2019/6/25 11:13:36
|
||||
*
|
||||
********************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using RabbitMQ.Client;
|
||||
|
||||
namespace MrWu.RabbitMQ {
|
||||
|
||||
/// <summary>
|
||||
/// 消费者
|
||||
/// </summary>
|
||||
public class Consumer {
|
||||
|
||||
/// <summary>
|
||||
/// 消费者队列
|
||||
/// </summary>
|
||||
public string queue;
|
||||
|
||||
/// <summary>
|
||||
/// 是否不需要确认
|
||||
/// </summary>
|
||||
public bool noAck = true;
|
||||
|
||||
/// <summary>
|
||||
/// 消息者标记
|
||||
/// </summary>
|
||||
public string consumertag {
|
||||
get;
|
||||
internal set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 消费者同时接收的消息数量
|
||||
/// </summary>
|
||||
public ushort prefetchCount = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 是否不接收本地
|
||||
/// </summary>
|
||||
public bool noLoacl;
|
||||
|
||||
/// <summary>
|
||||
/// 是否独占
|
||||
/// </summary>
|
||||
public bool exclusive;
|
||||
|
||||
/// <summary>
|
||||
/// 额外参数
|
||||
/// </summary>
|
||||
public IDictionary<string, object> arguments;
|
||||
|
||||
/// <summary>
|
||||
/// 消费者信道
|
||||
/// </summary>
|
||||
internal IModel channel;
|
||||
|
||||
/// <summary>
|
||||
/// 接收到消息
|
||||
/// </summary>
|
||||
public MessageHandle Receive;
|
||||
|
||||
}
|
||||
}
|
||||
490
MrWu/RabbitMQ/Message.cs
Normal file
490
MrWu/RabbitMQ/Message.cs
Normal file
@ -0,0 +1,490 @@
|
||||
/********************************
|
||||
*
|
||||
* 作者:吴隆健
|
||||
* 创建时间: 2019/6/24 15:27:55
|
||||
*
|
||||
********************************/
|
||||
|
||||
#if RABBITMQ
|
||||
|
||||
using RabbitMQ.Client;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace MrWu.RabbitMQ {
|
||||
|
||||
/// <summary>
|
||||
/// 消息委托
|
||||
/// </summary>
|
||||
/// <param name="msg"></param>
|
||||
public delegate void MessageHandle(Message msg);
|
||||
|
||||
/// <summary>
|
||||
/// 消息
|
||||
/// </summary>
|
||||
public class Message {
|
||||
|
||||
/// <summary>
|
||||
/// 日志用的查 慢的问题
|
||||
/// </summary>
|
||||
public DateTime logTime;
|
||||
|
||||
private static Exception ex {
|
||||
get {
|
||||
return new Exception("引用已释放的对象!");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否已经释放
|
||||
/// </summary>
|
||||
internal bool isFree = false;
|
||||
|
||||
private string m_exchange;
|
||||
/// <summary>
|
||||
/// 路由
|
||||
/// </summary>
|
||||
public string exchange {
|
||||
get {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
return m_exchange;
|
||||
}
|
||||
set {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
m_exchange = value;
|
||||
}
|
||||
}
|
||||
|
||||
private string m_routingkey;
|
||||
/// <summary>
|
||||
/// 路由键
|
||||
/// </summary>
|
||||
public string routingkey {
|
||||
get {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
return m_routingkey;
|
||||
}
|
||||
set {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
m_routingkey = value;
|
||||
}
|
||||
}
|
||||
|
||||
private bool m_durable;
|
||||
|
||||
public bool durable
|
||||
{
|
||||
get
|
||||
{
|
||||
if (isFree)
|
||||
throw ex;
|
||||
return m_durable;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (isFree)
|
||||
throw ex;
|
||||
|
||||
m_durable = value;
|
||||
}
|
||||
}
|
||||
|
||||
private bool m_mandatory;
|
||||
/// <summary>
|
||||
/// 无法路由是否触发return事件
|
||||
/// </summary>
|
||||
public bool mandatory {
|
||||
get {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
return m_mandatory;
|
||||
}
|
||||
set {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
m_mandatory = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 消息内容
|
||||
/// </summary>
|
||||
public byte[] body {
|
||||
get {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
//Debug.Debug.Log("消息内容:" + bodystr);
|
||||
return Encoding.UTF8.GetBytes(bodystr);
|
||||
}
|
||||
set {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
bodystr = Encoding.UTF8.GetString(value);
|
||||
}
|
||||
}
|
||||
|
||||
private string m_bodystr;
|
||||
/// <summary>
|
||||
/// 消息内容
|
||||
/// </summary>
|
||||
public string bodystr {
|
||||
get {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
return m_bodystr;
|
||||
}
|
||||
set {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
m_bodystr = value;
|
||||
}
|
||||
}
|
||||
|
||||
private IBasicProperties m_propertis;
|
||||
|
||||
/// <summary>
|
||||
/// 消息的属性
|
||||
/// </summary>
|
||||
public IBasicProperties propertis {
|
||||
get {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
return m_propertis;
|
||||
}
|
||||
set {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
m_propertis = value;
|
||||
}
|
||||
}
|
||||
|
||||
private ulong m_deliveryTag;
|
||||
/// <summary>
|
||||
/// 投递的或接收的消息标志
|
||||
/// </summary>
|
||||
public ulong deliveryTag {
|
||||
get {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
return m_deliveryTag;
|
||||
}
|
||||
internal set {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
m_deliveryTag = value;
|
||||
}
|
||||
}
|
||||
|
||||
private string m_id;
|
||||
|
||||
/// <summary>
|
||||
/// 消息的唯一标识
|
||||
/// </summary>
|
||||
public string id {
|
||||
get {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
return m_id;
|
||||
}
|
||||
internal set {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
m_id = value;
|
||||
}
|
||||
}
|
||||
|
||||
private bool m_success;
|
||||
/// <summary>
|
||||
/// 是否发送成功
|
||||
/// </summary>
|
||||
public bool success {
|
||||
get {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
return m_success;
|
||||
}
|
||||
internal set {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
m_success = value;
|
||||
}
|
||||
}
|
||||
|
||||
private bool m_isAutoFree;
|
||||
|
||||
/// <summary>
|
||||
/// 是否发送完毕自动释放
|
||||
/// </summary>
|
||||
public bool isAutoFree {
|
||||
get {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
return m_isAutoFree;
|
||||
}
|
||||
internal set {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
m_isAutoFree = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送失败事件
|
||||
/// </summary>
|
||||
public MessageHandle SendResult = null;
|
||||
|
||||
|
||||
|
||||
private IDictionary<string, object> m_useradata;
|
||||
/// <summary>
|
||||
/// 暂存数据,客户端发送失败可能用的上
|
||||
/// </summary>
|
||||
public IDictionary<string, object> userData {
|
||||
|
||||
get {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
return m_useradata;
|
||||
}
|
||||
set {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
m_useradata = value;
|
||||
}
|
||||
}
|
||||
|
||||
private IModel m_channel;
|
||||
|
||||
/// <summary>
|
||||
/// 处理该消息的信道
|
||||
/// </summary>
|
||||
internal IModel channel {
|
||||
get {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
return m_channel;
|
||||
}
|
||||
set {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
m_channel = value;
|
||||
}
|
||||
}
|
||||
|
||||
private bool m_noAck;
|
||||
|
||||
/// <summary>
|
||||
/// 是否需要确认
|
||||
/// </summary>
|
||||
public bool noAck {
|
||||
get {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
return m_noAck;
|
||||
}
|
||||
internal set {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
m_noAck = value;
|
||||
}
|
||||
}
|
||||
|
||||
private bool m_redelivered;
|
||||
|
||||
/// <summary>
|
||||
/// 是否被重复接收过
|
||||
/// </summary>
|
||||
public bool redelivered {
|
||||
get {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
return m_redelivered;
|
||||
}
|
||||
internal set {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
m_redelivered = value;
|
||||
}
|
||||
}
|
||||
|
||||
private uint m_messageCount;
|
||||
/// <summary>
|
||||
/// 剩余消息的数量
|
||||
/// </summary>
|
||||
public uint messageCount {
|
||||
get {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
return m_messageCount;
|
||||
}
|
||||
internal set {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
m_messageCount = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确认
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool Ack(bool multiple = false) {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
if (channel == null)
|
||||
return false;
|
||||
lock (channel) {
|
||||
try {
|
||||
channel.BasicAck(deliveryTag, multiple);
|
||||
} catch (Exception e) {
|
||||
Debug.Debug.Error("确认消息出错:" + e.ToString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 拒绝确认
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool NAck(bool multiple = false, bool requeue = false) {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
if (channel == null)
|
||||
return false;
|
||||
lock (channel) {
|
||||
try {
|
||||
channel.BasicNack(deliveryTag, multiple, requeue);
|
||||
} catch (Exception e) {
|
||||
Debug.Debug.Error("拒绝消息出错:" + e.ToString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private long m_createTime;
|
||||
/// <summary>
|
||||
/// 发送的逻辑时间
|
||||
/// </summary>
|
||||
internal long createTime {
|
||||
get {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
return m_createTime;
|
||||
}
|
||||
set {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
m_createTime = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
internal Message() {
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
/////
|
||||
///// </summary>
|
||||
///// <param name="id"></param>
|
||||
//internal Message(string id) {
|
||||
// this.id = id;
|
||||
// createTime = Debug.Debug.timevalue;
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString() {
|
||||
if (isFree)
|
||||
throw ex;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine("exchange:" + exchange);
|
||||
sb.AppendLine("routingkey:" + routingkey);
|
||||
sb.AppendLine("mandatory:" + mandatory);
|
||||
sb.AppendLine("deliveryTag:" + deliveryTag);
|
||||
sb.AppendLine("success:" + success);
|
||||
sb.AppendLine("body:" + bodystr);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 克隆一个
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Message Clone() {
|
||||
Message result = new Message();
|
||||
result.isFree = this.isFree;
|
||||
result.exchange = this.exchange;
|
||||
result.routingkey = this.routingkey;
|
||||
result.mandatory = this.mandatory;
|
||||
result.bodystr = this.bodystr;
|
||||
result.propertis = this.propertis;
|
||||
result.deliveryTag = this.deliveryTag;
|
||||
result.id = this.id;
|
||||
result.success = this.success;
|
||||
result.isAutoFree = this.isAutoFree;
|
||||
result.userData = this.userData;
|
||||
result.SendResult = this.SendResult;
|
||||
result.channel = this.channel;
|
||||
result.noAck = this.noAck;
|
||||
result.redelivered = this.redelivered;
|
||||
result.messageCount = this.messageCount;
|
||||
result.createTime = this.createTime;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清楚事件
|
||||
/// </summary>
|
||||
public void Clear() {
|
||||
success = false;
|
||||
noAck = false;
|
||||
isAutoFree = false;
|
||||
SendResult = null;
|
||||
exchange = null;
|
||||
routingkey = null;
|
||||
bodystr = null;
|
||||
propertis = null;
|
||||
userData = null;
|
||||
channel = null;
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// Dispose
|
||||
///// </summary>
|
||||
//public void Dispose() {
|
||||
// this.id = null;
|
||||
// this.exchange = null;
|
||||
// this.routingkey = null;
|
||||
// this.bodystr = null;
|
||||
// this.propertis?.Headers?.Clear();
|
||||
// this.propertis = null;
|
||||
|
||||
// this.userData?.Clear();
|
||||
// this.userData = null;
|
||||
// this.channel = null;
|
||||
|
||||
// if(this.SendResult != null) {
|
||||
// var deles = this.SendResult.GetInvocationList();
|
||||
// foreach(MessageHandle dele in deles) {
|
||||
// this.SendResult -= dele;
|
||||
// }
|
||||
// }
|
||||
|
||||
// GC.SuppressFinalize(this);
|
||||
//}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
79
MrWu/RabbitMQ/MessagePool.cs
Normal file
79
MrWu/RabbitMQ/MessagePool.cs
Normal file
@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
namespace MrWu.RabbitMQ {
|
||||
|
||||
/// <summary>
|
||||
/// 消息池
|
||||
/// </summary>
|
||||
public class MessagePool {
|
||||
|
||||
private static string m_tag;
|
||||
|
||||
/// <summary>
|
||||
/// 唯一标志头
|
||||
/// </summary>
|
||||
public static string tag {
|
||||
get {
|
||||
return m_tag;
|
||||
}
|
||||
set {
|
||||
m_tag = value + DateTime.Now.Ticks;
|
||||
}
|
||||
}
|
||||
|
||||
private static long id = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 获取id
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetId() {
|
||||
return tag + Interlocked.Increment(ref id);
|
||||
}
|
||||
|
||||
static MessagePool() {
|
||||
instance = new MessagePool();
|
||||
}
|
||||
|
||||
private MessagePool() {
|
||||
}
|
||||
|
||||
private static MessagePool instance;
|
||||
|
||||
/// <summary>
|
||||
/// 消息池
|
||||
/// </summary>
|
||||
private readonly ConcurrentQueue<Message> msgs = new ConcurrentQueue<Message>();
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个消息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Message GetMessage(string msgid = null) {
|
||||
if (msgid == null)
|
||||
msgid = GetId();//Guid.NewGuid().ToString();
|
||||
Message msg = null;
|
||||
if (!instance.msgs.TryDequeue(out msg))
|
||||
msg = new Message();
|
||||
msg.isFree = false;
|
||||
msg.id = msgid;
|
||||
msg.createTime = Debug.Debug.timevalue;
|
||||
return msg;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 放入一个消息
|
||||
/// </summary>
|
||||
/// <param name="msg"></param>
|
||||
public static void PutMessage(ref Message msg) {
|
||||
if (msg == null)
|
||||
return;
|
||||
msg.Clear();
|
||||
msg.isFree = true;
|
||||
instance.msgs.Enqueue(msg);
|
||||
msg = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
213
MrWu/RabbitMQ/RabbitConfig.cs
Normal file
213
MrWu/RabbitMQ/RabbitConfig.cs
Normal file
@ -0,0 +1,213 @@
|
||||
/********************************
|
||||
*
|
||||
* 作者:吴隆健
|
||||
* 创建时间: 2019/6/24 13:47:34
|
||||
*
|
||||
********************************/
|
||||
#if RABBITMQ
|
||||
using System;
|
||||
|
||||
namespace MrWu.RabbitMQ {
|
||||
/// <summary>
|
||||
/// Rabbit配置
|
||||
/// </summary>
|
||||
public class RabbitConfig {
|
||||
|
||||
/// <summary>
|
||||
/// 用户名
|
||||
/// </summary>
|
||||
public string username;
|
||||
/// <summary>
|
||||
/// 密码
|
||||
/// </summary>
|
||||
public string password;
|
||||
|
||||
/// <summary>
|
||||
/// 虚拟主机
|
||||
/// </summary>
|
||||
public string vhost;
|
||||
|
||||
/// <summary>
|
||||
/// ip
|
||||
/// </summary>
|
||||
public string host = "127.0.0.1";
|
||||
|
||||
/// <summary>
|
||||
/// 端口
|
||||
/// </summary>
|
||||
public int port = 5672;
|
||||
|
||||
/// <summary>
|
||||
/// 交换机
|
||||
/// </summary>
|
||||
public ExchangeInfo[] exchanges;
|
||||
|
||||
/// <summary>
|
||||
/// 队列
|
||||
/// </summary>
|
||||
public QueueInfo[] queues;
|
||||
|
||||
/// <summary>
|
||||
/// 绑定信息
|
||||
/// </summary>
|
||||
public BindInfo[] binds;
|
||||
|
||||
/// <summary>
|
||||
/// 是否异步确认模式
|
||||
/// </summary>
|
||||
public bool AsynConfirm = true;
|
||||
|
||||
/// <summary>
|
||||
/// 构造器
|
||||
/// </summary>
|
||||
public RabbitConfig() { }
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 交换机
|
||||
/// </summary>
|
||||
public class ExchangeInfo {
|
||||
/// <summary>
|
||||
/// 交换机名称
|
||||
/// </summary>
|
||||
public string name = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 类型
|
||||
/// </summary>
|
||||
public string type = "direct";
|
||||
|
||||
/// <summary>
|
||||
/// 持久化
|
||||
/// </summary>
|
||||
public bool durable = true;
|
||||
|
||||
/// <summary>
|
||||
/// 自动删除
|
||||
/// </summary>
|
||||
public bool autodelete = false;
|
||||
|
||||
/// <summary>
|
||||
/// 备用交换机 当消息无法路由是,投递到的备用交换机
|
||||
/// </summary>
|
||||
public string alternate_exchange = null;
|
||||
|
||||
/// <summary>
|
||||
/// 如果存在则先删除再声明
|
||||
/// </summary>
|
||||
public bool delExists = false;
|
||||
|
||||
/// <summary>
|
||||
/// 构造器
|
||||
/// </summary>
|
||||
public ExchangeInfo() { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 队列信息
|
||||
/// </summary>
|
||||
public class QueueInfo {
|
||||
/// <summary>
|
||||
/// 队列名称
|
||||
/// </summary>
|
||||
public string name;
|
||||
|
||||
/// <summary>
|
||||
/// 持久化
|
||||
/// </summary>
|
||||
public bool durable = false;
|
||||
|
||||
/// <summary>
|
||||
/// 独占
|
||||
/// </summary>
|
||||
public bool exclusive = false;
|
||||
|
||||
/// <summary>
|
||||
/// 自动删除
|
||||
/// </summary>
|
||||
public bool autodelete = false;
|
||||
|
||||
/// <summary>
|
||||
/// 启动时清空队列
|
||||
/// </summary>
|
||||
public bool startClear = false;
|
||||
|
||||
/// <summary>
|
||||
/// 如果存在则先删除
|
||||
/// </summary>
|
||||
public bool delExists = false;
|
||||
|
||||
/// <summary>
|
||||
/// 消息过期时间
|
||||
/// </summary>
|
||||
public int x_message_ttl = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 在指定的时间内没有消费者链接就会删除队列
|
||||
/// </summary>
|
||||
public int x_expires = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 消最大就绪的条目数
|
||||
/// </summary>
|
||||
public int x_max_length = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 消息最大的就绪内容大小
|
||||
/// </summary>
|
||||
public int x_max_length_bytes = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 当消息溢出时,删除头部消息(drop-head)/拒绝消息(reject-publish)
|
||||
/// </summary>
|
||||
public string x_overflow = null;
|
||||
|
||||
/// <summary>
|
||||
/// 当消息被丢弃时,丢弃到哪个交换机中 与 x-dead-touting-key 一起使用
|
||||
/// </summary>
|
||||
public string x_dead_letter_exchange = null;
|
||||
|
||||
/// <summary>
|
||||
/// 当消息被丢弃时,投递到交换机中使用的路由键
|
||||
/// </summary>
|
||||
public string x_dead_routing_key = null;
|
||||
|
||||
/// <summary>
|
||||
/// 队列 优先级
|
||||
/// </summary>
|
||||
public int x_max_priority = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 队列模式 default / lazy . lazy 表示消息存储在磁盘中,当消费者需要的时候才加载到内存中,当消息大量积累的情况应该启用
|
||||
/// </summary>
|
||||
public string x_queue_mode = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绑定信息
|
||||
/// </summary>
|
||||
public class BindInfo {
|
||||
/// <summary>
|
||||
/// 绑定类型 queue / exchange
|
||||
/// </summary>
|
||||
public string bindType = "queue";
|
||||
|
||||
/// <summary>
|
||||
/// 目标
|
||||
/// </summary>
|
||||
public string destination;
|
||||
|
||||
/// <summary>
|
||||
/// 源
|
||||
/// </summary>
|
||||
public string source;
|
||||
|
||||
/// <summary>
|
||||
/// 路由键
|
||||
/// </summary>
|
||||
public string routingkey;
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
807
MrWu/RabbitMQ/RabbitManager.cs
Normal file
807
MrWu/RabbitMQ/RabbitManager.cs
Normal file
@ -0,0 +1,807 @@
|
||||
/********************************
|
||||
*
|
||||
* 作者:吴隆健
|
||||
* 创建时间: 2019/6/24 14:22:26
|
||||
*
|
||||
********************************/
|
||||
#if RABBITMQ
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using RabbitMQ.Client;
|
||||
using RabbitMQ.Client.Events;
|
||||
|
||||
namespace MrWu.RabbitMQ {
|
||||
/// <summary>
|
||||
/// rabbit管理
|
||||
/// </summary>
|
||||
public class RabbitManager {
|
||||
|
||||
/// <summary>
|
||||
/// 配置
|
||||
/// </summary>
|
||||
public RabbitConfig config {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送失败
|
||||
/// </summary>
|
||||
public event MessageHandle SendResult;
|
||||
|
||||
/// <summary>
|
||||
/// 未路由成功事件
|
||||
/// </summary>
|
||||
public event MessageHandle notRoutingOk;
|
||||
|
||||
/// <summary>
|
||||
/// 构造
|
||||
/// </summary>
|
||||
public RabbitManager() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 未确认的数量
|
||||
/// </summary>
|
||||
public int NoAckCount {
|
||||
get {
|
||||
return waitAck.Count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动rabbit管理
|
||||
/// </summary>
|
||||
/// <param name="config"></param>
|
||||
public void Start(RabbitConfig config = null) {
|
||||
if (connection != null) {
|
||||
Debug.Debug.Error("已启动过!");
|
||||
return;
|
||||
}
|
||||
if (config != null)
|
||||
this.config = config;
|
||||
if (this.config == null)
|
||||
throw new ArgumentNullException("config is null");
|
||||
|
||||
Connection();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 链接
|
||||
/// </summary>
|
||||
public IConnection connection {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生产者信道
|
||||
/// </summary>
|
||||
private IModel producer {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取信息的通道
|
||||
/// </summary>
|
||||
private IModel infoChannel {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 等待发送确认的消息
|
||||
/// </summary>
|
||||
private ConcurrentDictionary<ulong, Message> waitAck { get; } = new ConcurrentDictionary<ulong, Message>();
|
||||
|
||||
/// <summary>
|
||||
/// 生产者锁
|
||||
/// </summary>
|
||||
private readonly object lockobj = new object();
|
||||
|
||||
/// <summary>
|
||||
/// 链接rabbit
|
||||
/// </summary>
|
||||
private void Connection() {
|
||||
ConnectionFactory factory = new ConnectionFactory();
|
||||
factory.UserName = config.username;
|
||||
factory.Password = config.password;
|
||||
factory.VirtualHost = config.vhost;
|
||||
factory.HostName = config.host;
|
||||
factory.Port = config.port;
|
||||
factory.AutomaticRecoveryEnabled = true;
|
||||
|
||||
if (connection != null) {
|
||||
connection.ConnectionBlocked -= ConnectionBlockedEventHandler;
|
||||
connection.ConnectionUnblocked -= ConnectionUnblockedEventHandler;
|
||||
}
|
||||
|
||||
|
||||
connection = factory.CreateConnection();
|
||||
connection.ConnectionBlocked += ConnectionBlockedEventHandler;
|
||||
connection.ConnectionUnblocked += ConnectionUnblockedEventHandler;
|
||||
|
||||
Debug.Debug.Log("链接创建生产者!");
|
||||
CreateProducer();
|
||||
DeclareExchange(config.exchanges);
|
||||
DeclareQueue(config.queues);
|
||||
Bind(config.binds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建生产者
|
||||
/// </summary>
|
||||
private void CreateProducer() {
|
||||
try {
|
||||
|
||||
Debug.Debug.Log("创建生产者:" + Thread.CurrentThread.ManagedThreadId);
|
||||
|
||||
producer = connection.CreateModel();
|
||||
producer.ConfirmSelect();
|
||||
producer.ModelShutdown += ModelShutdown;
|
||||
producer.BasicReturn += MessageReturn;
|
||||
producer.BasicAcks += MessageAck;
|
||||
producer.BasicNacks += MessageNAck;
|
||||
} catch (Exception e) {
|
||||
Debug.Debug.Error("创建生产者出错!" + e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 消息阻塞!!!
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="args"></param>
|
||||
private void ConnectionBlockedEventHandler(IConnection sender, ConnectionBlockedEventArgs args) {
|
||||
Debug.Debug.Fatal("RabbitMQ 消息阻塞,链接被禁用!" + args.Reason);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 消息阻塞被解除!
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
private void ConnectionUnblockedEventHandler(IConnection sender) {
|
||||
Debug.Debug.Fatal("RabbitMQ 消息解除阻塞!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 链接禁用!
|
||||
/// </summary>
|
||||
public void ConnectionBlocked() {
|
||||
if (connection == null)
|
||||
return;
|
||||
|
||||
connection.HandleConnectionBlocked("auto blocked");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解除链接禁用
|
||||
/// </summary>
|
||||
public void ConnectionUnBlocked() {
|
||||
if (connection == null)
|
||||
return;
|
||||
connection.HandleConnectionUnblocked();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建信息通道
|
||||
/// </summary>
|
||||
private void CreateInfoChannel() {
|
||||
try {
|
||||
infoChannel = connection.CreateModel();
|
||||
infoChannel.ModelShutdown += ModelShutdown;
|
||||
} catch (Exception e) {
|
||||
Debug.Debug.Error("创建信息获取通道出错!" + e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 消息发送成功
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <param name="args"></param>
|
||||
private void MessageAck(IModel model, BasicAckEventArgs args) {
|
||||
DoAck(model, args.DeliveryTag, args.Multiple, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <param name="deliverytag"></param>
|
||||
/// <param name="multiple"></param>
|
||||
/// <param name="success"></param>
|
||||
private void DoAck(IModel model, ulong deliverytag, bool multiple, bool success) {
|
||||
|
||||
void DoAck(Message _msg, bool _success) {
|
||||
_msg.success = success;
|
||||
if (_msg.SendResult != null)
|
||||
_msg.SendResult(_msg);
|
||||
else
|
||||
SendResult?.Invoke(_msg);
|
||||
|
||||
if (_msg.isAutoFree)
|
||||
MessagePool.PutMessage(ref _msg);
|
||||
}
|
||||
|
||||
Message msg;
|
||||
|
||||
//同步确认的消息也会到这里来!!!
|
||||
if (!multiple) {
|
||||
if (waitAck.TryRemove(deliverytag, out msg)) {//拿到
|
||||
DoAck(msg, success);
|
||||
}
|
||||
} else {
|
||||
List<ulong> keys = new List<ulong>();
|
||||
foreach (var item in waitAck) {
|
||||
if (item.Value.deliveryTag <= deliverytag)
|
||||
keys.Add(item.Value.deliveryTag);
|
||||
}
|
||||
foreach (var item in keys) {
|
||||
if (waitAck.TryRemove(item, out msg)) {
|
||||
DoAck(msg, success);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 消息未被发送
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <param name="args"></param>
|
||||
private void MessageNAck(IModel model, BasicNackEventArgs args) {
|
||||
DoAck(model, args.DeliveryTag, args.Multiple, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 信道断开
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <param name="reason"></param>
|
||||
private void ModelShutdown(IModel model, ShutdownEventArgs reason) {
|
||||
|
||||
Debug.Debug.Warning("信道断开事件:" + Thread.CurrentThread.ManagedThreadId);
|
||||
|
||||
if (producer != null && producer.IsClosed) {
|
||||
Debug.Debug.Info("剩余确认数量:" + waitAck.Count);
|
||||
// producer.BasicNack
|
||||
producer.ModelShutdown -= ModelShutdown;
|
||||
producer.BasicReturn -= MessageReturn;
|
||||
producer.BasicAcks -= MessageAck;
|
||||
producer.BasicNacks -= MessageNAck;
|
||||
|
||||
waitAck.Clear();
|
||||
|
||||
producer = null;
|
||||
} else if (infoChannel != null && infoChannel.IsClosed)
|
||||
infoChannel = null;
|
||||
Debug.Debug.Error("信道断开:" + reason);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 消息被返回
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <param name="args"></param>
|
||||
private void MessageReturn(IModel model, BasicReturnEventArgs args) {
|
||||
|
||||
if (args.BasicProperties != null) {
|
||||
string msgid = args.BasicProperties.MessageId;
|
||||
Message msg = MessagePool.GetMessage(msgid);// new Message(msgid);
|
||||
msg.noAck = true;
|
||||
msg.body = args.Body;
|
||||
msg.channel = model;
|
||||
msg.exchange = args.Exchange;
|
||||
msg.routingkey = args.RoutingKey;
|
||||
msg.userData = args.BasicProperties.Headers;
|
||||
|
||||
notRoutingOk?.Invoke(msg);
|
||||
} else {
|
||||
Debug.Debug.Error("怎么可能BasicProperties 不存在");
|
||||
}
|
||||
|
||||
Debug.Debug.Error("消息被撤回,ReplyText:" + args.Exchange + "," + args.RoutingKey +
|
||||
"," + args.ReplyText + ",ReplyCode:" + args.ReplyCode + ",body:" + Encoding.UTF8.GetString(args.Body));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 声明路由
|
||||
/// </summary>
|
||||
public void DeclareExchange(ExchangeInfo[] infos) {
|
||||
if (infos != null) {
|
||||
int len = infos.Length;
|
||||
for (int i = 0; i < len; i++) {
|
||||
ExchangeInfo exchange = infos[i];
|
||||
IDictionary<string, object> properties = new Dictionary<string, object>();
|
||||
if (!string.IsNullOrEmpty(exchange.alternate_exchange)) {
|
||||
properties.Add("alternate-exchange", exchange.alternate_exchange);
|
||||
}
|
||||
|
||||
if (exchange.delExists)
|
||||
DelExchange(exchange.name);
|
||||
|
||||
//Debug.Debug.Log("进入锁44444"+Thread.CurrentThread.ManagedThreadId);
|
||||
lock (lockobj) {
|
||||
producer.ExchangeDeclare(exchange.name, exchange.type, exchange.durable, exchange.autodelete, properties);
|
||||
}
|
||||
//Debug.Debug.Log("退出锁4444");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 声明队列
|
||||
/// </summary>
|
||||
public QueueDeclareOk DeclareQueue() {
|
||||
QueueDeclareOk result = null;
|
||||
//Debug.Debug.Log("进入锁3333"+Thread.CurrentThread.ManagedThreadId);
|
||||
lock (lockobj) {
|
||||
try {
|
||||
result = producer.QueueDeclare();
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//Debug.Debug.Log("退出锁3333");
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///声明队列
|
||||
/// </summary>
|
||||
public void DeclareQueue(QueueInfo[] infos) {
|
||||
if (infos != null) {
|
||||
int len = infos.Length;
|
||||
for (int i = 0; i < len; i++) {
|
||||
QueueInfo queue = infos[i];
|
||||
IDictionary<string, object> properties = new Dictionary<string, object>();
|
||||
if (!string.IsNullOrEmpty(queue.x_dead_letter_exchange)) {
|
||||
properties.Add("x-dead-letter-exchange", queue.x_dead_letter_exchange);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(queue.x_dead_routing_key)) {
|
||||
properties.Add("x-dead-routing-key", queue.x_dead_routing_key);
|
||||
}
|
||||
if (queue.x_expires > 0) {
|
||||
properties.Add("x-expires", queue.x_expires);
|
||||
}
|
||||
if (queue.x_max_length > 0) {
|
||||
properties.Add("x-max-length", queue.x_max_length);
|
||||
}
|
||||
if (queue.x_max_length_bytes > 0) {
|
||||
properties.Add("x-max-length-bytes", queue.x_max_length_bytes);
|
||||
}
|
||||
if (queue.x_max_priority > 0) {
|
||||
properties.Add("x-max-priority", queue.x_max_priority);
|
||||
}
|
||||
if (queue.x_message_ttl > 0) {
|
||||
properties.Add("x-message-ttl", queue.x_message_ttl);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(queue.x_overflow)) {
|
||||
properties.Add("x-overflow", queue.x_overflow);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(queue.x_queue_mode)) {
|
||||
properties.Add("x-queue-mode", queue.x_queue_mode);
|
||||
}
|
||||
if (queue.delExists)
|
||||
DelQueue(queue.name);
|
||||
|
||||
//Debug.Debug.Log("进入锁7454354354"+Thread.CurrentThread.ManagedThreadId);
|
||||
lock (lockobj) {
|
||||
producer.QueueDeclare(queue.name, queue.durable, queue.exclusive, queue.autodelete, properties);
|
||||
if (queue.startClear)
|
||||
producer.QueuePurge(queue.name);
|
||||
}
|
||||
//Debug.Debug.Log("退出锁7454354354"+Thread.CurrentThread.ManagedThreadId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绑定
|
||||
/// </summary>
|
||||
public void Bind(BindInfo[] infos) {
|
||||
if (infos != null) {
|
||||
int len = infos.Length;
|
||||
for (int i = 0; i < len; i++) {
|
||||
BindInfo bind = infos[i];
|
||||
switch (bind.bindType) {
|
||||
case "queue":
|
||||
//Debug.Debug.Log("进入锁asdasfasf"+Thread.CurrentThread.ManagedThreadId);
|
||||
lock (lockobj) {
|
||||
producer.QueueBind(bind.destination, bind.source, bind.routingkey);
|
||||
}
|
||||
//Debug.Debug.Log("退出锁asdasfasf"+Thread.CurrentThread.ManagedThreadId);
|
||||
break;
|
||||
case "exchange":
|
||||
//Debug.Debug.Log("进入锁asdsafasfasd"+Thread.CurrentThread.ManagedThreadId);
|
||||
lock (lockobj) {
|
||||
producer.ExchangeBind(bind.destination, bind.source, bind.routingkey);
|
||||
}
|
||||
//Debug.Debug.Log("退出锁asdsafasfasd"+Thread.CurrentThread.ManagedThreadId);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("BindType value is not queue/exchange");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取队列信息
|
||||
/// </summary>
|
||||
/// <param name="name">名称</param>
|
||||
/// <returns>队列信息</returns>
|
||||
public QueueDeclareOk GetQueueInfo(string name) {
|
||||
try {
|
||||
IModel channel = connection.CreateModel();
|
||||
var result = channel.QueueDeclarePassive(name);
|
||||
return result;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 交换机存在不存在
|
||||
/// </summary>
|
||||
/// <param name="name">交换机名称</param>
|
||||
/// <returns>true 表示存在 false 可能是网络故障,或者不存在</returns>
|
||||
public bool ExchangeExists(string name) {
|
||||
try {
|
||||
IModel channel = connection.CreateModel();
|
||||
channel.ExchangeDeclarePassive(name);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly ConcurrentBag<RpcClient> rpcs = new ConcurrentBag<RpcClient>();
|
||||
|
||||
/// <summary>
|
||||
/// 创建RPC客户端
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public RpcClient CreateRpcClient(ushort prefetchCount = 3) {
|
||||
var rpc = new RpcClient(config, prefetchCount);
|
||||
rpcs.Add(rpc);
|
||||
return rpc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建信道
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal IModel CreateIModel() {
|
||||
return connection.CreateModel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取消息属性
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IBasicProperties GetBasicProperties() {
|
||||
try {
|
||||
|
||||
IBasicProperties result = null;
|
||||
//Debug.Debug.Log("进入锁6666" + Thread.CurrentThread.ManagedThreadId);
|
||||
lock (lockobj) {
|
||||
//Debug.Debug.Log("生产者:" + (producer == null));
|
||||
if (producer == null)
|
||||
CreateProducer();
|
||||
if (producer == null)
|
||||
result = null;
|
||||
else
|
||||
result = producer.CreateBasicProperties();
|
||||
}
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
Debug.Debug.Error("获取消息属性出错:" + e.ToString());
|
||||
return null;
|
||||
} finally {
|
||||
//Debug.Debug.Log("退出锁6666");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 无返回值的发送,按配置来决定是异步还是同步确认
|
||||
/// </summary>
|
||||
public void BasicPublish(Message msg, bool isAutoFree) {
|
||||
BasicPublish(msg, config.AsynConfirm, isAutoFree);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 有返回值的发送_必须是同步,马上需要知道结果
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool BasicPublish_Result(Message msg, bool isAutoFree) {
|
||||
return BasicPublish(msg, false, isAutoFree);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送消息
|
||||
/// </summary>
|
||||
/// <param name="msg">消息</param>
|
||||
/// <param name="isAsyn">isAsyn 是否异步</param>
|
||||
/// <param name="isAutoFree">是否自动释放</param>
|
||||
private bool BasicPublish(Message msg, bool isAsyn, bool isAutoFree) {
|
||||
if (msg == null || msg.body == null || msg.body.Length == 0) {
|
||||
Debug.Debug.Error("发送数据为空!" + new System.Diagnostics.StackTrace().ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
msg.isAutoFree = isAutoFree;
|
||||
bool isNotice = false;
|
||||
bool result = false;
|
||||
try {
|
||||
//Debug.Debug.Log("进入锁1111" + Thread.CurrentThread.ManagedThreadId);
|
||||
lock (lockobj) {
|
||||
//Debug.Debug.Log("处理发送!" + (producer == null));
|
||||
if (producer == null) {
|
||||
CreateProducer();
|
||||
}
|
||||
//Debug.Debug.Log("111");
|
||||
if (producer == null) {
|
||||
isNotice = true;
|
||||
msg.success = false;
|
||||
} else {
|
||||
msg.channel = producer;
|
||||
|
||||
msg.noAck = true;
|
||||
msg.deliveryTag = producer.NextPublishSeqNo;
|
||||
if (msg.propertis == null)
|
||||
msg.propertis = producer.CreateBasicProperties();
|
||||
msg.propertis.MessageId = msg.id;
|
||||
msg.propertis.Headers = msg.userData;
|
||||
//持久化
|
||||
msg.propertis.SetPersistent(msg.durable);
|
||||
//Debug.Debug.Info($"读取到过期时间:{msg.propertis.Expiration}");
|
||||
if (!msg.durable)
|
||||
{
|
||||
msg.propertis.Expiration = "3000";
|
||||
}
|
||||
|
||||
//Debug.Debug.Log("发送消息:" + msg.bodystr + "," + msg.exchange + "," + msg.propertis + "," + msg.mandatory);
|
||||
|
||||
producer.BasicPublish(msg.exchange, msg.routingkey, msg.mandatory, false, msg.propertis, msg.body);
|
||||
if (!isAsyn) {//同步
|
||||
//Debug.Debug.Log("同步等待!");
|
||||
msg.success = producer.WaitForConfirms();
|
||||
result = msg.success;
|
||||
if (!msg.success) {
|
||||
Console.WriteLine(msg.success);
|
||||
}
|
||||
isNotice = true;
|
||||
|
||||
} else { //异步 //添加到列表
|
||||
if (producer.IsClosed) {
|
||||
isNotice = true;
|
||||
msg.success = false;
|
||||
} else {
|
||||
//Debug.Debug.Log("异步发送:" + msg.deliveryTag);
|
||||
|
||||
//Debug.Debug.Log("得到数据:" + waitAck.Count);
|
||||
waitAck.GetOrAdd(msg.deliveryTag, msg);
|
||||
|
||||
//Debug.Debug.Log("得到数据:" + waitAck.Count);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Debug.Debug.Warning("发送消息失败:" + e.ToString() + Environment.NewLine + msg.ToString());
|
||||
isNotice = true;
|
||||
msg.success = false;
|
||||
}
|
||||
|
||||
if (isNotice) {
|
||||
if (msg.SendResult != null)
|
||||
msg.SendResult(msg);
|
||||
else
|
||||
SendResult?.Invoke(msg);
|
||||
|
||||
if (msg.isAutoFree)
|
||||
MessagePool.PutMessage(ref msg);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除队列
|
||||
/// </summary>
|
||||
/// <param name="queue">队列名称</param>
|
||||
/// <param name="ifUnused">是否没有用户才删除</param>
|
||||
/// <param name="ifEmpty">是否是空队列才删除</param>
|
||||
/// <returns>uint 0表示删除失败 大于等于1表示删除成功 队列中消息数量+1</returns>
|
||||
public uint DelQueue(string queue, bool ifUnused = false, bool ifEmpty = false) {
|
||||
IModel model = connection.CreateModel();
|
||||
uint result = 0;
|
||||
try {
|
||||
result = model.QueueDelete(queue, ifUnused, ifEmpty);
|
||||
result += 1;
|
||||
} catch (Exception e) {
|
||||
Debug.Debug.Warning("删除队列异常:" + e.ToString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除交换机
|
||||
/// </summary>
|
||||
/// <param name="exchange">交换机</param>
|
||||
/// <param name="ifUnused">是否没用户才删除</param>
|
||||
/// <returns>true 表示删除成功 false 队列不存在</returns>
|
||||
public bool DelExchange(string exchange, bool ifUnused = false) {
|
||||
IModel model = connection.CreateModel();
|
||||
try {
|
||||
model.ExchangeDelete(exchange, ifUnused);
|
||||
} catch (Exception e) {
|
||||
Debug.Debug.Warning("删除交换机异常:" + e.ToString());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取消息
|
||||
/// </summary>
|
||||
/// <param name="queue">队列</param>
|
||||
/// <param name="noAck">true 表示自动确认 false 表示手动确认</param>
|
||||
/// <returns></returns>
|
||||
public Message GetMessage(string queue, bool noAck = true) {
|
||||
//Debug.Debug.Log("进入锁2222" + Thread.CurrentThread.ManagedThreadId);
|
||||
lock (lockobj) {
|
||||
if (infoChannel == null)
|
||||
CreateInfoChannel();
|
||||
if (infoChannel == null)
|
||||
return null;
|
||||
}
|
||||
//Debug.Debug.Log("退出锁2222");
|
||||
IModel key = infoChannel;
|
||||
Message msg = null;
|
||||
lock (key) {
|
||||
try {
|
||||
BasicGetResult bgr = key.BasicGet(queue, noAck);
|
||||
|
||||
if (bgr != null) {
|
||||
string msgid = null;
|
||||
IDictionary<string, object> userdata = null;
|
||||
if (bgr.BasicProperties == null) {
|
||||
Debug.Debug.Error("这个消息竟然没有属性!");
|
||||
} else {
|
||||
msgid = bgr.BasicProperties.MessageId;
|
||||
userdata = bgr.BasicProperties.Headers;
|
||||
}
|
||||
msg = MessagePool.GetMessage(msgid);// new Message(msgid);
|
||||
msg.noAck = noAck;
|
||||
msg.exchange = bgr.Exchange;
|
||||
msg.routingkey = bgr.RoutingKey;
|
||||
msg.propertis = bgr.BasicProperties;
|
||||
msg.body = bgr.Body;
|
||||
msg.deliveryTag = bgr.DeliveryTag;
|
||||
msg.channel = key;
|
||||
msg.redelivered = bgr.Redelivered;
|
||||
msg.messageCount = bgr.MessageCount;
|
||||
msg.userData = userdata;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Debug.Debug.Error("获取消息出错:" + e.ToString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加消费者
|
||||
/// </summary>
|
||||
/// <param name="consumer">消费者</param>
|
||||
public void AddConsumer(Consumer consumer) {
|
||||
IModel model = connection.CreateModel();
|
||||
consumer.channel = model;
|
||||
if (string.IsNullOrEmpty(consumer.consumertag))
|
||||
consumer.consumertag = Guid.NewGuid().ToString();
|
||||
|
||||
consumer.consumertag = model.BasicConsume(consumer.queue, consumer.noAck, consumer.consumertag,
|
||||
consumer.noLoacl, consumer.exclusive, consumer.arguments, new BasicConsumer(consumer));
|
||||
|
||||
if (consumer.prefetchCount > 0) {
|
||||
model.BasicQos(0, consumer.prefetchCount, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移出消费者
|
||||
/// </summary>
|
||||
/// <param name="consumer"></param>
|
||||
public void RemoveConsumer(Consumer consumer) {
|
||||
if (consumer == null || consumer.channel == null || consumer.channel.IsClosed)
|
||||
return;
|
||||
consumer.channel.BasicCancel(consumer.consumertag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭
|
||||
/// </summary>
|
||||
public void Close() {
|
||||
if (connection != null) {
|
||||
connection.Dispose();
|
||||
connection = null;
|
||||
}
|
||||
foreach (var item in rpcs) {
|
||||
item.Close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 消费者类
|
||||
/// </summary>
|
||||
private class BasicConsumer : DefaultBasicConsumer {
|
||||
|
||||
public Consumer consumer;
|
||||
|
||||
public BasicConsumer(Consumer consumer) {
|
||||
this.consumer = consumer;
|
||||
}
|
||||
|
||||
public override void HandleBasicCancel(string consumerTag) {
|
||||
base.HandleBasicCancel(consumerTag);
|
||||
}
|
||||
|
||||
public override void HandleBasicConsumeOk(string consumerTag) {
|
||||
base.HandleBasicConsumeOk(consumerTag);
|
||||
}
|
||||
|
||||
public override void HandleBasicCancelOk(string consumerTag) {
|
||||
base.HandleBasicCancelOk(consumerTag);
|
||||
}
|
||||
|
||||
public override void HandleBasicDeliver(string consumerTag, ulong deliveryTag, bool redelivered, string exchange, string routingKey, IBasicProperties properties, byte[] body) {
|
||||
string msgid = null;
|
||||
IDictionary<string, object> userdata = null;
|
||||
if (properties == null) {
|
||||
Debug.Debug.Error("消费者接收消息异常,没有收到消息id");
|
||||
} else {
|
||||
msgid = properties.MessageId;
|
||||
userdata = properties.Headers;
|
||||
}
|
||||
|
||||
Message message = MessagePool.GetMessage(msgid); // new Message(msgid);
|
||||
message.exchange = exchange;
|
||||
message.routingkey = routingKey;
|
||||
message.noAck = consumer.noAck;
|
||||
message.propertis = properties;
|
||||
message.redelivered = redelivered;
|
||||
message.deliveryTag = deliveryTag;
|
||||
message.channel = consumer.channel;
|
||||
message.body = body;
|
||||
message.userData = userdata;
|
||||
|
||||
//去处理消息
|
||||
consumer.Receive?.Invoke(message);
|
||||
//base.HandleBasicDeliver(consumerTag, deliveryTag, redelivered, exchange, routingKey, properties, body);
|
||||
}
|
||||
|
||||
public override void HandleModelShutdown(IModel model, ShutdownEventArgs reason) {
|
||||
base.HandleModelShutdown(model, reason);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
#endif
|
||||
248
MrWu/RabbitMQ/RpcClient.cs
Normal file
248
MrWu/RabbitMQ/RpcClient.cs
Normal file
@ -0,0 +1,248 @@
|
||||
/********************************
|
||||
*
|
||||
* 作者:吴隆健
|
||||
* 创建时间: 2019/6/29 15:19:34
|
||||
*
|
||||
********************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MrWu.Debug;
|
||||
using RabbitMQ.Client;
|
||||
using RabbitMQ.Client.Events;
|
||||
|
||||
namespace MrWu.RabbitMQ {
|
||||
/// <summary>
|
||||
/// RPC客户端
|
||||
/// </summary>
|
||||
public class RpcClient {
|
||||
|
||||
/// <summary>
|
||||
/// 发送者
|
||||
/// </summary>
|
||||
private IModel channel;
|
||||
|
||||
private IConnection connection;
|
||||
|
||||
private readonly object lockObj = new object();
|
||||
|
||||
/// <summary>
|
||||
/// rpc结果
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<string, string> respQueue = new ConcurrentDictionary<string, string>();
|
||||
|
||||
private RabbitConfig config;
|
||||
|
||||
private ushort prefetchCount;
|
||||
|
||||
/// <summary>
|
||||
/// 消费者
|
||||
/// </summary>
|
||||
private EventingBasicConsumer consumer;
|
||||
|
||||
/// <summary>
|
||||
/// 创建客户端
|
||||
/// </summary>
|
||||
/// <param name="config">rabbit配置</param>
|
||||
/// <param name="prefetchCount">同时处理的数量</param>
|
||||
internal RpcClient(RabbitConfig config, ushort prefetchCount) {
|
||||
this.config = config;
|
||||
this.prefetchCount = prefetchCount;
|
||||
ConnectionFactory factory = new ConnectionFactory();
|
||||
factory.UserName = config.username;
|
||||
factory.Password = config.password;
|
||||
factory.VirtualHost = config.vhost;
|
||||
factory.HostName = config.host;
|
||||
factory.Port = config.port;
|
||||
factory.AutomaticRecoveryEnabled = true;
|
||||
|
||||
connection = factory.CreateConnection();
|
||||
CreateChannel();
|
||||
|
||||
//this.CallBackTimeOutCheckTimer = new Timer(CallBackTimeoutCheck, null, 20, 20);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭
|
||||
/// </summary>
|
||||
internal void Close() {
|
||||
connection.Close();
|
||||
}
|
||||
|
||||
private void CreateChannel() {
|
||||
channel = connection.CreateModel();
|
||||
var queue = channel.QueueDeclare();
|
||||
reply_queue = queue.QueueName;
|
||||
consumer = new EventingBasicConsumer(channel);
|
||||
consumer.Received += Recive;
|
||||
|
||||
//Debug.Debug.Log("进来两次????");
|
||||
//只会同时接收prefetchCount 个数据
|
||||
channel.BasicQos(0, prefetchCount, false);
|
||||
channel.BasicConsume(reply_queue, true, consumer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// rpc 返回值队列
|
||||
/// </summary>
|
||||
private string reply_queue;
|
||||
|
||||
/// <summary>
|
||||
/// 接收到消息
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <param name="msg"></param>
|
||||
private void Recive(IBasicConsumer model, BasicDeliverEventArgs msg) {
|
||||
//Console.WriteLine($"MrWU.RpcClient.Recive\t{DateTime.Now:hh:mm:ss.fffffff}\t{msg?.BasicProperties?.CorrelationId}");
|
||||
//ThreadPool.QueueUserWorkItem(HandleRecvMsgThreadPoolMethod, msg);
|
||||
Task.Factory.StartNew(
|
||||
() => {
|
||||
// Debug.Debug.Log("Rpc接收到消息:" + );
|
||||
if (msg.BasicProperties == null)
|
||||
return;
|
||||
|
||||
//Debug.Debug.Log("RPC 接收到消息:" + msg.BasicProperties.CorrelationId);
|
||||
|
||||
if (!respQueue.TryAdd(msg.BasicProperties.CorrelationId, Encoding.UTF8.GetString(msg.Body))) {
|
||||
Debug.Debug.Error("RPC发送消息失败!" + msg.BasicProperties.CorrelationId);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/*
|
||||
if (msg?.BasicProperties != null) {
|
||||
var callbackResult = Encoding.UTF8.GetString(msg.Body);
|
||||
//Debug.Debug.Log("RPC 接收到消息:" + msg.BasicProperties.CorrelationId);
|
||||
if(AsyncTaskList.TryGetValue(msg.BasicProperties.CorrelationId, out var tuple)) {
|
||||
tuple.Item3.TrySetResult(callbackResult);
|
||||
} else if(!respQueue.TryAdd(msg.BasicProperties.CorrelationId, callbackResult)) {
|
||||
Debug.Debug.Error("RPC发送消息失败!" + msg.BasicProperties.CorrelationId);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取结果
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private string GetReplyResult(string corrlationId, int waitTime = 3000) {
|
||||
DateTime outtime = DateTime.Now.AddMilliseconds(waitTime);
|
||||
//while (true) {
|
||||
// //Debug.Debug.Log("处理处理接收消息:" + Thread.CurrentThread.ManagedThreadId);
|
||||
// if (respQueue.ContainsKey(corrlationId)) {
|
||||
// string msg = null;
|
||||
// if (respQueue.TryGetValue(corrlationId, out msg)) {
|
||||
// return msg;
|
||||
// } else {
|
||||
// Debug.Debug.Error("RPC消息竟被别人取走??");
|
||||
// return null;
|
||||
// }
|
||||
// } else {
|
||||
// Console.WriteLine($"MrWU.RpcClient.GetReplyResult for: {corrlationId},\t{}");
|
||||
// }
|
||||
// if (DateTime.Now > outtime)
|
||||
// return null;
|
||||
// Thread.Sleep(10);
|
||||
//}
|
||||
while(true) {
|
||||
//Debug.Debug.Log("处理处理接收消息:" + Thread.CurrentThread.ManagedThreadId);
|
||||
if(respQueue.TryRemove(corrlationId, out var msg)) {
|
||||
return msg;
|
||||
} else {
|
||||
}
|
||||
if(DateTime.Now > outtime)
|
||||
return null;
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// call 方法
|
||||
/// </summary>
|
||||
/// <param name="exchange">交换机</param>
|
||||
/// <param name="routingkey">路由键</param>
|
||||
/// <param name="message">消息</param>
|
||||
/// <param name="waitTime">超时时间 毫秒值</param>
|
||||
public string Call(string exchange, string routingkey, string message, int waitTime = 3000) {
|
||||
// Debug.Debug.Log("call : " + reply_queue);
|
||||
string correlationId = MessagePool.GetId();// Guid.NewGuid().ToString();
|
||||
try {
|
||||
lock(lockObj) {
|
||||
if(channel == null || channel.IsClosed) {
|
||||
CreateChannel();
|
||||
}
|
||||
}
|
||||
IBasicProperties properties = channel.CreateBasicProperties();
|
||||
properties.ReplyTo = reply_queue;
|
||||
properties.CorrelationId = correlationId;
|
||||
|
||||
//Debug.Debug.Log("rpcCall :" + correlationId);
|
||||
channel.BasicPublish(exchange, routingkey, properties, Encoding.UTF8.GetBytes(message));
|
||||
var ret = GetReplyResult(correlationId, waitTime);
|
||||
return ret;
|
||||
|
||||
} catch(Exception e) {
|
||||
Debug.Debug.Error("Rpc出现错误!" + e.ToString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//ConcurrentDictionary<string, Tuple<DateTime, string, TaskCompletionSource<string>>> AsyncTaskList { get; } = new ConcurrentDictionary<string, Tuple<DateTime, string, TaskCompletionSource<string>>>();
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// 异步Callback
|
||||
/// </summary>
|
||||
/// <param name="exchange"></param>
|
||||
/// <param name="routingkey"></param>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="waitTime"></param>
|
||||
/// <returns></returns>
|
||||
public Task<string> AsyncCall(string exchange, string routingkey, string message, int waitTime = 3000) {
|
||||
var correlationId = Guid.NewGuid().ToString();
|
||||
var taskSrc = new TaskCompletionSource<string>();
|
||||
try {
|
||||
lock(lockObj) {
|
||||
if(channel == null || channel.IsClosed) {
|
||||
CreateChannel();
|
||||
}
|
||||
}
|
||||
var properties = channel.CreateBasicProperties();
|
||||
properties.ReplyTo = reply_queue;
|
||||
properties.CorrelationId = correlationId;
|
||||
channel.BasicPublish(exchange, routingkey, properties, Encoding.UTF8.GetBytes(message));
|
||||
AsyncTaskList.TryAdd(correlationId, new Tuple<DateTime, string, TaskCompletionSource<string>>(DateTime.Now.AddMilliseconds(waitTime), message, taskSrc));
|
||||
} catch(Exception ex) {
|
||||
Debug.Debug.Error("Rpc出现错误!" + ex.ToString());
|
||||
taskSrc.TrySetException(new Exception("AsyncCall Rpc出现错误", ex));
|
||||
}
|
||||
return taskSrc.Task;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
Timer CallBackTimeOutCheckTimer { get; }
|
||||
|
||||
|
||||
void CallBackTimeoutCheck(object obj) {
|
||||
var now = DateTime.Now;
|
||||
var outdateItems = AsyncTaskList.Where(p => p.Value.Item1 < now).ToArray();
|
||||
if(AsyncTaskList.Count>0 && outdateItems.Length == AsyncTaskList.Count) {
|
||||
foreach(var item in outdateItems) {
|
||||
if(AsyncTaskList.TryRemove(item.Key, out var tuple)) {
|
||||
//tuple.Item3.TrySetException(new TimeoutException("超时检查: " + tuple.Item2));
|
||||
tuple.Item3.TrySetResult(string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
11
MrWu/RabbitMQ/分析.txt
Normal file
11
MrWu/RabbitMQ/分析.txt
Normal file
@ -0,0 +1,11 @@
|
||||
消息丢失的原因
|
||||
1.网络中断
|
||||
2.由于队列的限制,消息被拒收
|
||||
3.消息没有路由匹配
|
||||
4.由于队列的限制,消息被丢弃
|
||||
5.由于消息未被及时消费,消息过期被丢弃
|
||||
|
||||
获取到消息丢失
|
||||
1.网络中断
|
||||
执行投递消息时引发错误
|
||||
2.触发basic.return事件
|
||||
94
MrWu/ReadMe.txt
Normal file
94
MrWu/ReadMe.txt
Normal file
@ -0,0 +1,94 @@
|
||||
命名规范
|
||||
|
||||
接口名以 大写字母 I 开头
|
||||
|
||||
类名大写字母开头
|
||||
私有类以下划线加大写字母开头
|
||||
|
||||
公开方法名(public)以大写字母开头
|
||||
|
||||
非公开方法名以下划线在大写字母开头
|
||||
|
||||
字段(常量除外) 都是私有的 并且以下划线加小写字母开头
|
||||
|
||||
属性 都是公开的 并且以小写字母开头
|
||||
|
||||
常量 私有以下划线加大写字母开头
|
||||
|
||||
常量 公开以大写字母开头
|
||||
|
||||
|
||||
设计模式
|
||||
|
||||
以下是创建对象的模式
|
||||
|
||||
简单工厂方法
|
||||
public static Food CreateFood(string type){
|
||||
switch(type){
|
||||
case "type1":
|
||||
break;
|
||||
case "type2":
|
||||
break;
|
||||
}
|
||||
}
|
||||
简单工厂的缺点,当需要增加功能时,需要修改原有代码,违背了开闭原则(可用反射解决)。
|
||||
|
||||
工厂方法
|
||||
每次创建实体时,需要先创建相应的工厂,再创建实体。增加功能时,无需修改代码,增加新的工厂即可。
|
||||
|
||||
抽象工厂模式
|
||||
对工厂方法聚合, 工厂方法只能生产一类实体,抽象工厂把所有工厂聚合在一个类中。聚合在一起生产。
|
||||
|
||||
建造者模式
|
||||
工厂方法是创建一个单一实体。建造者模式则可以把单一实体聚合在一起,生产一个复杂对象。
|
||||
|
||||
原型模式
|
||||
原型模式是从原来的基础上拷贝一个来使用.Clone
|
||||
|
||||
以下属于结构模型
|
||||
|
||||
适配器模式
|
||||
在原来的基础上,增加一个接口转变参数,适配老的接口。
|
||||
|
||||
桥接模式
|
||||
让另外一个接口实现具体的功能
|
||||
比如Class1 中 A,B,C 三个接口, Class2 中 A,B,C同样有三个接口, Class1 中执有 Class2 的实例,调用Class1中的接口时,直接呼Class2的接口。
|
||||
这样实现方在 Class2 中. 好处是,当Class1 增加接口的时候,Class2 可以不作出修改。
|
||||
|
||||
装饰者模式
|
||||
理由继承增加功能,原来的功能不变, 可以有多层功能。
|
||||
|
||||
组合模式
|
||||
把类似的方法抽象出来, 用一个根节点管理。是树形结构的。
|
||||
|
||||
外观模式
|
||||
对外暴露一个接口,外部使用同一调用这个接口,在内部由其他类型协调合作处理接口的具体事务。相当于一个黑盒,
|
||||
|
||||
享元模式
|
||||
对象共享化, 把共享的部分放在内部(不会岁环境更改),会更改的地方使用参数传递。目的是减少对象的创建。字符串的驻留机制
|
||||
|
||||
代理模式
|
||||
接口调用时,调用代理的接口,代理再调用实例方法,代理持有实例, 当实例发生改变时,只需要跟换代理的实例。
|
||||
|
||||
|
||||
|
||||
模板方法
|
||||
抽象类中实现方法,在方法的中途调用抽象方法,等实例方法来填充
|
||||
|
||||
命令模式
|
||||
由命令 与 执行者组成 执行者去处理命令.
|
||||
|
||||
状态者模式:
|
||||
状态抽象为对象, 处理事务有状态实例来处理,如果发生状态改变,则创建新的状态实例。 优点是增加状态, 不需要改变原来代码
|
||||
|
||||
策略者模式:
|
||||
计算某个值时,使用不同策略得出不同结果
|
||||
|
||||
责任链模式:
|
||||
处理事务自己处理不了就往下一个事务处理者抛出,直到处理完毕
|
||||
|
||||
访问者模式:
|
||||
使数据外部可读,隐藏数据,不可修改
|
||||
|
||||
备忘录模式
|
||||
允许对数据副本保存,用于下次恢复
|
||||
340
MrWu/Time/TimeConvert.cs
Normal file
340
MrWu/Time/TimeConvert.cs
Normal file
@ -0,0 +1,340 @@
|
||||
/*
|
||||
* 作者:吴隆健
|
||||
* 日期: 2019-04-23
|
||||
* 时间: 13:22
|
||||
*
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace MrWu.Time {
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public enum TimeStyle {
|
||||
/// <summary>
|
||||
/// 年月日时分秒
|
||||
/// </summary>
|
||||
yyyyMMddhhmmss,
|
||||
/// <summary>
|
||||
/// 年月日时分秒毫秒
|
||||
/// </summary>
|
||||
yyyyMMddhhmmssffff,
|
||||
/// <summary>
|
||||
/// 年月日
|
||||
/// </summary>
|
||||
yyyyMMdd,
|
||||
/// <summary>
|
||||
/// 月日时分秒
|
||||
/// </summary>
|
||||
MMddhhmmss,
|
||||
/// <summary>
|
||||
/// 月日时分秒毫秒
|
||||
/// </summary>
|
||||
MMddhhmmssfff,
|
||||
/// <summary>
|
||||
/// 时分秒
|
||||
/// </summary>
|
||||
hhmmss,
|
||||
/// <summary>
|
||||
/// 毫秒值
|
||||
/// </summary>
|
||||
fff
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 自定义时间结构_为了结构体对齐
|
||||
/// </summary>
|
||||
public struct CustomTime {
|
||||
|
||||
/// <summary>
|
||||
/// 年份的时间基数
|
||||
/// </summary>
|
||||
public const int weight = 1000000;
|
||||
|
||||
/// <summary> 年 今年第几天*24 + 今天的小时
|
||||
/// 小时部分 = tm.year % 100 * 100 0000 + tm.DayOfYear* 1440 + tm.hour
|
||||
/// </summary>
|
||||
public int hours;
|
||||
|
||||
/// <summary>
|
||||
/// 分钟部分
|
||||
/// </summary>
|
||||
public byte minute;
|
||||
|
||||
/// <summary>
|
||||
/// 秒钟部分
|
||||
/// </summary>
|
||||
public byte second;
|
||||
|
||||
/// <summary>
|
||||
/// 毫秒部分
|
||||
/// </summary>
|
||||
public ushort millisecond;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="hours"></param>
|
||||
/// <param name="minute"></param>
|
||||
/// <param name="second"></param>
|
||||
/// <param name="millisecond"></param>
|
||||
public CustomTime(int hours, byte minute = 0, byte second = 0, ushort millisecond = 0) {
|
||||
this.hours = hours;
|
||||
this.minute = minute;
|
||||
this.second = second;
|
||||
this.millisecond = millisecond;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 哪年
|
||||
/// </summary>
|
||||
public int year {
|
||||
get {
|
||||
return hours / weight + 2000;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 这一天是这一年当中的第几天
|
||||
/// </summary>
|
||||
public int dayofyear {
|
||||
get {
|
||||
return hours % weight / 24;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 几月
|
||||
/// </summary>
|
||||
public int month {
|
||||
get {
|
||||
return TimeConvert.GetMonth(year, dayofyear);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 几号
|
||||
/// </summary>
|
||||
public int day {
|
||||
get {
|
||||
return TimeConvert.GetDay(year, dayofyear);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 几点
|
||||
/// </summary>
|
||||
public int hour {
|
||||
get {
|
||||
return hours % weight % 24;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 时间转换
|
||||
/// </summary>
|
||||
public static class TimeConvert {
|
||||
|
||||
/// <summary>
|
||||
/// 闰年个个月的天数
|
||||
/// </summary>
|
||||
private readonly static byte[] _leapMonthDay = new byte[] {
|
||||
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 普通年个个月的天数
|
||||
/// </summary>
|
||||
private readonly static byte[] _noleapMonthDay = new byte[] {
|
||||
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 一年中每月的天数,润年在2月+1天
|
||||
/// </summary>
|
||||
public static byte[] MonthDay(int year) {
|
||||
bool isleap = IsLeapYear(year);
|
||||
if (isleap)
|
||||
return _leapMonthDay;
|
||||
return _noleapMonthDay;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取这一天是这一年的几月
|
||||
/// </summary>
|
||||
/// <param name="year">哪年</param>
|
||||
/// <param name="dayofyear">这一年的第几天</param>
|
||||
/// <returns></returns>
|
||||
public static int GetMonth(int year, int dayofyear) {
|
||||
bool isleap = IsLeapYear(year);
|
||||
byte[] bts = MonthDay(year);
|
||||
|
||||
int allday = 0;
|
||||
int len = bts.Length;
|
||||
for (int i = 0; i < len; i++) {
|
||||
allday += bts[i];
|
||||
if (dayofyear <= allday)
|
||||
return i + 1;
|
||||
}
|
||||
throw new ArgumentOutOfRangeException("dayofyear < 1 or dayofyear > 365 or 366");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取这一天是这年的几日
|
||||
/// </summary>
|
||||
/// <param name="year"></param>
|
||||
/// <param name="dayofyear"></param>
|
||||
/// <returns></returns>
|
||||
public static int GetDay(int year, int dayofyear) {
|
||||
//Console.WriteLine(year + "," + dayofyear);
|
||||
|
||||
int month = GetMonth(year, dayofyear);
|
||||
byte[] bts = MonthDay(year);
|
||||
|
||||
int allday = 0;
|
||||
for (int i = 1; i < month; i++) {
|
||||
allday += bts[i - 1];
|
||||
}
|
||||
return dayofyear - allday;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 某年是否是闰年
|
||||
/// </summary>
|
||||
/// <param name="year"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsLeapYear(int year) {
|
||||
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 时间戳起始时间
|
||||
/// </summary>
|
||||
private static readonly DateTime _startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1, 0, 0, 0, 0));
|
||||
|
||||
/// <summary>
|
||||
/// 时间转13位时间戳
|
||||
/// </summary>
|
||||
/// <param name="time">时间</param>
|
||||
/// <param name="bflag">true 表示10位时间戳 false 表示13位时间戳</param>
|
||||
/// <returns>时间戳</returns>
|
||||
public static long DateTimeToTimeStamp(DateTime time, bool bflag = true) {
|
||||
TimeSpan ts = time - _startTime;
|
||||
if (bflag)
|
||||
return Convert.ToInt64(ts.TotalSeconds);
|
||||
else
|
||||
return Convert.ToInt64(ts.TotalMilliseconds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 时间戳转时间
|
||||
/// </summary>
|
||||
/// <param name="timeStamp">时间戳字符串</param>
|
||||
/// <returns>时间</returns>
|
||||
public static DateTime TimeStampToDateTime(string timeStamp) {
|
||||
|
||||
if (timeStamp.Length == 10)
|
||||
timeStamp += "0000000";
|
||||
else if (timeStamp.Length == 13)
|
||||
timeStamp += "0000";
|
||||
|
||||
long ltime = long.Parse(timeStamp);
|
||||
|
||||
//Console.WriteLine(ltime);
|
||||
|
||||
TimeSpan toNow = new TimeSpan(ltime);
|
||||
|
||||
return _startTime.Add(toNow);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 自定义时间格式转DateTime
|
||||
/// </summary>
|
||||
/// <param name="ct">自定义时间格式</param>
|
||||
/// <returns></returns>
|
||||
public static DateTime CustomTimeToDateTime(CustomTime ct) {
|
||||
int year = ct.year;
|
||||
int month = ct.month;
|
||||
int day = ct.day;
|
||||
int hour = ct.hour;
|
||||
return new DateTime(year, month, day, hour, ct.minute, ct.second, ct.millisecond);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 时间 转自定义时间格式
|
||||
/// </summary>
|
||||
/// <param name="time">时间</param>
|
||||
/// <returns></returns>
|
||||
public static CustomTime DateTimeToCustomTime(DateTime time) {
|
||||
int year = time.Year;
|
||||
int dayofyear = time.DayOfYear;
|
||||
int hour = time.Hour;
|
||||
byte minute = (byte)time.Minute;
|
||||
byte second = (byte)time.Second;
|
||||
ushort millisecond = (ushort)time.Millisecond;
|
||||
|
||||
hour = year % 100 * CustomTime.weight + dayofyear * 24 + hour;
|
||||
return new CustomTime(hour, minute, second, millisecond);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 时间转字符串
|
||||
/// </summary>
|
||||
/// <param name="time">时间</param>
|
||||
/// <param name="style">字符串格式</param>
|
||||
/// <param name="dateChar">年月日以什么字符串链接</param>
|
||||
/// <param name="timeChar">时分秒以什么字符串链接</param>
|
||||
/// <param name="datetimelink">日期部分与时间部分以什么字符链接</param>
|
||||
/// <returns></returns>
|
||||
public static string TimeToString(this DateTime time, TimeStyle style = TimeStyle.yyyyMMddhhmmss, string dateChar = "-", string timeChar = ":", string datetimelink = " ") {
|
||||
string format = string.Empty;
|
||||
string dateCharlink = string.Empty;
|
||||
if (dateChar != string.Empty)
|
||||
dateCharlink += dateChar;
|
||||
string timeCharlink = string.Empty;
|
||||
if (timeChar != string.Empty)
|
||||
timeCharlink += timeChar;
|
||||
string dtlink = string.Empty;
|
||||
if (datetimelink != string.Empty)
|
||||
dtlink += datetimelink;
|
||||
|
||||
switch (style) {
|
||||
case TimeStyle.yyyyMMddhhmmssffff:
|
||||
format = string.Format("yyyy{0}MM{0}dd{2}HH{1}mm{1}ss{1}fff", dateCharlink, timeCharlink, dtlink);
|
||||
break;
|
||||
case TimeStyle.yyyyMMddhhmmss:
|
||||
format = string.Format("yyyy{0}MM{0}dd{2}HH{1}mm{1}ss", dateCharlink, timeCharlink, dtlink);
|
||||
break;
|
||||
case TimeStyle.yyyyMMdd:
|
||||
format = string.Format("yyyy{0}MM{0}dd", dateCharlink);
|
||||
break;
|
||||
case TimeStyle.MMddhhmmss:
|
||||
format = string.Format("MM{0}dd{1}HH{2}mm{2}ss", dateCharlink, dtlink, timeCharlink);
|
||||
break;
|
||||
case TimeStyle.MMddhhmmssfff:
|
||||
format = string.Format("MM{0}dd{1}HH{2}mm{2}ss{2}fff", dateCharlink, dtlink, timeCharlink);
|
||||
break;
|
||||
case TimeStyle.hhmmss:
|
||||
format = string.Format("HH{0}mm{0}ss", timeCharlink);
|
||||
break;
|
||||
case TimeStyle.fff:
|
||||
format = string.Format("fff");
|
||||
break;
|
||||
}
|
||||
return time.ToString(format);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取到第二天的时间差
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static TimeSpan GetTomorrowDifference()
|
||||
{
|
||||
var tom = DateTime.Now.Date.AddDays(1);
|
||||
return tom - DateTime.Now;
|
||||
}
|
||||
}
|
||||
}
|
||||
75
MrWu/core/Enumerator.cs
Normal file
75
MrWu/core/Enumerator.cs
Normal file
@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MrWu.core
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public struct ArrayEnumerator<T> : IEnumerator<T>, IEnumerator
|
||||
{
|
||||
private T[] array;
|
||||
|
||||
private int index;
|
||||
|
||||
private T current;
|
||||
|
||||
/// <summary>
|
||||
/// 迭代器
|
||||
/// </summary>
|
||||
/// <param name="array"></param>
|
||||
public ArrayEnumerator(T[] array) {
|
||||
this.array = array;
|
||||
index = 0;
|
||||
current = default(T);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 下一个
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool MoveNext() {
|
||||
if (index < array.Length) {
|
||||
current = array[index];
|
||||
index++;
|
||||
return true;
|
||||
}
|
||||
return MoveNextRare();
|
||||
}
|
||||
|
||||
private bool MoveNextRare() {
|
||||
index = array.Length + 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前的值
|
||||
/// </summary>
|
||||
public T Current {
|
||||
get {
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
||||
Object IEnumerator.Current {
|
||||
get {
|
||||
return Current;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public void Dispose() { }
|
||||
|
||||
/// <summary>
|
||||
/// 重置
|
||||
/// </summary>
|
||||
public void Reset() {
|
||||
index = 0;
|
||||
current = default(T);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user