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:
2026-07-07 12:02:15 +08:00
commit e9616125ce
958 changed files with 158203 additions and 0 deletions

686
MrWu/Basic/BaseFun.cs Normal file
View 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;
}
}
}

View 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

View 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
View 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
View File

@ -0,0 +1,7 @@
namespace MrWu.DB
{
public static class DbConst
{
public const string RowsAffected = "RowsAffected";
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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();
}
}
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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>

View 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
View 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
View 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

View 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;
}
}
}

View 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

View 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
View 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
View File

@ -0,0 +1,11 @@
消息丢失的原因
1.网络中断
2.由于队列的限制,消息被拒收
3.消息没有路由匹配
4.由于队列的限制,消息被丢弃
5.由于消息未被及时消费,消息过期被丢弃
获取到消息丢失
1.网络中断
执行投递消息时引发错误
2.触发basic.return事件

94
MrWu/ReadMe.txt Normal file
View 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
View 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
View 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);
}
}
}