diff --git a/JiShe.CollectBus.Common/Consts/RegexConst.cs b/JiShe.CollectBus.Common/Consts/RegexConst.cs new file mode 100644 index 0000000..a68e98b --- /dev/null +++ b/JiShe.CollectBus.Common/Consts/RegexConst.cs @@ -0,0 +1,23 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Text; + +namespace JiShe.CollectBus.Common.Consts +{ + public class RegexConst + { + public const string PhoneNumber = "1[3|4|5|7|8|9][0-9]{9}"; + + public const string IsNumeric = @"^[+-]?\d+[.]?\d*$"; + + public const string Ip = @"^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$"; + + public const string Email = @"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$"; + + public const string Url = @"^(ht|f)tp(s?)\:\/\/[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(:(0-9)*)*(\/?)([a-zA-Z0-9\-\.\?\,\'\/\\\+&%\$#_]*)?$"; + + public const string Date = @"(\d{4})-(\d{1,2})-(\d{1,2})"; + + public const string ZipCode = @"^\d{6}$"; + } +} diff --git a/JiShe.CollectBus.Common/Enums/MaskTypeEnum.cs b/JiShe.CollectBus.Common/Enums/MaskTypeEnum.cs new file mode 100644 index 0000000..44b1187 --- /dev/null +++ b/JiShe.CollectBus.Common/Enums/MaskTypeEnum.cs @@ -0,0 +1,19 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Text; + +namespace JiShe.CollectBus.Common.Enums +{ + public enum MaskTypeEnum + { + /// + /// Masks all characters within the masking region, regardless of type. + /// + All, + + /// + /// Masks only alphabetic and numeric characters within the masking region. + /// + AlphaNumericOnly, + } +} diff --git a/JiShe.CollectBus.Common/Enums/SortEnum.cs b/JiShe.CollectBus.Common/Enums/SortEnum.cs new file mode 100644 index 0000000..b018c1c --- /dev/null +++ b/JiShe.CollectBus.Common/Enums/SortEnum.cs @@ -0,0 +1,12 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Text; + +namespace JiShe.CollectBus.Common.Enums +{ + public enum SortEnum + { + Asc, + Desc + } +} diff --git a/JiShe.CollectBus.Common/Extensions/CollectionExtensions.cs b/JiShe.CollectBus.Common/Extensions/CollectionExtensions.cs new file mode 100644 index 0000000..6d493a4 --- /dev/null +++ b/JiShe.CollectBus.Common/Extensions/CollectionExtensions.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using JetBrains.Annotations; + +namespace JiShe.CollectBus.Common.Extensions +{ + /// + /// Extension methods for Collections. + /// + public static class CollectionExtensions + { + /// + /// Checks whatever given collection object is null or has no item. + /// + [Description("检查给定的集合对象是否为null或没有项目")] + public static bool IsNullOrEmpty([CanBeNull] this ICollection source) + { + return source == null || source.Count <= 0; + } + + /// + /// Adds an item to the collection if it's not already in the collection. + /// + /// Collection + /// Item to check and add + /// Type of the items in the collection + /// Returns True if added, returns False if not. + [Description("项不在集合中则将其添加到集合中")] + public static bool AddIfNotContains([NotNull] this ICollection source, T item) + { + if (source == null) + { + throw new ArgumentNullException("source"); + } + + if (source.Contains(item)) + { + return false; + } + + source.Add(item); + return true; + } + + /// + /// Adds items to the collection which are not already in the collection. + /// + /// The collection + /// Item to check and add + /// Type of the items in the collection + /// Returns the added items. + public static IEnumerable AddIfNotContains([NotNull] this ICollection source, IEnumerable items) + { + var addedItems = new List(); + + foreach (var item in items) + { + if (source.Contains(item)) + { + continue; + } + + source.Add(item); + addedItems.Add(item); + } + + return addedItems; + } + + /// + /// Adds an item to the collection if it's not already in the collection based on the given . + /// + /// The collection + /// The condition to decide if the item is already in the collection + /// A factory that returns the item + /// Type of the items in the collection + /// Returns True if added, returns False if not. + public static bool AddIfNotContains([NotNull] this ICollection source, [NotNull] Func predicate, [NotNull] Func itemFactory) + { + if (source.Any(predicate)) + { + return false; + } + + source.Add(itemFactory()); + return true; + } + + /// + /// Removes all items from the collection those satisfy the given . + /// + /// Type of the items in the collection + /// The collection + /// The condition to remove the items + /// List of removed items + [Description("从集合中移除满足给定条件的所有项")] + public static IList RemoveAll([NotNull] this ICollection source, Func predicate) + { + var items = source.Where(predicate).ToList(); + + foreach (var item in items) + { + source.Remove(item); + } + + return items; + } + + /// + /// Removes all items from the collection those satisfy the given . + /// + /// Type of the items in the collection + /// The collection + /// Items to be removed from the list + public static void RemoveAll([NotNull] this ICollection source, IEnumerable items) + { + foreach (var item in items) + { + source.Remove(item); + } + } + } +} \ No newline at end of file diff --git a/JiShe.CollectBus.Common/Extensions/ComparableExtensions.cs b/JiShe.CollectBus.Common/Extensions/ComparableExtensions.cs new file mode 100644 index 0000000..8516edc --- /dev/null +++ b/JiShe.CollectBus.Common/Extensions/ComparableExtensions.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel; + +namespace JiShe.CollectBus.Common.Extensions +{ + /// + /// Extension methods for . + /// + public static class ComparableExtensions + { + /// + /// Checks a value is between a minimum and maximum value. + /// + /// The value to be checked + /// Minimum (inclusive) value + /// Maximum (inclusive) value + [Description("检查值是否介于最小值和最大值之间")] + public static bool IsBetween(this T value, T minInclusiveValue, T maxInclusiveValue) where T : IComparable + { + return value.CompareTo(minInclusiveValue) >= 0 && value.CompareTo(maxInclusiveValue) <= 0; + } + } +} \ No newline at end of file diff --git a/JiShe.CollectBus.Common/Extensions/DataTableExtensions.cs b/JiShe.CollectBus.Common/Extensions/DataTableExtensions.cs new file mode 100644 index 0000000..6735fc7 --- /dev/null +++ b/JiShe.CollectBus.Common/Extensions/DataTableExtensions.cs @@ -0,0 +1,291 @@ +锘縰sing System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Linq; +using System.Xml.Linq; + +namespace JiShe.CollectBus.Common.Extensions +{ + public static class DataTableExtensions + { + + /// Converts to list. + /// + /// The dt. + /// + ///
+ ///
+ [Description("杞崲涓哄垪琛")] + public static IList ToList(this DataTable dt) where T : class + { + IList list = new List(); + foreach (DataRow dr in dt.Rows) + { + T t = Activator.CreateInstance(); + var props = typeof(T).GetProperties(); + foreach (var pro in props) + { + var tempName = pro.Name; + if (!dt.Columns.Contains(tempName)) continue; + if (!pro.CanWrite) continue; + var value = dr[tempName]; + if (value != DBNull.Value) + pro.SetValue(t, value, null); + } + + list.Add(t); + } + + return list; + } + + /// Converts to data table. + /// + /// The source. + /// + ///
+ ///
+ [Description("杞崲涓篋ataTable")] + public static DataTable ToDataTable(this ICollection source) + { + var props = typeof(T).GetProperties(); + var dt = new DataTable(); + dt.Columns.AddRange(props.Select(p => + new DataColumn(p.Name, + (p.PropertyType.IsGenericType) && (p.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) + ? p.PropertyType.GetGenericArguments()[0] + : p.PropertyType)).ToArray()); + + for (var i = 0; i < source.Count; i++) + { + var tempList = new ArrayList(); + foreach (var obj in props.Select(pi => pi.GetValue(source.ElementAt(i), null))) tempList.Add(obj); + var array = tempList.ToArray(); + dt.LoadDataRow(array, true); + } + + return dt; + } + + /// Converts to xml. + /// The dt. + /// Name of the root. + /// + ///
+ ///
+ [Description("杞崲涓篨ml")] + public static XDocument ToXml(this DataTable dt, string rootName) + { + var xdoc = new XDocument + { + Declaration = new XDeclaration("1.0", "utf-8", "") + }; + xdoc.Add(new XElement(rootName)); + foreach (DataRow row in dt.Rows) + { + var element = new XElement(dt.TableName); + foreach (DataColumn col in dt.Columns) + { + element.Add(new XElement(col.ColumnName, row[col].ToString().Trim(' '))); + } + xdoc.Root?.Add(element); + } + + return xdoc; + } + + /// + /// "SELECT DISTINCT" over a DataTable + /// + /// Input DataTable + /// Field to select (distinct) + /// + [Description("鍦―ataTable涓'SELECT DISTINCT'")] + public static DataTable SelectDistinct(this DataTable sourceTable, string fieldName) + { + return SelectDistinct(sourceTable, fieldName, string.Empty); + } + + /// + /// "SELECT DISTINCT" over a DataTable + /// + /// Input DataTable + /// Fields to select (distinct) Split ',' + /// Optional filter to be applied to the selection + /// + public static DataTable SelectDistinct(this DataTable sourceTable, string fieldNames, string filter) + { + var dt = new DataTable(); + var arrFieldNames = fieldNames.Replace(" ", "").Split(','); + foreach (var s in arrFieldNames) + { + if (sourceTable.Columns.Contains(s)) + dt.Columns.Add(s, sourceTable.Columns[s].DataType); + else + throw new Exception($"The column {s} does not exist."); + } + + object[] lastValues = null; + foreach (DataRow dr in sourceTable.Select(filter, fieldNames)) + { + var newValues = GetRowFields(dr, arrFieldNames); + if (lastValues == null || !(ObjectComparison(lastValues, newValues))) + { + lastValues = newValues; + dt.Rows.Add(lastValues); + } + } + + return dt; + } + + /// Selects the rows. + /// The dt. + /// The where expression. + /// The order by expression. + /// + ///
+ ///
+ [Description("鏌ヨ鍏ㄩ儴琛")] + public static DataTable SelectRows(this DataTable dt, string whereExpression, string orderByExpression) + { + dt.DefaultView.RowFilter = whereExpression; + dt.DefaultView.Sort = orderByExpression; + return dt.DefaultView.ToTable(); + } + + /// take any DataTable and remove duplicate rows based on any column. + /// The dt. + /// Name of the key col. + /// + ///
+ ///
+ [Description("鍒犻櫎閲嶅鐨勮")] + public static DataTable Duplicate(this DataTable dt, string keyColName) + { + var tblOut = dt.Clone(); + foreach (DataRow row in dt.Rows) + { + var found = false; + var caseIdToTest = row[keyColName].ToString(); + foreach (DataRow row2 in tblOut.Rows) + { + if (row2[keyColName].ToString() == caseIdToTest) + { + found = true; + break; + } + } + if (!found) + tblOut.ImportRow(row); + } + return tblOut; + } + + /// Checks if two DataTable objects have the same content. + /// The this data table. + /// The other data table. + /// + ///
+ ///
+ [Description("妫鏌ヤ袱涓狣ataTable瀵硅薄鏄惁鍏锋湁鐩稿悓鐨勫唴瀹")] + public static bool EqualsByContent(this DataTable thisDataTable, DataTable otherDataTable) + { + // Compare row count. + if (thisDataTable.Rows.Count != otherDataTable.Rows.Count) + { + return false; + } + + // Compare column count. + if (thisDataTable.Columns.Count != otherDataTable.Columns.Count) + { + return false; + } + + // Compare data in each cell of each row. + for (int i = 0; i < thisDataTable.Rows.Count; i++) + { + for (int j = 0; j < thisDataTable.Columns.Count; j++) + { + if (!thisDataTable.Rows[i][j].Equals(otherDataTable.Rows[i][j])) + { + return false; + } + } + } + + // The two DataTables contain the same data. + return true; + } + + /// Renames the column. + /// The dt. + /// The old name. + /// The new name. + [Description("閲嶅懡鍚嶅垪")] + public static void RenameColumn(this DataTable dt, string oldName, string newName) + { + if (dt != null && !string.IsNullOrEmpty(oldName) && !string.IsNullOrEmpty(newName) && oldName != newName) + { + int idx = dt.Columns.IndexOf(oldName); + dt.Columns[idx].ColumnName = newName; + dt.AcceptChanges(); + } + } + + /// Removes the column. + /// The dt. + /// Name of the column. + [Description("鍒犻櫎鍒")] + public static void RemoveColumn(this DataTable dt, string columnName) + { + if (dt != null && !string.IsNullOrEmpty(columnName) && dt.Columns.IndexOf(columnName) >= 0) + { + int idx = dt.Columns.IndexOf(columnName); + dt.Columns.RemoveAt(idx); + dt.AcceptChanges(); + } + } + + private static object[] GetRowFields(DataRow dr, string[] arrFieldNames) + { + if (arrFieldNames.Length == 1) + return new object[] { dr[arrFieldNames[0]] }; + var itemArray = new ArrayList(); + foreach (var field in arrFieldNames) + itemArray.Add(dr[field]); + + return itemArray.ToArray(); + } + + private static bool ObjectComparison(object a, object b) + { + if (a == DBNull.Value && b == DBNull.Value) // both are DBNull.Value + return true; + if (a == DBNull.Value || b == DBNull.Value) // only one is DBNull.Value + return false; + return a.Equals(b); // value type standard comparison + } + + private static bool ObjectComparison(IReadOnlyList a, IReadOnlyList b) + { + var retValue = true; + + if (a.Count == b.Count) + for (var i = 0; i < a.Count; i++) + { + if (!ObjectComparison(a[i], b[i])) + { + retValue = false; + break; + } + retValue = true; + } + + return retValue; + } + } +} diff --git a/JiShe.CollectBus.Common/Extensions/DateTimeExtensions.cs b/JiShe.CollectBus.Common/Extensions/DateTimeExtensions.cs new file mode 100644 index 0000000..b12778f --- /dev/null +++ b/JiShe.CollectBus.Common/Extensions/DateTimeExtensions.cs @@ -0,0 +1,171 @@ +锘縰sing System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; + +namespace JiShe.CollectBus.Common.Extensions +{ + public static class DateTimeExtensions + { + /// + /// Converts a DateTime to a Unix Timestamp + /// + /// This DateTime + /// + [Description("灏嗘棩鏈熸椂闂磋浆鎹负Unix鏃堕棿鎴")] + public static double ToUnixTimestamp(this DateTime target) + { + var origin = new DateTime(1970, 1, 1, 0, 0, 0, 0); + var diff = target - origin; + return Math.Floor(diff.TotalSeconds); + } + + /// + /// Converts a Unix Timestamp in to a DateTime + /// + /// This Unix Timestamp + /// + [Description("灏哢nix鏃堕棿鎴宠浆鎹负鏃ユ湡鏃堕棿")] + public static DateTime FromUnixTimestamp(this double unixTime) + { + var epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0); + return epoch.AddSeconds(unixTime); + } + + /// + /// Gets the value of the End of the day (23:59) + /// + /// + /// + [Description("鑾峰彇涓澶╃粨鏉熺殑鍊硷紙23:59锛")] + public static DateTime ToDayEnd(this DateTime target) + { + return target.Date.AddDays(1).AddMilliseconds(-1); + } + + /// + /// Gets the First Date of the week for the specified date + /// + /// this DateTime + /// The Start Day of the Week (ie, Sunday/Monday) + /// The First Date of the week + [Description("鑾峰彇鎸囧畾鏃ユ湡鐨勬槦鏈熺殑绗竴涓棩鏈")] + public static DateTime StartOfWeek(this DateTime dt, DayOfWeek startOfWeek) + { + var diff = dt.DayOfWeek - startOfWeek; + + if (diff < 0) + diff += 7; + + return dt.AddDays(-1 * diff).Date; + } + + /// + /// Returns all the days of a month. + /// + /// The year. + /// The month. + /// + [Description("鑾峰彇涓涓湀鐨勬墍鏈夋棩鏈")] + public static IEnumerable DaysOfMonth(int year, int month) + { + return Enumerable.Range(0, DateTime.DaysInMonth(year, month)) + .Select(day => new DateTime(year, month, day + 1)); + } + + /// + /// Determines the Nth instance of a Date's DayOfWeek in a month + /// + /// + /// 11/29/2011 would return 5, because it is the 5th Tuesday of each month + [Description("鑾峰彇褰撳墠鏃ユ湡鍦ㄤ竴涓湀鐨勭鍑犱釜鏄熸湡")] + public static int WeekDayInstanceOfMonth(this DateTime dateTime) + { + var y = 0; + return DaysOfMonth(dateTime.Year, dateTime.Month) + .Where(date => dateTime.DayOfWeek.Equals(date.DayOfWeek)) + .Select(x => new { n = ++y, date = x }) + .Where(x => x.date.Equals(new DateTime(dateTime.Year, dateTime.Month, dateTime.Day))) + .Select(x => x.n).FirstOrDefault(); + } + + /// + /// Gets the total days in a month + /// + /// The date time. + /// + [Description("鑾峰彇涓涓湀鍐呯殑鎬诲ぉ鏁")] + public static int TotalDaysInMonth(this DateTime dateTime) + { + return DaysOfMonth(dateTime.Year, dateTime.Month).Count(); + } + + /// + /// Get the first day in a month + /// + /// The date time. + /// + [Description("鑾峰彇涓涓湀鍐呯殑绗竴澶")] + public static DateTime FirstInMonth(this DateTime dateTime) + { + return DateTime.Now.AddDays(1 - DateTime.Now.Day); + } + + /// + /// Get the Last Day in a Month 23:59:59 + /// + /// + /// + [Description("鑾峰彇涓涓湀鍐呯殑鏈鍚庝竴澶 23:59:59")] + public static DateTime LastInMonth(this DateTime dateTime) + { + return DateTime.Now.AddDays(1 - DateTime.Now.Day).Date.AddMonths(1).AddSeconds(-1); + } + + /// + /// Takes any date and returns it's value as an Unspecified DateTime + /// + /// + /// + [Description("鑾峰彇Unspecified鏃ユ湡")] + public static DateTime ToDateTimeUnspecified(this DateTime date) + { + if (date.Kind == DateTimeKind.Unspecified) + { + return date; + } + + return new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, DateTimeKind.Unspecified); + } + + /// + /// Trims the milliseconds off of a datetime + /// + /// + /// + [Description("灏嗘棩鏈熸椂闂寸缉鐭绉")] + public static DateTime TrimMilliseconds(this DateTime date) + { + return new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, date.Kind); + } + + /// + /// Clears the time. + /// + /// The date time. + /// + [Description("娓呴櫎鏃堕棿")] + public static DateTime ClearTime(this DateTime dateTime) + { + return dateTime.Subtract( + new TimeSpan( + 0, + dateTime.Hour, + dateTime.Minute, + dateTime.Second, + dateTime.Millisecond + ) + ); + } + } +} diff --git a/JiShe.CollectBus.Common/Extensions/DayOfWeekExtensions.cs b/JiShe.CollectBus.Common/Extensions/DayOfWeekExtensions.cs new file mode 100644 index 0000000..8b3b9ca --- /dev/null +++ b/JiShe.CollectBus.Common/Extensions/DayOfWeekExtensions.cs @@ -0,0 +1,89 @@ +锘縰sing System; +using System.ComponentModel; +using System.Linq; + +namespace JiShe.CollectBus.Common.Extensions +{ + /// + /// Extension methods for . + /// + public static class DayOfWeekExtensions + { + /// + /// Check if a given value is weekend. + /// + [Description("妫鏌ョ粰瀹氬兼槸鍚︿负鍛ㄦ湯")] + public static bool IsWeekend(this DayOfWeek dayOfWeek) + { + return dayOfWeek.IsIn(DayOfWeek.Saturday, DayOfWeek.Sunday); + } + + /// + /// Check if a given value is weekday. + /// + [Description("妫鏌ョ粰瀹氬兼槸鍚︿负宸ヤ綔鏃")] + public static bool IsWeekday(this DayOfWeek dayOfWeek) + { + return dayOfWeek.IsIn(DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday); + } + + /// + /// Finds the NTH week day of a month. + /// + /// The day of week. + /// The year. + /// The month. + /// The nth instance. + /// Compensates for 4th and 5th DayOfWeek of Month + [Description("鏌ユ壘涓涓湀鐨勭n鍛")] + public static DateTime FindNthWeekDayOfMonth(this DayOfWeek dayOfWeek, int year, int month, int n) + { + if (n < 1 || n > 5) + { + throw new ArgumentOutOfRangeException(nameof(n)); + } + + var y = 0; + + var daysOfMonth = DateTimeExtensions.DaysOfMonth(year, month); + + // compensate for "last DayOfWeek in month" + var totalInstances = dayOfWeek.TotalInstancesInMonth(year, month); + if (n == 5 && n > totalInstances) + n = 4; + + var foundDate = daysOfMonth + .Where(date => dayOfWeek.Equals(date.DayOfWeek)) + .OrderBy(date => date) + .Select(x => new { n = ++y, date = x }) + .Where(x => x.n.Equals(n)).Select(x => x.date).First(); //black magic wizardry + + return foundDate; + } + + /// + /// Finds the total number of instances of a specific DayOfWeek in a month. + /// + /// The day of week. + /// The year. + /// The month. + /// + [Description("鑾峰彇涓涓湀鍐呯壒瀹欴ayOfWeek鐨勫疄渚嬫绘暟")] + public static int TotalInstancesInMonth(this DayOfWeek dayOfWeek, int year, int month) + { + return DateTimeExtensions.DaysOfMonth(year, month).Count(date => dayOfWeek.Equals(date.DayOfWeek)); + } + + /// + /// Gets the total number of instances of a specific DayOfWeek in a month. + /// + /// The day of week. + /// The date in a month. + /// + [Description("鑾峰彇涓涓湀鍐呯壒瀹欴ayOfWeek鐨勫疄渚嬫绘暟")] + public static int TotalInstancesInMonth(this DayOfWeek dayOfWeek, DateTime dateTime) + { + return dayOfWeek.TotalInstancesInMonth(dateTime.Year, dateTime.Month); + } + } +} \ No newline at end of file diff --git a/JiShe.CollectBus.Common/Extensions/DecimalOrIntExtensions.cs b/JiShe.CollectBus.Common/Extensions/DecimalOrIntExtensions.cs new file mode 100644 index 0000000..4660ec7 --- /dev/null +++ b/JiShe.CollectBus.Common/Extensions/DecimalOrIntExtensions.cs @@ -0,0 +1,128 @@ +锘縰sing System.ComponentModel; +using System.Text.RegularExpressions; + +namespace JiShe.CollectBus.Common.Extensions +{ + public static class DecimalOrIntExtensions + { + /// Converts to chinese amount. + /// The number. + /// + ///
+ ///
+ [Description("鎹㈢畻鎴愪腑鏂囬噾棰")] + public static string ToChineseAmount(this decimal number) + { + return BuildChineseAmount(number); + } + + /// Converts to chinese amount. + /// The number. + /// + ///
+ ///
+ public static string ToChineseAmount(this int number) + { + return BuildChineseAmount(number); + } + + /// Determines whether the specified minimum is between. + /// The number. + /// The minimum. + /// The maximum. + /// + /// true if the specified minimum is between; otherwise, false. + [Description("鍒ゆ柇鍊兼槸鍚︿粙浜庝袱鑰呬箣闂")] + public static bool IsBetween(this decimal number, decimal min, decimal max) + { + return number >= min && number <= max; + } + + /// Determines whether the specified minimum is between. + /// The number. + /// The minimum. + /// The maximum. + /// + /// true if the specified minimum is between; otherwise, false. + public static bool IsBetween(this int number, int min, int max) + { + return number >= min && number <= max; + } + + /// Determines whether the specified minimum is between. + /// The number. + /// The minimum. + /// The maximum. + /// + /// true if the specified minimum is between; otherwise, false. + public static bool IsBetween(this float number, float min, float max) + { + return number >= min && number <= max; + } + + /// Determines whether the specified minimum is between. + /// The number. + /// The minimum. + /// The maximum. + /// + /// true if the specified minimum is between; otherwise, false. + public static bool IsBetween(this double number, double min, double max) + { + return number >= min && number <= max; + } + + /// Determines whether the specified minimum is between. + /// The number. + /// The minimum. + /// The maximum. + /// + /// true if the specified minimum is between; otherwise, false. + public static bool IsBetween(this decimal? number, decimal min, decimal max) + { + return number.HasValue && (number >= min && number <= max); + } + + /// Determines whether the specified minimum is between. + /// The number. + /// The minimum. + /// The maximum. + /// + /// true if the specified minimum is between; otherwise, false. + public static bool IsBetween(this int? number, int min, int max) + { + return number.HasValue && (number >= min && number <= max); + } + + /// Determines whether the specified minimum is between. + /// The number. + /// The minimum. + /// The maximum. + /// + /// true if the specified minimum is between; otherwise, false. + public static bool IsBetween(this float? number, float min, float max) + { + return number.HasValue && (number >= min && number <= max); + } + + /// Determines whether the specified minimum is between. + /// The number. + /// The minimum. + /// The maximum. + /// + /// true if the specified minimum is between; otherwise, false. + public static bool IsBetween(this double? number, double min, double max) + { + return number.HasValue && (number >= min && number <= max); + } + + private static string BuildChineseAmount(decimal number) + { + var s = number.ToString("#L#E#D#C#K#E#D#C#J#E#D#C#I#E#D#C#H#E#D#C#G#E#D#C#F#E#D#C#.0B0A"); + var d = Regex.Replace(s, + @"((?<=-|^)[^1-9]*)|((?'z'0)[0A-E]*((?=[1-9])|(?'-z'(?=[F-L\.]|$))))|((?'b'[F-L])(?'z'0)[0A-L]*((?=[1-9])|(?'-z'(?=[\.]|$))))", + "${b}${z}"); + var r = Regex.Replace(d, ".", m => "璐熷厓绌洪浂澹硅窗鍙佽倖浼嶉檰鏌掓崒鐜栫┖绌虹┖绌虹┖绌虹┖鍒嗚鎷句桨浠熶竾浜垮厗浜灀绉┌"[m.Value[0] - '-'].ToString()); + return r; + } + } +} \ No newline at end of file diff --git a/JiShe.CollectBus.Common/Extensions/DictionaryExtensions.cs b/JiShe.CollectBus.Common/Extensions/DictionaryExtensions.cs new file mode 100644 index 0000000..23f9af6 --- /dev/null +++ b/JiShe.CollectBus.Common/Extensions/DictionaryExtensions.cs @@ -0,0 +1,83 @@ +锘縰sing System; +using System.Collections.Generic; +using System.ComponentModel; + +namespace JiShe.CollectBus.Common.Extensions +{ + /// + /// Extension methods for Dictionary. + /// + public static class DictionaryExtensions + { + /// + /// This method is used to try to get a value in a dictionary if it does exists. + /// + /// Type of the value + /// The collection object + /// Key + /// Value of the key (or default value if key not exists) + /// True if key does exists in the dictionary + internal static bool TryGetValue(this IDictionary dictionary, string key, out T value) + { + object valueObj; + if (dictionary.TryGetValue(key, out valueObj) && valueObj is T) + { + value = (T)valueObj; + return true; + } + + value = default(T); + return false; + } + + /// + /// Gets a value from the dictionary with given key. Returns default value if can not find. + /// + /// Dictionary to check and get + /// Key to find the value + /// Type of the key + /// Type of the value + /// Value if found, default if can not found. + [Description("浠庡瓧鍏镐腑鑾峰彇鍏锋湁缁欏畾閿殑鍊硷紝濡傛灉鎵句笉鍒帮紝鍒欒繑鍥為粯璁ゅ")] + public static TValue GetOrDefault(this IDictionary dictionary, TKey key) + { + TValue obj; + return dictionary.TryGetValue(key, out obj) ? obj : default(TValue); + } + + /// + /// Gets a value from the dictionary with given key. Add value if can not find. + /// + /// Dictionary to check and get + /// Key to find the value + /// A factory method used to create the value if not found in the dictionary + /// Type of the key + /// Type of the value + /// Value if found, default if can not found. + [Description("浠庡瓧鍏镐腑鑾峰彇鍏锋湁缁欏畾閿殑鍊硷紝濡傛灉鎵句笉鍒帮紝鍒欐坊鍔")] + public static TValue GetOrAdd(this IDictionary dictionary, TKey key, Func factory) + { + TValue obj; + if (dictionary.TryGetValue(key, out obj)) + { + return obj; + } + + return dictionary[key] = factory(key); + } + + /// + /// Gets a value from the dictionary with given key. Add value if can not find. + /// + /// Dictionary to check and get + /// Key to find the value + /// A factory method used to create the value if not found in the dictionary + /// Type of the key + /// Type of the value + /// Value if found, default if can not found. + public static TValue GetOrAdd(this IDictionary dictionary, TKey key, Func factory) + { + return dictionary.GetOrAdd(key, k => factory()); + } + } +} \ No newline at end of file diff --git a/JiShe.CollectBus.Common/Extensions/EnumerableExtensions.cs b/JiShe.CollectBus.Common/Extensions/EnumerableExtensions.cs new file mode 100644 index 0000000..94e4ad1 --- /dev/null +++ b/JiShe.CollectBus.Common/Extensions/EnumerableExtensions.cs @@ -0,0 +1,68 @@ +锘縰sing System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; + +namespace JiShe.CollectBus.Common.Extensions +{ + /// + /// Extension methods for . + /// + public static class EnumerableExtensions + { + /// + /// Concatenates the members of a constructed collection of type System.String, using the specified separator between each member. + /// This is a shortcut for string.Join(...) + /// + /// A collection that contains the strings to concatenate. + /// The string to use as a separator. separator is included in the returned string only if values has more than one element. + /// A string that consists of the members of values delimited by the separator string. If values has no members, the method returns System.String.Empty. + [Description("Enumerable杞崲String")] + public static string JoinAsString(this IEnumerable source, string separator) + { + return string.Join(separator, source); + } + + /// + /// Concatenates the members of a collection, using the specified separator between each member. + /// This is a shortcut for string.Join(...) + /// + /// A collection that contains the objects to concatenate. + /// The string to use as a separator. separator is included in the returned string only if values has more than one element. + /// The type of the members of values. + /// A string that consists of the members of values delimited by the separator string. If values has no members, the method returns System.String.Empty. + public static string JoinAsString(this IEnumerable source, string separator) + { + return string.Join(separator, source); + } + + /// + /// Filters a by given predicate if given condition is true. + /// + /// Enumerable to apply filtering + /// A boolean value + /// Predicate to filter the enumerable + /// Filtered or not filtered enumerable based on + [Description("Enumerable绛涢")] + public static IEnumerable WhereIf(this IEnumerable source, bool condition, Func predicate) + { + return condition + ? source.Where(predicate) + : source; + } + + /// + /// Filters a by given predicate if given condition is true. + /// + /// Enumerable to apply filtering + /// A boolean value + /// Predicate to filter the enumerable + /// Filtered or not filtered enumerable based on + public static IEnumerable WhereIf(this IEnumerable source, bool condition, Func predicate) + { + return condition + ? source.Where(predicate) + : source; + } + } +} diff --git a/JiShe.CollectBus.Common/Extensions/EventHandlerExtensions.cs b/JiShe.CollectBus.Common/Extensions/EventHandlerExtensions.cs new file mode 100644 index 0000000..109e01f --- /dev/null +++ b/JiShe.CollectBus.Common/Extensions/EventHandlerExtensions.cs @@ -0,0 +1,56 @@ +using System; +using System.ComponentModel; + +namespace JiShe.CollectBus.Common.Extensions +{ + /// + /// Extension methods for . + /// + public static class EventHandlerExtensions + { + /// + /// Raises given event safely with given arguments. + /// + /// The event handler + /// Source of the event + [Description("使用给定参数安全引发给定事件")] + public static void InvokeSafely(this EventHandler eventHandler, object sender) + { + eventHandler.InvokeSafely(sender, EventArgs.Empty); + } + + /// + /// Raises given event safely with given arguments. + /// + /// The event handler + /// Source of the event + /// Event argument + public static void InvokeSafely(this EventHandler eventHandler, object sender, EventArgs e) + { + if (eventHandler == null) + { + return; + } + + eventHandler(sender, e); + } + + /// + /// Raises given event safely with given arguments. + /// + /// Type of the + /// The event handler + /// Source of the event + /// Event argument + public static void InvokeSafely(this EventHandler eventHandler, object sender, TEventArgs e) + where TEventArgs : EventArgs + { + if (eventHandler == null) + { + return; + } + + eventHandler(sender, e); + } + } +} \ No newline at end of file diff --git a/JiShe.CollectBus.Common/Extensions/ExceptionExtensions.cs b/JiShe.CollectBus.Common/Extensions/ExceptionExtensions.cs new file mode 100644 index 0000000..163edfb --- /dev/null +++ b/JiShe.CollectBus.Common/Extensions/ExceptionExtensions.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel; +using System.Runtime.ExceptionServices; + +namespace JiShe.CollectBus.Common.Extensions +{ + /// + /// Extension methods for class. + /// + public static class ExceptionExtensions + { + /// + /// Uses method to re-throws exception + /// while preserving stack trace. + /// + /// Exception to be re-thrown + [Description("重新引发异常")] + public static void ReThrow(this Exception exception) + { + ExceptionDispatchInfo.Capture(exception).Throw(); + } + } +} \ No newline at end of file diff --git a/JiShe.CollectBus.Common/Extensions/HttpResponseExtensions.cs b/JiShe.CollectBus.Common/Extensions/HttpResponseExtensions.cs new file mode 100644 index 0000000..d521d78 --- /dev/null +++ b/JiShe.CollectBus.Common/Extensions/HttpResponseExtensions.cs @@ -0,0 +1,33 @@ +锘縰sing System.ComponentModel; +using System.Net; +using System.Net.Http.Headers; +using System.Text; +using System.Web; + +namespace JiShe.CollectBus.Common.Extensions +{ + public static class HttpResponseExtensions + { + /// + /// Sets the cookie. + /// + /// The headers. + /// The cookie. + [Description("璁剧疆璇锋眰澶")] + public static void SetCookie(this HttpResponseHeaders headers, Cookie cookie) + { + var cookieBuilder = new StringBuilder(HttpUtility.UrlEncode(cookie.Name) + "=" + HttpUtility.UrlEncode(cookie.Value)); + if (cookie.HttpOnly) + { + cookieBuilder.Append("; HttpOnly"); + } + + if (cookie.Secure) + { + cookieBuilder.Append("; Secure"); + } + + headers.Add("Set-Cookie", cookieBuilder.ToString()); + } + } +} diff --git a/JiShe.CollectBus.Common/Extensions/ListExtensions.cs b/JiShe.CollectBus.Common/Extensions/ListExtensions.cs new file mode 100644 index 0000000..340cb26 --- /dev/null +++ b/JiShe.CollectBus.Common/Extensions/ListExtensions.cs @@ -0,0 +1,366 @@ +锘縰sing System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using JetBrains.Annotations; + +namespace JiShe.CollectBus.Common.Extensions +{ + /// + /// Extension methods for . + /// + public static class ListExtensions + { + /// + /// Inserts the range. + /// + /// + /// The source. + /// The index. + /// The items. + [Description("鎻掑叆澶氫釜")] + public static void InsertRange(this IList source, int index, IEnumerable items) + { + foreach (var item in items) + { + source.Insert(index++, item); + } + } + + /// + /// Finds the index. + /// + /// + /// The source. + /// The selector. + /// + [Description("鏌ユ壘绱㈠紩")] + public static int FindIndex(this IList source, Predicate selector) + { + for (var i = 0; i < source.Count; ++i) + { + if (selector(source[i])) + { + return i; + } + } + + return -1; + } + + /// + /// Adds the first. + /// + /// + /// The source. + /// The item. + [Description("鍦ㄥ紑澶存彃鍏")] + public static void AddFirst(this IList source, T item) + { + source.Insert(0, item); + } + + /// + /// Adds the last. + /// + /// + /// The source. + /// The item. + [Description("鍦ㄧ粨灏炬彃鍏")] + public static void AddLast(this IList source, T item) + { + source.Insert(source.Count, item); + } + + /// + /// Inserts the after. + /// + /// + /// The source. + /// The existing item. + /// The item. + [Description("鍦...涔嬪悗鎻掑叆")] + public static void InsertAfter(this IList source, T existingItem, T item) + { + var index = source.IndexOf(existingItem); + if (index < 0) + { + source.AddFirst(item); + return; + } + + source.Insert(index + 1, item); + } + + /// + /// Inserts the after. + /// + /// + /// The source. + /// The selector. + /// The item. + public static void InsertAfter(this IList source, Predicate selector, T item) + { + var index = source.FindIndex(selector); + if (index < 0) + { + source.AddFirst(item); + return; + } + + source.Insert(index + 1, item); + } + + /// + /// Inserts the before. + /// + /// + /// The source. + /// The existing item. + /// The item. + [Description("鍦...涔嬪墠鎻掑叆")] + public static void InsertBefore(this IList source, T existingItem, T item) + { + var index = source.IndexOf(existingItem); + if (index < 0) + { + source.AddLast(item); + return; + } + + source.Insert(index, item); + } + + /// + /// Inserts the before. + /// + /// + /// The source. + /// The selector. + /// The item. + public static void InsertBefore(this IList source, Predicate selector, T item) + { + var index = source.FindIndex(selector); + if (index < 0) + { + source.AddLast(item); + return; + } + + source.Insert(index, item); + } + + /// + /// Replaces the while. + /// + /// + /// The source. + /// The selector. + /// The item. + [Description("鏇挎崲")] + public static void ReplaceWhile(this IList source, Predicate selector, T item) + { + for (int i = 0; i < source.Count; i++) + { + if (selector(source[i])) + { + source[i] = item; + } + } + } + + /// + /// Replaces the while. + /// + /// + /// The source. + /// The selector. + /// The item factory. + public static void ReplaceWhile(this IList source, Predicate selector, Func itemFactory) + { + for (int i = 0; i < source.Count; i++) + { + var item = source[i]; + if (selector(item)) + { + source[i] = itemFactory(item); + } + } + } + + /// + /// Replaces the one. + /// + /// + /// The source. + /// The selector. + /// The item. + [Description("鏇挎崲")] + public static void ReplaceOne(this IList source, Predicate selector, T item) + { + for (int i = 0; i < source.Count; i++) + { + if (selector(source[i])) + { + source[i] = item; + return; + } + } + } + + /// + /// Replaces the one. + /// + /// + /// The source. + /// The selector. + /// The item factory. + public static void ReplaceOne(this IList source, Predicate selector, Func itemFactory) + { + for (int i = 0; i < source.Count; i++) + { + var item = source[i]; + if (selector(item)) + { + source[i] = itemFactory(item); + return; + } + } + } + + /// + /// Replaces the one. + /// + /// + /// The source. + /// The item. + /// The replace with. + public static void ReplaceOne(this IList source, T item, T replaceWith) + { + for (int i = 0; i < source.Count; i++) + { + if (Comparer.Default.Compare(source[i], item) == 0) + { + source[i] = replaceWith; + return; + } + } + } + + /// + /// Moves the item. + /// + /// + /// The source. + /// The selector. + /// Index of the target. + /// targetIndex should be between 0 and " + (source.Count - 1) + [Description("绉诲姩椤")] + public static void MoveItem(this List source, Predicate selector, int targetIndex) + { + if (!targetIndex.IsBetween(0, source.Count - 1)) + { + throw new IndexOutOfRangeException("targetIndex should be between 0 and " + (source.Count - 1)); + } + + var currentIndex = source.FindIndex(0, selector); + if (currentIndex == targetIndex) + { + return; + } + + var item = source[currentIndex]; + source.RemoveAt(currentIndex); + source.Insert(targetIndex, item); + } + + /// + /// Gets the or add. + /// + /// + /// The source. + /// The selector. + /// The factory. + /// + [Description("鑾峰彇骞舵坊鍔")] + public static T GetOrAdd([NotNull] this IList source, Func selector, Func factory) + { + var item = source.FirstOrDefault(selector); + + if (item == null) + { + item = factory(); + source.Add(item); + } + + return item; + } + + /// + /// Sort a list by a topological sorting, which consider their dependencies. + /// + /// The type of the members of values. + /// A list of objects to sort + /// Function to resolve the dependencies + /// Equality comparer for dependencies + /// + /// Returns a new list ordered by dependencies. + /// If A depends on B, then B will come before than A in the resulting list. + /// + [Description("閫氳繃topological鎺掑簭瀵瑰垪琛ㄨ繘琛屾帓搴忥紝topological鎺掑簭鑰冭檻浜嗗畠浠箣闂寸殑渚濊禆鍏崇郴")] + public static List SortByDependencies( + this IEnumerable source, + Func> getDependencies, + IEqualityComparer comparer = null) + { + var sorted = new List(); + var visited = new Dictionary(comparer); + + foreach (var item in source) + { + SortByDependenciesVisit(item, getDependencies, sorted, visited); + } + + return sorted; + } + + /// + /// + /// + /// The type of the members of values. + /// Item to resolve + /// Function to resolve the dependencies + /// List with the sortet items + /// Dictionary with the visited items + private static void SortByDependenciesVisit(T item, Func> getDependencies, List sorted, + Dictionary visited) + { + bool inProcess; + var alreadyVisited = visited.TryGetValue(item, out inProcess); + + if (alreadyVisited) + { + if (inProcess) + { + throw new ArgumentException("Cyclic dependency found! Item: " + item); + } + } + else + { + visited[item] = true; + + var dependencies = getDependencies(item); + if (dependencies != null) + { + foreach (var dependency in dependencies) + { + SortByDependenciesVisit(dependency, getDependencies, sorted, visited); + } + } + + visited[item] = false; + sorted.Add(item); + } + } + } +} diff --git a/JiShe.CollectBus.Common/Extensions/LockExtensions.cs b/JiShe.CollectBus.Common/Extensions/LockExtensions.cs new file mode 100644 index 0000000..cff7df7 --- /dev/null +++ b/JiShe.CollectBus.Common/Extensions/LockExtensions.cs @@ -0,0 +1,68 @@ +锘縰sing System; + +namespace JiShe.CollectBus.Common.Extensions +{ + /// + /// Extension methods to make locking easier. + /// + public static class LockExtensions + { + /// + /// Executes given by locking given object. + /// + /// Source object (to be locked) + /// Action (to be executed) + public static void Locking(this object source, Action action) + { + lock (source) + { + action(); + } + } + + /// + /// Executes given by locking given object. + /// + /// Type of the object (to be locked) + /// Source object (to be locked) + /// Action (to be executed) + public static void Locking(this T source, Action action) where T : class + { + lock (source) + { + action(source); + } + } + + /// + /// Executes given and returns it's value by locking given object. + /// + /// Return type + /// Source object (to be locked) + /// Function (to be executed) + /// Return value of the + public static TResult Locking(this object source, Func func) + { + lock (source) + { + return func(); + } + } + + /// + /// Executes given and returns it's value by locking given object. + /// + /// Type of the object (to be locked) + /// Return type + /// Source object (to be locked) + /// Function (to be executed) + /// Return value of the + public static TResult Locking(this T source, Func func) where T : class + { + lock (source) + { + return func(source); + } + } + } +} diff --git a/JiShe.CollectBus.Common/Extensions/ObjectExtensions.cs b/JiShe.CollectBus.Common/Extensions/ObjectExtensions.cs new file mode 100644 index 0000000..0d4b260 --- /dev/null +++ b/JiShe.CollectBus.Common/Extensions/ObjectExtensions.cs @@ -0,0 +1,154 @@ +锘縰sing System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using System.Reflection; + +namespace JiShe.CollectBus.Common.Extensions +{ + public static class ObjectExtensions + { + /// Gets the description. + /// The custom attribute provider. + /// + ///
+ ///
+ [Description("鑾峰彇Description")] + public static string GetDescription(this ICustomAttributeProvider customAttributeProvider) + { + var des = string.Empty; + var desAttribute = customAttributeProvider.GetAttribute(); + if (desAttribute != null) des = desAttribute.Description; + return des; + } + + /// Determines whether this instance is required. + /// The property information. + /// + /// true if the specified property information is required; otherwise, false. + [Description("鏄惁蹇呭~")] + public static bool IsRequired(this PropertyInfo propertyInfo) + { + if (propertyInfo.GetAttribute(true) != null) return true; + //Boolean銆丅yte銆丼Byte銆両nt16銆乁Int16銆両nt32銆乁Int32銆両nt64銆乁Int64銆丆har銆丏ouble銆丼ingle + if (propertyInfo.PropertyType.IsPrimitive) return true; + switch (propertyInfo.PropertyType.Name) + { + case "DateTime": + case "Decimal": + return true; + } + return false; + } + + /// Gets the attribute. + /// + /// The assembly. + /// if set to true [inherit]. + /// + ///
+ ///
+ [Description("鑾峰彇灞炴")] + public static T GetAttribute(this ICustomAttributeProvider assembly, bool inherit = false) + where T : Attribute + { + return assembly + .GetCustomAttributes(typeof(T), inherit) + .OfType() + .FirstOrDefault(); + } + + /// + /// Enums to list. + /// + /// + /// + /// T must be of type System.Enum + [Description("鏋氫妇杞崲涓洪泦鍚")] + public static List EnumToList() + { + var enumType = typeof(T); + + if (enumType.BaseType != typeof(Enum)) + throw new ArgumentException("T must be of type System.Enum"); + + var enumValArray = Enum.GetValues(enumType); + + var enumValList = new List(enumValArray.Length); + enumValList.AddRange(from int val in enumValArray select (T)Enum.Parse(enumType, val.ToString())); + return enumValList; + } + + /// + /// Enums to dictionary. + /// + /// The t. + /// + /// + /// object is not an Enumeration + [Description("鏋氫妇杞崲涓哄瓧鍏")] + public static IDictionary EnumToDictionary(this Type t) + { + if (t == null) throw new NullReferenceException(); + if (!t.IsEnum) throw new InvalidCastException("object is not an Enumeration"); + + var names = Enum.GetNames(t); + var values = Enum.GetValues(t); + + return (from i in Enumerable.Range(0, names.Length) + select new { Key = names[i], Value = (int)values.GetValue(i) }).ToDictionary(k => k.Key, k => k.Value); + } + + /// + /// Used to simplify and beautify casting an object to a type. + /// + /// Type to be casted + /// Object to cast + /// Casted object + public static T As(this object obj) + where T : class + { + return (T)obj; + } + + /// + /// Converts given object to a value or enum type using or method. + /// + /// Object to be converted + /// Type of the target object + /// Converted object + public static T To(this object obj) + where T : struct + { + if (typeof(T) == typeof(Guid) || typeof(T) == typeof(TimeSpan)) + { + return (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFromInvariantString(obj.ToString()); + } + if (typeof(T).IsEnum) + { + if (Enum.IsDefined(typeof(T), obj)) + { + return (T)Enum.Parse(typeof(T), obj.ToString()); + } + else + { + throw new ArgumentException($"Enum type undefined '{obj}'."); + } + } + + return (T)Convert.ChangeType(obj, typeof(T), CultureInfo.InvariantCulture); + } + + /// + /// Check if an item is in a list. + /// + /// Item to check + /// List of items + /// Type of the items + public static bool IsIn(this T item, params T[] list) + { + return list.Contains(item); + } + } +} diff --git a/JiShe.CollectBus.Common/Extensions/StreamExtensions.cs b/JiShe.CollectBus.Common/Extensions/StreamExtensions.cs new file mode 100644 index 0000000..e683313 --- /dev/null +++ b/JiShe.CollectBus.Common/Extensions/StreamExtensions.cs @@ -0,0 +1,59 @@ +锘縰sing System.ComponentModel; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.Common.Extensions +{ + public static class StreamExtensions + { + /// + /// Gets all bytes. + /// + /// The stream. + /// + [Description("鑾峰彇瀛楄妭鏁扮粍")] + public static byte[] GetAllBytes(this Stream stream) + { + using (var memoryStream = new MemoryStream()) + { + stream.CopyTo(memoryStream); + return memoryStream.ToArray(); + } + } + + /// + /// Gets all bytes asynchronous. + /// + /// The stream. + /// The cancellation token. + /// + public static async Task GetAllBytesAsync(this Stream stream, CancellationToken cancellationToken = default) + { + using (var memoryStream = new MemoryStream()) + { + stream.Position = 0; + await stream.CopyToAsync(memoryStream, cancellationToken); + return memoryStream.ToArray(); + } + } + + /// + /// Copies to asynchronous. + /// + /// The stream. + /// The destination. + /// The cancellation token. + /// + [Description("澶嶅埗")] + public static Task CopyToAsync(this Stream stream, Stream destination, CancellationToken cancellationToken) + { + stream.Position = 0; + return stream.CopyToAsync( + destination, + 81920, //this is already the default value, but needed to set to be able to pass the cancellationToken + cancellationToken + ); + } + } +} diff --git a/JiShe.CollectBus.Common/Extensions/StringExtensions.cs b/JiShe.CollectBus.Common/Extensions/StringExtensions.cs new file mode 100644 index 0000000..21e9e19 --- /dev/null +++ b/JiShe.CollectBus.Common/Extensions/StringExtensions.cs @@ -0,0 +1,1120 @@ +锘縰sing System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Security; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using JiShe.CollectBus.Common.Consts; +using JiShe.CollectBus.Common.Enums; + +namespace JiShe.CollectBus.Common.Extensions +{ + public static class StringExtensions + { + /// Determines whether [is null or white space]. + /// The string. + /// + /// true if [is null or white space] [the specified string]; otherwise, false. + [Description("妫鏌ユ槸鍚︿负NULL鎴栫┖鏍")] + public static bool IsNullOrWhiteSpace(this string str) + { + return string.IsNullOrWhiteSpace(str); + } + + /// Determines whether [is null or empty]. + /// The string. + /// + /// true if [is null or empty] [the specified string]; otherwise, false. + [Description("妫鏌ユ槸鍚︿负NULL鎴栫┖")] + public static bool IsNullOrEmpty(this string str) + { + return string.IsNullOrEmpty(str); + } + + /// Copies the specified string. + /// The string. + /// + ///
+ ///
+ [Description("澶嶅埗")] + public static string Copy(this string str) + { + return string.Copy(str); + } + + /// Gets the phone number. + /// The string. + /// The pattern. + /// + ///
+ ///
+ [Description("鑾峰彇鍗曚釜鎵嬫満鍙")] + public static string GetPhoneNumber(this string str, string pattern = RegexConst.PhoneNumber) + { + var reg = new Regex(pattern); + var match = reg.Match(str); + if (match.Success) + { + return match.Value; + } + return null; + } + + /// Gets the phone numbers. + /// The string. + /// The pattern. + /// + ///
+ ///
+ [Description("鑾峰彇鍏ㄩ儴鎵嬫満鍙")] + public static List GetPhoneNumbers(this string str, string pattern = RegexConst.PhoneNumber) + { + var reg = new Regex(pattern); + var matches = reg.Matches(str); + return (from Match item in matches select item.Value).ToList(); + } + + /// Regex Match the specified pattern. + /// The string. + /// The pattern. + /// + ///
+ ///
+ [Description("姝e垯鍖归厤鍗曚釜")] + public static string RegexMatch(this string str, string pattern) + { + var reg = new Regex(pattern); + var match = reg.Match(str); + if (match.Success) + { + return match.Value; + } + return null; + } + + /// Regex Matches the specified pattern. + /// The string. + /// The pattern. + /// + ///
+ ///
+ [Description("姝e垯鍖归厤澶氫釜")] + public static List RegexMatches(this string str, string pattern) + { + var reg = new Regex(pattern); + var matches = reg.Matches(str); + return (from Match item in matches select item.Value).ToList(); + } + + /// Converts the string representation of a number to an integer. + /// The string. + /// + ///
+ ///
+ [Description("String杞崲Int")] + public static int ToInt(this string str) + { + return int.Parse(str); + } + + /// Converts to decimal. + /// The string. + /// + ///
+ ///
+ [Description("String杞崲Decimal")] + public static decimal ToDecimal(this string str) + { + return decimal.Parse(str); + } + + /// Determines whether this instance is numeric. + /// The string. + /// + /// true if the specified string is numeric; otherwise, false. + [Description("鏄惁涓烘暟瀛")] + public static bool IsNumeric(this string str) + { + var regex = new Regex(RegexConst.IsNumeric); + return regex.IsMatch(str); + } + + /// Determines whether the specified value is contains. + /// The string. + /// The value. + /// + /// true if the specified value is contains; otherwise, false. + [Description("鏄惁鍖呭惈")] + public static bool IsContains(this string str, string value) + { + return str.IndexOf(value, StringComparison.Ordinal) >= 0; + } + + /// Ins the specified string values. + /// The string. + /// The string values. + /// + ///
+ ///
+ [Description("鏄惁瀛樺湪鏁扮粍涓")] + public static bool In(this string str, params string[] stringValues) + { + foreach (string otherValue in stringValues) + if (string.CompareOrdinal(str, otherValue) == 0) + return true; + + return false; + } + + /// Formats the specified arg0. + /// The string. + /// The arg0. + /// + ///
+ ///
+ [Description("鏍煎紡鍖")] + public static string Format(this string str, object arg0) + { + return string.Format(str, arg0); + } + + /// Formats the specified arguments. + /// The string. + /// The arguments. + /// + ///
+ ///
+ public static string Format(this string str, params object[] args) + { + return string.Format(str, args); + } + + /// Encrypts the specified key. + /// The string. + /// The key. + /// + ///
+ ///
+ /// An empty string value cannot be encrypted. + /// or + /// Cannot encrypt using an empty key. Please supply an encryption key. + [Description("RSA鍔犲瘑")] + public static string Encrypt(this string str, string key) + { + if (string.IsNullOrEmpty(str)) + { + throw new ArgumentException("An empty string value cannot be encrypted."); + } + if (string.IsNullOrEmpty(key)) + { + throw new ArgumentException("Cannot encrypt using an empty key. Please supply an encryption key."); + } + var cspParameters = new CspParameters { KeyContainerName = key }; + var rsa = new RSACryptoServiceProvider(cspParameters) { PersistKeyInCsp = true }; + var bytes = rsa.Encrypt(Encoding.UTF8.GetBytes(str), true); + return BitConverter.ToString(bytes); + } + + /// Decrypts the specified key. + /// The string. + /// The key. + /// + ///
+ ///
+ /// An empty string value cannot be encrypted. + /// or + /// Cannot decrypt using an empty key. Please supply a decryption key. + [Description("RSA瑙e瘑")] + public static string Decrypt(this string str, string key) + { + string result = null; + if (string.IsNullOrEmpty(str)) + { + throw new ArgumentException("An empty string value cannot be encrypted."); + } + if (string.IsNullOrEmpty(key)) + { + throw new ArgumentException("Cannot decrypt using an empty key. Please supply a decryption key."); + } + var cspParameters = new CspParameters { KeyContainerName = key }; + var rsa = new RSACryptoServiceProvider(cspParameters) { PersistKeyInCsp = true }; + var decryptArray = str.Split(new string[] { "-" }, StringSplitOptions.None); + var decryptByteArray = Array.ConvertAll(decryptArray, (s => Convert.ToByte(byte.Parse(s, System.Globalization.NumberStyles.HexNumber)))); + var bytes = rsa.Decrypt(decryptByteArray, true); + result = Encoding.UTF8.GetString(bytes); + return result; + } + + /// Firsts to upper. + /// The string. + /// + ///
+ ///
+ [Description("棣栧瓧姣嶅ぇ鍐")] + public static string FirstToUpper(this string str) + { + if (string.IsNullOrEmpty(str)) + { + return string.Empty; + } + + var theChars = str.ToCharArray(); + theChars[0] = char.ToUpper(theChars[0]); + return new string(theChars); + } + + /// Converts to securestring. + /// The string. + /// + ///
+ ///
+ [Description("杞崲涓哄畨鍏ㄥ瓧绗︿覆")] + public static SecureString ToSecureString(this string str) + { + var secureString = new SecureString(); + foreach (var c in str) + secureString.AppendChar(c); + + return secureString; + } + + /// Determines whether this instance is date. + /// The string. + /// + /// true if the specified string is date; otherwise, false. + [Description("杞崲涓烘棩鏈")] + public static bool IsDate(this string str) + { + return !string.IsNullOrEmpty(str) && DateTime.TryParse(str, out _); + } + + /// Determines whether [is email address]. + /// The string. + /// + /// true if [is email address] [the specified string]; otherwise, false. + [Description("鏄惁涓洪偖绠卞湴鍧")] + public static bool IsEmailAddress(this string str) + { + var regex = new Regex(RegexConst.Email); + return regex.IsMatch(str); + } + + /// Parses the specified string. + /// + /// The string. + /// + ///
+ ///
+ [Description("杞崲涓轰换浣曟牸寮")] + public static T Parse(this string str) + { + var result = default(T); + if (!string.IsNullOrEmpty(str)) + { + var tc = TypeDescriptor.GetConverter(typeof(T)); + result = (T)tc.ConvertFrom(str); + } + return result; + } + + /// Determines whether this instance is unique identifier. + /// The string. + /// + /// true if the specified string is unique identifier; otherwise, false. + /// + [Description("鏄惁涓篏uid")] + public static bool IsGuid(this string str) + { + if (str == null) + throw new ArgumentNullException(); + + var format = new Regex( + "^[A-Fa-f0-9]{32}$|" + + "^({|\\()?[A-Fa-f0-9]{8}-([A-Fa-f0-9]{4}-){3}[A-Fa-f0-9]{12}(}|\\))?$|" + + "^({)?[0xA-Fa-f0-9]{3,10}(, {0,1}[0xA-Fa-f0-9]{3,6}){2}, {0,1}({)([0xA-Fa-f0-9]{3,4}, {0,1}){7}[0xA-Fa-f0-9]{3,4}(}})$"); + var match = format.Match(str); + return match.Success; + } + + /// Determines whether this instance is URL. + /// The string. + /// + /// true if the specified string is URL; otherwise, false. + [Description("鏄惁涓哄湴鍧")] + public static bool IsUrl(this string str) + { + var regex = new Regex(RegexConst.Url); + return regex.IsMatch(str); + } + + /// Masks the specified number exposed. + /// The string. + /// The number exposed. + /// The mask character. + /// The type. + /// + ///
+ ///
+ [Description("灞忚斀瀛楃锛屽锛123***789")] + public static string Mask(this string str, int numExposed, char maskChar = '*', MaskTypeEnum type = MaskTypeEnum.All) + { + var maskedString = str; + + if (str.IsLengthAtLeast(numExposed)) + { + var builder = new StringBuilder(str.Length); + int index = maskedString.Length - numExposed; + + if (type == MaskTypeEnum.AlphaNumericOnly) + { + CreateAlphaNumMask(builder, str, maskChar, index); + } + else + { + builder.Append(maskChar, index); + } + + builder.Append(str.Substring(index)); + maskedString = builder.ToString(); + } + + return maskedString; + } + + /// + /// Masks the mobile. + /// + /// The mobile. + /// + [Description("灞忚斀鎵嬫満鍙")] + public static string MaskMobile(this string mobile) + { + if (!mobile.IsNullOrEmpty() && mobile.Length > 7) + { + var regex = new Regex(@"(?<=\d{3}).+(?=\d{4})", RegexOptions.IgnoreCase); + mobile = regex.Replace(mobile, "****"); + } + + return mobile; + } + + /// + /// Masks the identifier card. + /// + /// The identifier card. + /// + [Description("灞忚斀韬唤璇")] + public static string MaskIdCard(this string idCard) + { + if (!idCard.IsNullOrEmpty() && idCard.Length > 10) + { + var regex = new Regex(@"(?<=\w{6}).+(?=\w{4})", RegexOptions.IgnoreCase); + idCard = regex.Replace(idCard, "********"); + } + + return idCard; + } + + /// + /// Masks the bank card. + /// + /// The bank card. + /// + [Description("灞忚斀閾惰鍗")] + public static string MaskBankCard(this string bankCard) + { + if (!bankCard.IsNullOrEmpty() && bankCard.Length > 4) + { + var regex = new Regex(@"(?<=\d{4})\d+(?=\d{4})", RegexOptions.IgnoreCase); + bankCard = regex.Replace(bankCard, " **** **** "); + } + + return bankCard; + } + + /// Determines whether [is length at least] [the specified length]. + /// The string. + /// The length. + /// + /// true if [is length at least] [the specified length]; otherwise, false. + /// length - The length must be a non-negative number. + [Description("鍒ゆ柇鏄惁涓烘渶鍚庝竴浣嶅瓧绗")] + public static bool IsLengthAtLeast(this string str, int length) + { + if (length < 0) + { + throw new ArgumentOutOfRangeException(nameof(length), length, + "The length must be a non-negative number."); + } + + return str != null && str.Length >= length; + } + + /// Determines whether [is strong password]. + /// The string. + /// + /// true if [is strong password] [the specified string]; otherwise, false. + [Description("鍒ゆ柇鏄惁涓哄己澹瘑鐮")] + public static bool IsStrongPassword(this string str) + { + var isStrong = Regex.IsMatch(str, @"[\d]"); + if (isStrong) isStrong = Regex.IsMatch(str, @"[a-z]"); + if (isStrong) isStrong = Regex.IsMatch(str, @"[A-Z]"); + if (isStrong) isStrong = Regex.IsMatch(str, @"[\s~!@#\$%\^&\*\(\)\{\}\|\[\]\\:;'?,.`+=<>\/]"); + if (isStrong) isStrong = str.Length > 7; + return isStrong; + } + + /// Determines whether [is match regex] [the specified pattern]. + /// The string. + /// The pattern. + /// + /// true if [is match regex] [the specified pattern]; otherwise, false. + [Description("鏄惁姝e垯鍖归厤閫氳繃")] + public static bool IsMatchRegex(this string str, string pattern) + { + var regex = new Regex(pattern); + return regex.IsMatch(str); + } + + /// Converts to bytes. + /// Name of the file. + /// + ///
+ ///
+ /// + [Description("鏂囦欢鐗╃悊鍦板潃杞崲涓哄瓧鑺傛暟缁")] + public static byte[] ToBytes(this string fileName) + { + if (!File.Exists(fileName)) + throw new FileNotFoundException(fileName); + + return File.ReadAllBytes(fileName); + } + + /// Converts to color. + /// The RGB. + /// + ///
+ ///
+ [Description("杞崲涓篊olor")] + public static Color ToColor(this string rgb) + { + rgb = rgb.Replace("#", ""); + var a = Convert.ToByte("ff", 16); + byte pos = 0; + if (rgb.Length == 8) + { + a = Convert.ToByte(rgb.Substring(pos, 2), 16); + pos = 2; + } + var r = Convert.ToByte(rgb.Substring(pos, 2), 16); + pos += 2; + var g = Convert.ToByte(rgb.Substring(pos, 2), 16); + pos += 2; + var b = Convert.ToByte(rgb.Substring(pos, 2), 16); + return Color.FromArgb(a, r, g, b); + } + + /// + /// Adds a char to end of given string if it does not ends with the char. + /// + [Description("濡傛灉缁欏畾瀛楃涓蹭笉浠char]缁撳熬锛屽垯鍦ㄥ叾缁撳熬娣诲姞[char]")] + public static string EnsureEndsWith(this string str, char c) + { + return EnsureEndsWith(str, c, StringComparison.Ordinal); + } + + /// + /// Adds a char to end of given string if it does not ends with the char. + /// + public static string EnsureEndsWith(this string str, char c, StringComparison comparisonType) + { + if (str == null) + { + throw new ArgumentNullException(nameof(str)); + } + + if (str.EndsWith(c.ToString(), comparisonType)) + { + return str; + } + + return str + c; + } + + /// + /// Adds a char to end of given string if it does not ends with the char. + /// + public static string EnsureEndsWith(this string str, char c, bool ignoreCase, CultureInfo culture) + { + if (str == null) + { + throw new ArgumentNullException(nameof(str)); + } + + if (str.EndsWith(c.ToString(culture), ignoreCase, culture)) + { + return str; + } + + return str + c; + } + + /// + /// Adds a char to beginning of given string if it does not starts with the char. + /// + [Description("濡傛灉缁欏畾瀛楃涓蹭笉浠char]寮澶达紝鍒欏湪鍏跺紑澶存坊鍔燵char]")] + public static string EnsureStartsWith(this string str, char c) + { + return EnsureStartsWith(str, c, StringComparison.Ordinal); + } + + /// + /// Adds a char to beginning of given string if it does not starts with the char. + /// + public static string EnsureStartsWith(this string str, char c, StringComparison comparisonType) + { + if (str == null) + { + throw new ArgumentNullException(nameof(str)); + } + + if (str.StartsWith(c.ToString(), comparisonType)) + { + return str; + } + + return c + str; + } + + /// + /// Adds a char to beginning of given string if it does not starts with the char. + /// + public static string EnsureStartsWith(this string str, char c, bool ignoreCase, CultureInfo culture) + { + if (str == null) + { + throw new ArgumentNullException(nameof(str)); + } + + if (str.StartsWith(c.ToString(culture), ignoreCase, culture)) + { + return str; + } + + return c + str; + } + + /// + /// Gets a substring of a string from beginning of the string. + /// + /// Thrown if is null + /// Thrown if is bigger that string's length + [Description("浠庡瓧绗︿覆鐨勫紑澶磋幏鍙栨寚瀹氶暱搴﹀瓧绗︿覆")] + public static string Left(this string str, int len) + { + if (str == null) + { + throw new ArgumentNullException(nameof(str)); + } + + if (str.Length < len) + { + throw new ArgumentException("len argument can not be bigger than given string's length!"); + } + + return str.Substring(0, len); + } + + /// + /// Converts line endings in the string to . + /// + [Description("灏嗗瓧绗︿覆涓殑琛屽熬杞崲涓篍nvironment.NewLine")] + public static string NormalizeLineEndings(this string str) + { + return str.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", Environment.NewLine); + } + + /// + /// Gets index of nth occurence of a char in a string. + /// + /// source string to be searched + /// Char to search in + /// Count of the occurence + [Description("鑾峰彇瀛楃涓蹭腑绗琻涓瓧绗︾殑绱㈠紩")] + public static int NthIndexOf(this string str, char c, int n) + { + if (str == null) + { + throw new ArgumentNullException(nameof(str)); + } + + var count = 0; + for (var i = 0; i < str.Length; i++) + { + if (str[i] != c) + { + continue; + } + + if ((++count) == n) + { + return i; + } + } + + return -1; + } + + /// + /// Removes first occurrence of the given postfixes from end of the given string. + /// Ordering is important. If one of the postFixes is matched, others will not be tested. + /// + /// The string. + /// one or more postfix. + /// Modified string or the same string if it has not any of given postfixes + [Description("浠庣粰瀹氬瓧绗︿覆鐨勬湯灏惧垹闄ょ涓涓嚭鐜扮殑缁欏畾鍚庣紑")] + public static string RemovePostFix(this string str, params string[] postFixes) + { + if (str == null) + { + return null; + } + + if (string.IsNullOrEmpty(str)) + { + return string.Empty; + } + + if (postFixes.IsNullOrEmpty()) + { + return str; + } + + foreach (var postFix in postFixes) + { + if (str.EndsWith(postFix)) + { + return str.Left(str.Length - postFix.Length); + } + } + + return str; + } + + /// + /// Removes first occurrence of the given prefixes from beginning of the given string. + /// Ordering is important. If one of the preFixes is matched, others will not be tested. + /// + /// The string. + /// one or more prefix. + /// Modified string or the same string if it has not any of given prefixes + [Description("浠庣粰瀹氬瓧绗︿覆鐨勫紑澶寸Щ闄ょ涓涓嚭鐜扮殑缁欏畾鍓嶇紑")] + public static string RemovePreFix(this string str, params string[] preFixes) + { + if (str == null) + { + return null; + } + + if (string.IsNullOrEmpty(str)) + { + return string.Empty; + } + + if (preFixes.IsNullOrEmpty()) + { + return str; + } + + foreach (var preFix in preFixes) + { + if (str.StartsWith(preFix)) + { + return str.Right(str.Length - preFix.Length); + } + } + + return str; + } + + /// + /// Gets a substring of a string from end of the string. + /// + /// Thrown if is null + /// Thrown if is bigger that string's length + [Description("浠庡瓧绗︿覆鐨勭粨灏捐幏鍙栨寚瀹氶暱搴﹀瓧绗︿覆")] + public static string Right(this string str, int len) + { + if (str == null) + { + throw new ArgumentNullException(nameof(str)); + } + + if (str.Length < len) + { + throw new ArgumentException("len argument can not be bigger than given string's length!"); + } + + return str.Substring(str.Length - len, len); + } + + /// + /// Uses string.Split method to split given string by given separator. + /// + [Description("瀛楃涓叉媶鍒")] + public static string[] Split(this string str, string separator) + { + return str.Split(new[] { separator }, StringSplitOptions.None); + } + + /// + /// Uses string.Split method to split given string by given separator. + /// + public static string[] Split(this string str, string separator, StringSplitOptions options) + { + return str.Split(new[] { separator }, options); + } + + /// + /// Uses string.Split method to split given string by . + /// + [Description("瀛楃涓叉崲琛屾媶鍒")] + public static string[] SplitToLines(this string str) + { + return str.Split(Environment.NewLine); + } + + /// + /// Uses string.Split method to split given string by . + /// + public static string[] SplitToLines(this string str, StringSplitOptions options) + { + return str.Split(Environment.NewLine, options); + } + + /// + /// Uses string.Split method to split given string by separator. + /// + /// The string. + /// The separator. + /// + public static string[] SplitToLines(this string str, string separator) + { + return str.Split(separator); + } + + /// + /// Converts PascalCase string to camelCase string. + /// + /// String to convert + /// Invariant culture + /// camelCase of the string + [Description("灏哖ascalCase瀛楃涓茶浆鎹负camelCase瀛楃涓")] + public static string ToCamelCase(this string str, bool invariantCulture = true) + { + if (string.IsNullOrWhiteSpace(str)) + { + return str; + } + + if (str.Length == 1) + { + return invariantCulture ? str.ToLowerInvariant() : str.ToLower(); + } + + return (invariantCulture ? char.ToLowerInvariant(str[0]) : char.ToLower(str[0])) + str.Substring(1); + } + + /// + /// Converts PascalCase string to camelCase string in specified culture. + /// + /// String to convert + /// An object that supplies culture-specific casing rules + /// camelCase of the string + public static string ToCamelCase(this string str) + { + if (string.IsNullOrWhiteSpace(str)) + { + return str; + } + + if (str.Length == 1) + { + return str.ToLower(); + } + + return char.ToLower(str[0]) + str.Substring(1); + } + + /// + /// Converts given PascalCase/camelCase string to sentence (by splitting words by space). + /// Example: "ThisIsSampleSentence" is converted to "This is a sample sentence". + /// + /// String to convert. + /// Invariant culture + [Description("灏嗙粰瀹氱殑PascalCase/camelCase瀛楃涓茶浆鎹负鍙ュ瓙锛堥氳繃鎸夌┖鏍兼媶鍒嗗崟璇嶏級")] + public static string ToSentenceCase(this string str, bool invariantCulture = false) + { + if (string.IsNullOrWhiteSpace(str)) + { + return str; + } + + return Regex.Replace( + str, + "[a-z][A-Z]", + m => m.Value[0] + " " + (invariantCulture ? char.ToLowerInvariant(m.Value[1]) : char.ToLower(m.Value[1])) + ); + } + + /// + /// Converts given PascalCase/camelCase string to sentence (by splitting words by space). + /// Example: "ThisIsSampleSentence" is converted to "This is a sample sentence". + /// + /// String to convert. + /// An object that supplies culture-specific casing rules. + public static string ToSentenceCase(this string str) + { + if (string.IsNullOrWhiteSpace(str)) + { + return str; + } + + return Regex.Replace(str, "[a-z][A-Z]", m => m.Value[0] + " " + char.ToLower(m.Value[1])); + } + + /// + /// Converts string to enum value. + /// + /// Type of enum + /// String value to convert + /// Returns enum object + [Description("灏嗗瓧绗︿覆杞崲涓烘灇涓惧")] + public static T ToEnum(this string value) + where T : struct + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + return (T)Enum.Parse(typeof(T), value); + } + + /// + /// Converts string to enum value. + /// + /// Type of enum + /// String value to convert + /// Ignore case + /// Returns enum object + public static T ToEnum(this string value, bool ignoreCase) + where T : struct + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + return (T)Enum.Parse(typeof(T), value, ignoreCase); + } + + /// + /// Converts to md5. + /// + /// The string. + /// + [Description("灏嗗瓧绗︿覆杞崲涓篗D5")] + public static string ToMd5(this string str) + { + using (var md5 = MD5.Create()) + { + var inputBytes = Encoding.UTF8.GetBytes(str); + var hashBytes = md5.ComputeHash(inputBytes); + + var sb = new StringBuilder(); + foreach (var hashByte in hashBytes) + { + sb.Append(hashByte.ToString("X2")); + } + + return sb.ToString(); + } + } + + /// + /// Converts camelCase string to PascalCase string. + /// + /// String to convert + /// Invariant culture + /// PascalCase of the string + [Description("灏哻amelCase瀛楃涓茶浆鎹负pascalase瀛楃涓")] + public static string ToPascalCase(this string str, bool invariantCulture = true) + { + if (string.IsNullOrWhiteSpace(str)) + { + return str; + } + + if (str.Length == 1) + { + return invariantCulture ? str.ToUpperInvariant() : str.ToUpper(); + } + + return (invariantCulture ? char.ToUpperInvariant(str[0]) : char.ToUpper(str[0])) + str.Substring(1); + } + + /// + /// Converts camelCase string to PascalCase string in specified culture. + /// + /// String to convert + /// An object that supplies culture-specific casing rules + /// PascalCase of the string + public static string ToPascalCase(this string str) + { + if (string.IsNullOrWhiteSpace(str)) + { + return str; + } + + if (str.Length == 1) + { + return str.ToUpper(); + } + + return char.ToUpper(str[0]) + str.Substring(1); + } + + /// + /// Gets a substring of a string from beginning of the string if it exceeds maximum length. + /// + /// Thrown if is null + [Description("濡傛灉鎸囧畾闀垮害瓒呰繃鏈澶ч暱搴︼紝鍒欎粠璇ュ瓧绗︿覆鐨勫紑澶磋幏鍙栨寚瀹氶暱搴︾殑瀛楃")] + public static string Truncate(this string str, int maxLength) + { + if (str == null) + { + return null; + } + + if (str.Length <= maxLength) + { + return str; + } + + return str.Left(maxLength); + } + + /// + /// Gets a substring of a string from beginning of the string if it exceeds maximum length. + /// It adds a "..." postfix to end of the string if it's truncated. + /// Returning string can not be longer than maxLength. + /// + /// Thrown if is null + public static string TruncateWithPostfix(this string str, int maxLength) + { + return TruncateWithPostfix(str, maxLength, "..."); + } + + /// + /// Gets a substring of a string from beginning of the string if it exceeds maximum length. + /// It adds given to end of the string if it's truncated. + /// Returning string can not be longer than maxLength. + /// + /// Thrown if is null + public static string TruncateWithPostfix(this string str, int maxLength, string postfix) + { + if (str == null) + { + return null; + } + + if (string.IsNullOrEmpty(str) || maxLength == 0) + { + return string.Empty; + } + + if (str.Length <= maxLength) + { + return str; + } + + if (maxLength <= postfix.Length) + { + return postfix.Left(maxLength); + } + + return str.Left(maxLength - postfix.Length) + postfix; + } + + /// + /// Read file context. + /// + /// + /// + [Description("閫氳繃鏂囦欢鐗╃悊璺緞鑾峰彇鏂囦欢鏂囨湰")] + public static string ReadFile(this string filePath) + { + string context = string.Empty; + if (!File.Exists(filePath)) + { + throw new IOException($"'{filePath}'file not exist"); + } + using (FileStream fs = new FileStream(filePath, FileMode.Open)) + { + using (StreamReader sr = new StreamReader(fs, Encoding.UTF8)) + { + context = sr.ReadToEnd().ToString(); + } + } + return context; + } + + /// + /// Verify number sort + /// + /// + /// + /// + [Description("楠岃瘉鏂囨湰鏁板瓧锛屽'1,2,3'锛涙槸鍚︽寜鎸囧畾鎺掑簭")] + public static bool VerifySort(this string intStr, SortEnum sort = SortEnum.Asc) + { + var list = intStr.Split(",") + .Select(int.Parse) + .ToList(); + switch (sort) + { + case SortEnum.Asc: + list = list.OrderBy(a => a) + .ToList(); + break; + case SortEnum.Desc: + list = list.OrderByDescending(a => a) + .ToList(); + break; + default: + throw new ArgumentOutOfRangeException(nameof(sort), sort, null); + } + + return string.Join(",", list) == intStr; + } + + private static void CreateAlphaNumMask(StringBuilder buffer, string source, char mask, int length) + { + for (int i = 0; i < length; i++) + { + buffer.Append(char.IsLetterOrDigit(source[i]) + ? mask + : source[i]); + } + } + + } +} diff --git a/JiShe.CollectBus.Common/Extensions/XmlExtensions.cs b/JiShe.CollectBus.Common/Extensions/XmlExtensions.cs new file mode 100644 index 0000000..26d2c57 --- /dev/null +++ b/JiShe.CollectBus.Common/Extensions/XmlExtensions.cs @@ -0,0 +1,64 @@ +锘縰sing System; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Xml; +using System.Xml.Linq; +using System.Xml.Serialization; + +namespace JiShe.CollectBus.Common.Extensions +{ + public static class XmlExtensions + { + /// + /// Froms the XML. + /// + /// + /// The XML. + /// + [Description("灏哫ML杞崲涓篛bject")] + public static object FromXml(this string xml) where T : new() + { + using (var sr = new StringReader(xml)) + { + var xmlSerializer = new XmlSerializer(typeof(T)); + return (T)xmlSerializer.Deserialize(sr); + } + } + + /// + /// Deserializes the specified XML document. + /// + /// + /// The XML document. + /// + [Description("鍙嶅簭鍒楀寲鎸囧畾鐨刋ML鏂囨。")] + public static T Deserialize(this XDocument xmlDocument) + { + var xmlSerializer = new XmlSerializer(typeof(T)); + using (var reader = xmlDocument.CreateReader()) + return (T)xmlSerializer.Deserialize(reader); + } + + /// + /// Gets an attribute's value from an Xml node. + /// + /// The Xml node + /// Attribute name + /// Value of the attribute + [Description("浠嶺ml鑺傜偣鑾峰彇灞炴х殑鍊")] + public static string GetAttributeValueOrNull(this XmlNode node, string attributeName) + { + if (node.Attributes == null || node.Attributes.Count <= 0) + { + throw new Exception(node.Name + " node has not " + attributeName + " attribute"); + } + + return node.Attributes + .Cast() + .Where(attr => attr.Name == attributeName) + .Select(attr => attr.Value) + .FirstOrDefault(); + } + } +} diff --git a/JiShe.CollectBus.Common/Helpers/TypeHelper.cs b/JiShe.CollectBus.Common/Helpers/TypeHelper.cs new file mode 100644 index 0000000..2528a0b --- /dev/null +++ b/JiShe.CollectBus.Common/Helpers/TypeHelper.cs @@ -0,0 +1,66 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +namespace JiShe.CollectBus.Common.Helpers +{ + public static class TypeHelper + { + public static bool IsFunc(object obj) + { + if (obj == null) + { + return false; + } + + var type = obj.GetType(); + if (!type.GetTypeInfo().IsGenericType) + { + return false; + } + + return type.GetGenericTypeDefinition() == typeof(Func<>); + } + + public static bool IsFunc(object obj) + { + return obj != null && obj.GetType() == typeof(Func); + } + + public static bool IsPrimitiveExtendedIncludingNullable(Type type, bool includeEnums = false) + { + if (IsPrimitiveExtended(type, includeEnums)) + { + return true; + } + + if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + return IsPrimitiveExtended(type.GenericTypeArguments[0], includeEnums); + } + + return false; + } + + private static bool IsPrimitiveExtended(Type type, bool includeEnums) + { + if (type.GetTypeInfo().IsPrimitive) + { + return true; + } + + if (includeEnums && type.GetTypeInfo().IsEnum) + { + return true; + } + + return type == typeof(string) || + type == typeof(decimal) || + type == typeof(DateTime) || + type == typeof(DateTimeOffset) || + type == typeof(TimeSpan) || + type == typeof(Guid); + } + } +} diff --git a/JiShe.CollectBus.Common/JiShe.CollectBus.Common.csproj b/JiShe.CollectBus.Common/JiShe.CollectBus.Common.csproj index b4b43f4..626aa63 100644 --- a/JiShe.CollectBus.Common/JiShe.CollectBus.Common.csproj +++ b/JiShe.CollectBus.Common/JiShe.CollectBus.Common.csproj @@ -5,4 +5,8 @@ enable + + + + diff --git a/JiShe.CollectBus.Core/JiShe.CollectBus.Core.csproj b/JiShe.CollectBus.Core/JiShe.CollectBus.Core.csproj index a83352b..6dd2744 100644 --- a/JiShe.CollectBus.Core/JiShe.CollectBus.Core.csproj +++ b/JiShe.CollectBus.Core/JiShe.CollectBus.Core.csproj @@ -7,12 +7,14 @@ - - + + + - + + diff --git a/JiShe.CollectBus.Core/Plugins/TcpServiceReceivedPlugin.cs b/JiShe.CollectBus.Core/Plugins/TcpServiceReceivedPlugin.cs index 2907e27..1240510 100644 --- a/JiShe.CollectBus.Core/Plugins/TcpServiceReceivedPlugin.cs +++ b/JiShe.CollectBus.Core/Plugins/TcpServiceReceivedPlugin.cs @@ -10,22 +10,15 @@ namespace JiShe.CollectBus.Core.Plugins { public async Task OnTcpReceived(ITcpSession client, ReceivedDataEventArgs e) { - //TODO:鐢佃〃锛 376.1 645-07 modbus 姘磋〃锛 118 645-97 + //TODO: 鐢佃〃涓荤珯鍒伴泦涓櫒鐨勫崗璁兘鏄376.1鍗忚锛岄泦涓櫒涓嬪彂鍒扮數琛ㄥ崗璁垎涓645-07鍜宮odbus + //TODO: 姘磋〃涓荤珯鍒伴泦涓櫒鐨勫崗璁垎涓118鍜645-97鍗忚 + //TODO: 杩炴帴鎴愬姛鏃惰幏鍙栨。妗堜俊鎭紝鏍规嵁妗f淇℃伅鍖归厤鍗忚姝e垯鑾峰彇鍗忚鏈嶅姟杩涜鐩戝惉鍙戦 const string protocolType = "StandardProtocol"; - switch (protocolType) - { - //case "376": - // //todo锛氱櫥褰曟嬁鍒拌澶囦俊鎭紝鏍规嵁璁惧淇℃伅浣跨敤涓嶅悓鐨勫崗璁В鏋愭湇鍔 - // break; - //case "645": - // //todo: 鐩存帴鎷胯澶囦俊鎭紝鏍规嵁璁惧淇℃伅浣跨敤涓嶅悓鐨勫崗璁В鏋愭湇鍔 - // break; - } + var protocolPlugin = serviceProvider.GetKeyedService(protocolType); client.Logger.Info($"{protocolPlugin?.Get().Name},{protocolPlugin?.Get().RegularExpression}"); - //浠庡鎴风鏀跺埌淇℃伅 var messageHexString = Convert.ToHexString(e.ByteBlock.Span); client.Logger.Info($"[TCP] 宸蹭粠{client.GetIPPort()}鎺ユ敹鍒颁俊鎭細{messageHexString}"); diff --git a/JiShe.CollectBus.EntityFrameworkCore/Entities/EntityBase.cs b/JiShe.CollectBus.EntityFrameworkCore/Entities/EntityBase.cs new file mode 100644 index 0000000..0fd328e --- /dev/null +++ b/JiShe.CollectBus.EntityFrameworkCore/Entities/EntityBase.cs @@ -0,0 +1,13 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.EntityFrameworkCore.Entities +{ + public class EntityBase : IEntity + { + public TKey Id { get; set; } + } +} diff --git a/JiShe.CollectBus.EntityFrameworkCore/Entities/FullAuditedEntity.cs b/JiShe.CollectBus.EntityFrameworkCore/Entities/FullAuditedEntity.cs new file mode 100644 index 0000000..203c475 --- /dev/null +++ b/JiShe.CollectBus.EntityFrameworkCore/Entities/FullAuditedEntity.cs @@ -0,0 +1,19 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.EntityFrameworkCore.Entities +{ + public class FullAuditedEntity : EntityBase, ICreationAudited, IModificationAudited, IDeletionAudited + { + public TUser CreatorId { get; set; } + public DateTime CreationTime { get; set; } + public TUser LastModifierId { get; set; } + public DateTime? LastModificationTime { get; set; } + public TUser DeleterId { get; set; } + public bool IsDeleted { get; set; } + public DateTime? DeletionTime { get; set; } + } +} diff --git a/JiShe.CollectBus.EntityFrameworkCore/Entities/ICreationAudited.cs b/JiShe.CollectBus.EntityFrameworkCore/Entities/ICreationAudited.cs new file mode 100644 index 0000000..2ce1d52 --- /dev/null +++ b/JiShe.CollectBus.EntityFrameworkCore/Entities/ICreationAudited.cs @@ -0,0 +1,22 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.EntityFrameworkCore.Entities +{ + public interface ICreationAudited : IHasCreator, IHasCreationTime + { + } + + public interface IHasCreator + { + TUser CreatorId { get; set; } + } + + public interface IHasCreationTime + { + DateTime CreationTime { get; set; } + } +} diff --git a/JiShe.CollectBus.EntityFrameworkCore/Entities/IDeletionAudited.cs b/JiShe.CollectBus.EntityFrameworkCore/Entities/IDeletionAudited.cs new file mode 100644 index 0000000..027f987 --- /dev/null +++ b/JiShe.CollectBus.EntityFrameworkCore/Entities/IDeletionAudited.cs @@ -0,0 +1,27 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.EntityFrameworkCore.Entities +{ + public interface IDeletionAudited : IHasDeleter, ISoftDelete, IHasDeletionTime + { + } + + public interface ISoftDelete + { + bool IsDeleted { get; set; } + } + + public interface IHasDeleter + { + TUser DeleterId { get; set; } + } + + public interface IHasDeletionTime + { + DateTime? DeletionTime { get; set; } + } +} diff --git a/JiShe.CollectBus.EntityFrameworkCore/Entities/IEntity.cs b/JiShe.CollectBus.EntityFrameworkCore/Entities/IEntity.cs new file mode 100644 index 0000000..88f25ed --- /dev/null +++ b/JiShe.CollectBus.EntityFrameworkCore/Entities/IEntity.cs @@ -0,0 +1,13 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.EntityFrameworkCore.Entities +{ + public interface IEntity + { + TKey Id { get; } + } +} diff --git a/JiShe.CollectBus.EntityFrameworkCore/Entities/IExtendableObject.cs b/JiShe.CollectBus.EntityFrameworkCore/Entities/IExtendableObject.cs new file mode 100644 index 0000000..79c857b --- /dev/null +++ b/JiShe.CollectBus.EntityFrameworkCore/Entities/IExtendableObject.cs @@ -0,0 +1,20 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.EntityFrameworkCore.Entities +{ + public interface IExtendableObject + { + /// + /// Gets or sets the extension data. + /// + /// + /// The extension data. + /// + string ExtensionData { get; set; } + } +} diff --git a/JiShe.CollectBus.EntityFrameworkCore/Entities/IModificationAudited.cs b/JiShe.CollectBus.EntityFrameworkCore/Entities/IModificationAudited.cs new file mode 100644 index 0000000..8db2e32 --- /dev/null +++ b/JiShe.CollectBus.EntityFrameworkCore/Entities/IModificationAudited.cs @@ -0,0 +1,22 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace JiShe.CollectBus.EntityFrameworkCore.Entities +{ + public interface IModificationAudited : IHasLastModifier, IHasLastModificationTime + { + } + + public interface IHasLastModifier + { + TUser LastModifierId { get; set; } + } + + public interface IHasLastModificationTime + { + DateTime? LastModificationTime { get; set; } + } +} diff --git a/JiShe.CollectBus.EntityFrameworkCore/Extensions/ExtendableObjectExtensions.cs b/JiShe.CollectBus.EntityFrameworkCore/Extensions/ExtendableObjectExtensions.cs new file mode 100644 index 0000000..74f8916 --- /dev/null +++ b/JiShe.CollectBus.EntityFrameworkCore/Extensions/ExtendableObjectExtensions.cs @@ -0,0 +1,153 @@ +锘縰sing JiShe.CollectBus.EntityFrameworkCore.Entities; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; +using JetBrains.Annotations; +using JiShe.CollectBus.Common.Helpers; +using Newtonsoft.Json.Linq; +using Formatting = Newtonsoft.Json.Formatting; + +namespace JiShe.CollectBus.EntityFrameworkCore.Extensions +{ + public static class ExtendableObjectExtensions + { + public static T GetData(this IExtendableObject extendableObject, string name, bool handleType = false) + { + return extendableObject.GetData( + name, + handleType + ? new JsonSerializer { TypeNameHandling = TypeNameHandling.All } + : JsonSerializer.CreateDefault() + ); + } + + public static T GetData(this IExtendableObject extendableObject, string name, JsonSerializer? jsonSerializer) + { + if (extendableObject.ExtensionData == null) + { + return default(T); + } + + var json = JObject.Parse(extendableObject.ExtensionData); + + var prop = json[name]; + if (prop == null) + { + return default(T); + } + + if (TypeHelper.IsPrimitiveExtendedIncludingNullable(typeof(T))) + { + return prop.Value(); + } + else + { + return (T)prop.ToObject(typeof(T), jsonSerializer ?? JsonSerializer.CreateDefault()); + } + } + + + + public static void SetData(this IExtendableObject extendableObject, string name, T? value, bool handleType = false) + { + extendableObject.SetData( + name, + value, + handleType + ? new JsonSerializer { TypeNameHandling = TypeNameHandling.All } + : JsonSerializer.CreateDefault() + ); + } + + public static void SetData(this IExtendableObject extendableObject, string name, T? value, JsonSerializer? jsonSerializer) + { + if (extendableObject == null) + { + throw new ArgumentNullException(nameof(extendableObject)); + } + + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + if (jsonSerializer == null) + { + jsonSerializer = JsonSerializer.CreateDefault(); + } + + if (extendableObject.ExtensionData == null) + { + if (EqualityComparer.Default.Equals(value, default(T))) + { + return; + } + + extendableObject.ExtensionData = "{}"; + } + + var json = JObject.Parse(extendableObject.ExtensionData); + + if (value == null || EqualityComparer.Default.Equals(value, default(T))) + { + if (json[name] != null) + { + json.Remove(name); + } + } + else if (TypeHelper.IsPrimitiveExtendedIncludingNullable(value.GetType())) + { + json[name] = new JValue(value); + } + else + { + json[name] = JToken.FromObject(value, jsonSerializer); + } + + var data = json.ToString(Formatting.None); + if (data == "{}") + { + data = null; + } + + extendableObject.ExtensionData = data; + } + + public static bool RemoveData(this IExtendableObject extendableObject, string name) + { + if (extendableObject == null) + { + throw new ArgumentNullException(nameof(extendableObject)); + } + + if (extendableObject.ExtensionData == null) + { + return false; + } + + var json = JObject.Parse(extendableObject.ExtensionData); + + var token = json[name]; + if (token == null) + { + return false; + } + + json.Remove(name); + + var data = json.ToString(Formatting.None); + if (data == "{}") + { + data = null; + } + + extendableObject.ExtensionData = data; + + return true; + } + } +} diff --git a/JiShe.CollectBus.EntityFrameworkCore/JiShe.CollectBus.EntityFrameworkCore.csproj b/JiShe.CollectBus.EntityFrameworkCore/JiShe.CollectBus.EntityFrameworkCore.csproj new file mode 100644 index 0000000..dc31f27 --- /dev/null +++ b/JiShe.CollectBus.EntityFrameworkCore/JiShe.CollectBus.EntityFrameworkCore.csproj @@ -0,0 +1,18 @@ +锘 + + + net8.0 + enable + enable + + + + + + + + + + + + diff --git a/JiShe.CollectBus.EntityFrameworkCore/Repositories/IRepository.cs b/JiShe.CollectBus.EntityFrameworkCore/Repositories/IRepository.cs new file mode 100644 index 0000000..65c95fd --- /dev/null +++ b/JiShe.CollectBus.EntityFrameworkCore/Repositories/IRepository.cs @@ -0,0 +1,62 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading.Tasks; +using JiShe.CollectBus.EntityFrameworkCore.Entities; + +namespace JiShe.CollectBus.EntityFrameworkCore.Repositories +{ + public interface IRepository where TEntity : class, IEntity + { + IQueryable GetAll(params Expression>[] propertySelectors); + + IQueryable GetAll(Expression> expression, + params Expression>[] propertySelectors); + + TEntity Find(TPrimaryKey id); + + Task FindAsync(TPrimaryKey id); + + TEntity Get(Expression> expression, + params Expression>[] propertySelectors); + + Task GetAsync(Expression> expression, + params Expression>[] propertySelectors); + + void Insert(TEntity entity, bool autoSave = true); + + Task InsertAsync(TEntity entity, bool autoSave = true); + + Task InsertAndGetIdAsync(TEntity entity, bool autoSave = true); + + void InsertList(List entities, bool autoSave = true); + + Task InsertListAsync(List entities, bool autoSave = true); + + void Update(TEntity entity, bool autoSave = true); + + Task UpdateAsync(TEntity entity, bool autoSave = true); + + void UpdateList(IEnumerable entities); + + Task UpdateListAsync(IEnumerable entities); + + void Delete(TPrimaryKey id, bool autoSave = true); + + Task DeleteAsync(TPrimaryKey id, bool autoSave = true); + + void Delete(TEntity entity, bool autoSave = true); + + Task DeleteAsync(TEntity entity, bool autoSave = true); + + void HardDelete(TPrimaryKey id, bool autoSave = true); + + Task HardDeleteAsync(TPrimaryKey id, bool autoSave = true); + + void HardDelete(TEntity entity, bool autoSave = true); + + Task HardDeleteAsync(TEntity entity, bool autoSave = true); + } +} diff --git a/JiShe.CollectBus.EntityFrameworkCore/Repositories/Repository.cs b/JiShe.CollectBus.EntityFrameworkCore/Repositories/Repository.cs new file mode 100644 index 0000000..ce1ad5e --- /dev/null +++ b/JiShe.CollectBus.EntityFrameworkCore/Repositories/Repository.cs @@ -0,0 +1,296 @@ +锘縰sing System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading.Tasks; +using JiShe.CollectBus.Common.Extensions; +using JiShe.CollectBus.EntityFrameworkCore.Entities; +using Microsoft.EntityFrameworkCore; + +namespace JiShe.CollectBus.EntityFrameworkCore.Repositories +{ + public class Repository : IRepository + where TEntity : class, IEntity + where TDbContext : DbContext + { + public virtual DbContext DbContext { private set; get; } + + public virtual DbSet Table => DbContext.Set(); + + public Repository(DbContext dbContext) + { + DbContext = dbContext; + } + + public virtual IQueryable GetAll(params Expression>[] propertySelectors) + { + var query = Table.AsNoTracking(); + if (!propertySelectors.IsNullOrEmpty()) + { + foreach (var propertySelector in propertySelectors) + { + query = query.Include(propertySelector); + } + } + return query; + } + + public virtual IQueryable GetAll(Expression> expression, params Expression>[] propertySelectors) + { + var query = Table.Where(expression); + if (!propertySelectors.IsNullOrEmpty()) + { + foreach (var propertySelector in propertySelectors) + { + query = query.Include(propertySelector); + } + } + return query; + } + + public virtual TEntity Find(TPrimaryKey id) + { + return Table.Find(id); + } + + public virtual async Task FindAsync(TPrimaryKey id) + { + return await Table.FindAsync(id); + } + + public virtual TEntity Get(Expression> expression, params Expression>[] propertySelectors) + { + var query = GetAll(); + if (!propertySelectors.IsNullOrEmpty()) + { + foreach (var propertySelector in propertySelectors) + { + query = query.Include(propertySelector); + } + return query.FirstOrDefault(expression); + } + return Table.FirstOrDefault(expression); + } + + public virtual async Task GetAsync(Expression> expression, params Expression>[] propertySelectors) + { + var query = GetAll(); + if (!propertySelectors.IsNullOrEmpty()) + { + foreach (var propertySelector in propertySelectors) + { + query = query.Include(propertySelector); + } + return await query.FirstOrDefaultAsync(expression); + } + return await Table.FirstOrDefaultAsync(expression); + } + + public virtual void Insert(TEntity entity, bool autoSave = true) + { + var hasCreationTime = typeof(IHasCreationTime).IsAssignableFrom(typeof(TEntity)); + if (hasCreationTime) + { + ((IHasCreationTime)entity).CreationTime = DateTime.Now; + } + ((IHasCreationTime)entity).CreationTime = DateTime.Now; + Table.Add(entity); + if (autoSave) + { + DbContext.SaveChanges(); + } + } + + public virtual async Task InsertAsync(TEntity entity, bool autoSave = true) + { + var hasCreationTime = typeof(IHasCreationTime).IsAssignableFrom(typeof(TEntity)); + if (hasCreationTime) + { + ((IHasCreationTime)entity).CreationTime = DateTime.Now; + } + await Table.AddAsync(entity); + if (autoSave) + { + await DbContext.SaveChangesAsync(); + } + } + + public virtual async Task InsertAndGetIdAsync(TEntity entity, bool autoSave = true) + { + var hasCreationTime = typeof(IHasCreationTime).IsAssignableFrom(typeof(TEntity)); + if (hasCreationTime) + { + ((IHasCreationTime)entity).CreationTime = DateTime.Now; + } + await Table.AddAsync(entity); + if (autoSave) + { + await DbContext.SaveChangesAsync(); + } + return entity.Id; + } + + public virtual void InsertList(List entities, bool autoSave = true) + { + var hasCreationTime = typeof(IHasCreationTime).IsAssignableFrom(typeof(TEntity)); + if (hasCreationTime) + { + foreach (var entity in entities) + { + ((IHasCreationTime)entity).CreationTime = DateTime.Now; + } + } + Table.AddRange(entities); + if (autoSave) + { + DbContext.SaveChanges(); + } + } + + public virtual async Task InsertListAsync(List entities, bool autoSave = true) + { + var hasCreationTime = typeof(IHasCreationTime).IsAssignableFrom(typeof(TEntity)); + if (hasCreationTime) + { + foreach (var entity in entities) + { + ((IHasCreationTime)entity).CreationTime = DateTime.Now; + } + } + await Table.AddRangeAsync(entities); + if (autoSave) + { + await DbContext.SaveChangesAsync(); + } + } + + public virtual void Update(TEntity entity, bool autoSave = true) + { + var hasLastModificationTime = typeof(IHasLastModificationTime).IsAssignableFrom(typeof(TEntity)); + if (hasLastModificationTime) + { + ((IHasLastModificationTime)entity).LastModificationTime = DateTime.Now; + } + Table.Update(entity); + if (autoSave) + { + DbContext.SaveChanges(); + } + } + + public virtual async Task UpdateAsync(TEntity entity, bool autoSave = true) + { + var hasLastModificationTime = typeof(IHasLastModificationTime).IsAssignableFrom(typeof(TEntity)); + if (hasLastModificationTime) + { + ((IHasLastModificationTime)entity).LastModificationTime = DateTime.Now; + } + Table.Update(entity); + if (autoSave) + { + await DbContext.SaveChangesAsync(); + } + } + + public virtual void UpdateList(IEnumerable entities) + { + Table.UpdateRange(entities); + DbContext.SaveChanges(); + } + + public virtual async Task UpdateListAsync(IEnumerable entities) + { + Table.UpdateRange(entities); + await DbContext.SaveChangesAsync(); + } + + public virtual void Delete(TPrimaryKey id, bool autoSave = true) + { + var entity = Find(id); + if (entity == null) + { + throw new ArgumentNullException(); + } + Delete(entity, autoSave); + } + + public virtual async Task DeleteAsync(TPrimaryKey id, bool autoSave = true) + { + var entity = await FindAsync(id); + if (entity == null) + { + throw new ArgumentNullException(); + } + await DeleteAsync(entity, autoSave); + } + + public virtual void Delete(TEntity entity, bool autoSave = true) + { + var hasDeletionTime = typeof(IHasDeletionTime).IsAssignableFrom(typeof(TEntity)); + if (hasDeletionTime) + { + ((IHasDeletionTime)entity).DeletionTime = DateTime.Now; + } + var isDeleteEntity = typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)); + if (isDeleteEntity) + { + ((ISoftDelete)entity).IsDeleted = true; + } + Update(entity, autoSave); + } + + public virtual async Task DeleteAsync(TEntity entity, bool autoSave = true) + { + var hasDeletionTime = typeof(IHasDeletionTime).IsAssignableFrom(typeof(TEntity)); + if (hasDeletionTime) + { + ((IHasDeletionTime)entity).DeletionTime = DateTime.Now; + } + var isDeleteEntity = typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)); + if (isDeleteEntity) + { + ((ISoftDelete)entity).IsDeleted = true; + } + await UpdateAsync(entity, autoSave); + } + + public virtual void HardDelete(TPrimaryKey id, bool autoSave = true) + { + var entity = Find(id); + if (entity == null) + { + throw new ArgumentNullException(); + } + HardDelete(entity, autoSave); + } + + public async Task HardDeleteAsync(TPrimaryKey id, bool autoSave = true) + { + var entity = await FindAsync(id); + if (entity == null) + { + throw new ArgumentNullException(); + } + await HardDeleteAsync(entity, autoSave); + } + + public virtual void HardDelete(TEntity entity, bool autoSave = true) + { + Table.Remove(entity); + if (autoSave) + { + DbContext.SaveChanges(); + } + } + + public virtual async Task HardDeleteAsync(TEntity entity, bool autoSave = true) + { + Table.Remove(entity); + if (autoSave) + { + await DbContext.SaveChangesAsync(); + } + } + } +} diff --git a/JiShe.CollectBus.Protocol/StandardProtocolPlugin.cs b/JiShe.CollectBus.Protocol/StandardProtocolPlugin.cs index 1f7a7a1..91355d1 100644 --- a/JiShe.CollectBus.Protocol/StandardProtocolPlugin.cs +++ b/JiShe.CollectBus.Protocol/StandardProtocolPlugin.cs @@ -4,8 +4,6 @@ using JiShe.CollectBus.Protocol.Contracts.Attributes; using JiShe.CollectBus.Protocol.Contracts.DependencyInjection; using JiShe.CollectBus.Protocol.Contracts.Models; using Microsoft.Extensions.Caching.Distributed; -using System.Data; -using System.Net.Sockets; using TouchSocket.Sockets; namespace JiShe.CollectBus.Protocol diff --git a/JiShe.CollectBus.sln b/JiShe.CollectBus.sln index 9b3bf6f..7e140af 100644 --- a/JiShe.CollectBus.sln +++ b/JiShe.CollectBus.sln @@ -17,7 +17,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{3A04FB29-E EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "core", "core", "{C7DEC9FB-3F75-4584-85B0-16EA3CB222E5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.Protocol.Test", "JiShe.CollectBus.Protocol.Test\JiShe.CollectBus.Protocol.Test.csproj", "{289196B4-FFBE-4E40-A3A1-FCFADBE945ED}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.Protocol.Test", "JiShe.CollectBus.Protocol.Test\JiShe.CollectBus.Protocol.Test.csproj", "{289196B4-FFBE-4E40-A3A1-FCFADBE945ED}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.EntityFrameworkCore", "JiShe.CollectBus.EntityFrameworkCore\JiShe.CollectBus.EntityFrameworkCore.csproj", "{16D42BCF-EDB8-4153-B37D-0B10FB6DF36C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -49,6 +51,10 @@ Global {289196B4-FFBE-4E40-A3A1-FCFADBE945ED}.Debug|Any CPU.Build.0 = Debug|Any CPU {289196B4-FFBE-4E40-A3A1-FCFADBE945ED}.Release|Any CPU.ActiveCfg = Release|Any CPU {289196B4-FFBE-4E40-A3A1-FCFADBE945ED}.Release|Any CPU.Build.0 = Release|Any CPU + {16D42BCF-EDB8-4153-B37D-0B10FB6DF36C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {16D42BCF-EDB8-4153-B37D-0B10FB6DF36C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {16D42BCF-EDB8-4153-B37D-0B10FB6DF36C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {16D42BCF-EDB8-4153-B37D-0B10FB6DF36C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -60,6 +66,7 @@ Global {4468B52D-3AAE-4918-B4D6-E6E8F000825D} = {C7DEC9FB-3F75-4584-85B0-16EA3CB222E5} {1D3A5A4E-B977-4E33-A1AF-62508110C3B7} = {C7DEC9FB-3F75-4584-85B0-16EA3CB222E5} {289196B4-FFBE-4E40-A3A1-FCFADBE945ED} = {3A04FB29-EA75-4499-BBF3-AF24C7D46A1D} + {16D42BCF-EDB8-4153-B37D-0B10FB6DF36C} = {C7DEC9FB-3F75-4584-85B0-16EA3CB222E5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {33261859-9CD1-4A43-B181-AB75C247D1CD}