剛過了個五一,在杭州處處看房子,不知道杭州最近怎麼了,杭州買房的人這麼多,房價漲得太厲害,這幾年翻倍翻倍地漲,剛過G20,又要亞運會,讓我這樣的剛需用戶買不起,也買不到房子,搞得人心惶惶,太恐怖了,心好累。git
這幾天,由於這件事情感受人都是懵的,沒法靜心學習複雜的東西,因此就看看一些基礎,學習學習NopCommerce的CommonHelper都寫了些啥,看看別人的代碼巧在哪裏,妙在哪裏。github
nopCommerce是最好的開源電子商務購物 系統。nopCommerce免費提供。今天,它是最好和最流行的ASP.NET電子商務軟件。它已被下載超過180萬次!正則表達式
nopCommerce是一個徹底可定製的購物系統。它穩定且高度可用。nopCommerce是一個開源的電子商務解決方案,它是基於MS SQL 2008(或更高版本)後端數據庫的ASP.NET(MVC)。咱們易於使用的購物車解決方案特別適合已經超過現有系統的商家,並可能與您當前的網站託管商或咱們的託管合做夥伴一塊兒託管。它擁有開始經過互聯網銷售物理和數字產品所需的一切。數據庫
以上解釋引用自該項目的Github :https://github.com/nopSolutions/nopCommerce後端
由於這幾天沒沒法靜心學習該項目的架構,全部只拎出該項目的CommonHelper.cs來談談。數組
#region Fields private static readonly Regex _emailRegex; //we use EmailValidator from FluentValidation. So let's keep them sync - https://github.com/JeremySkinner/FluentValidation/blob/master/src/FluentValidation/Validators/EmailValidator.cs private const string _emailExpression = @"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-||_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+([a-z]+|\d|-|\.{0,1}|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])?([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$"; #endregion #region Methods /// <summary> /// 檢查Email(是否爲空,是否超長,格式是否規範) /// </summary> /// <param name="email">The email.</param> /// <returns></returns> public static string EnsureSubscriberEmailOrThrow(string email) { var output = EnsureNotNull(email); output = output.Trim(); output = EnsureMaximumLength(output, 255); if (!IsValidEmail(output)) { throw new NopException("Email is not valid."); } return output; } /// <summary> /// 用正則表達式校驗Email /// </summary> /// <param name="email">Email to verify</param> /// <returns>true if the string is a valid e-mail address and false if it's not</returns> public static bool IsValidEmail(string email) { if (string.IsNullOrEmpty(email)) return false; email = email.Trim(); return _emailRegex.IsMatch(email); }
這個E-mial校驗的方法基本是能夠直接拿過來用的,校驗的正則表達式也很全面,須要用的時候能夠過來copy。架構
/// <summary> /// 檢查該字符串是不是可用的Ip地址 /// </summary> /// <param name="ipAddress">IPAddress to verify</param> /// <returns>true if the string is a valid IpAddress and false if it's not</returns> public static bool IsValidIpAddress(string ipAddress) { return IPAddress.TryParse(ipAddress, out IPAddress _); }
直接使用了系統自帶的IPAddress.TryParse方法,不少小夥伴還不知道吧!併發
/// <summary> /// 產生一個指定長度的隨機數據字符串 /// </summary> /// <param name="length">Length</param> /// <returns>Result string</returns> public static string GenerateRandomDigitCode(int length) { var random = new Random(); var str = string.Empty; for (var i = 0; i < length; i++) str = string.Concat(str, random.Next(10).ToString()); return str; }
這裏的話,其實我以爲用stringbuild比直接用string更好一點,尤爲是當length比較長的時候,可能用stringbuild效率更高一些。app
/// <summary> /// 產生一個隨機數 /// </summary> /// <param name="min">Minimum number</param> /// <param name="max">Maximum number</param> /// <returns>Result</returns> public static int GenerateRandomInteger(int min = 0, int max = int.MaxValue) { var randomNumberBuffer = new byte[10]; new RNGCryptoServiceProvider().GetBytes(randomNumberBuffer); return new Random(BitConverter.ToInt32(randomNumberBuffer, 0)).Next(min, max); }
當時不懂Random工做原理的時候,以爲這個方法簡直是脫褲子放P,畫蛇添足,搞得這麼麻煩幹嗎! 直接Random().Next(min,max)不就產生了一個指定範圍的隨機數嗎?幹嗎搞得這麼複雜呢?dom
原來,Random是須要一個隨機數做爲「種子」的,當這個種子相同時,那麼產生的隨機數也是相同的,有同窗確定會說,咱們平時用的時候沒有指定「種子」數據,也能產生我想要的隨機數啊! 其實,當咱們沒有指定「種子」的時候,Random時默認以當前時間做爲種子的,當高併發訪問的狀況下,若是使用時間做爲種子數據,這顯然就頗有可能產生相同的隨機數,這顯然就不那麼「隨機」了,因此該方法看似多餘的方法都只是爲了利用RNGCryptoServiceProvider().GetBytes()產生一個足夠隨機的byte[],而後再把該byte[]轉換成數字,那麼該數字就能基本不會重複了,也就是」種子」不重複,因此隨機數也不會重複了。
/// <summary> /// 檢查兩個數組是否相等 /// </summary> /// <typeparam name="T">Type</typeparam> /// <param name="a1">Array 1</param> /// <param name="a2">Array 2</param> /// <returns>Result</returns> public static bool ArraysEqual<T>(T[] a1, T[] a2) { //also see Enumerable.SequenceEqual(a1, a2); if (ReferenceEquals(a1, a2)) return true; if (a1 == null || a2 == null) return false; if (a1.Length != a2.Length) return false; var comparer = EqualityComparer<T>.Default; for (var i = 0; i < a1.Length; i++) { if (!comparer.Equals(a1[i], a2[i])) return false; } return true; }
搜先檢測地址引用是否相同,若是相同,確定時同一個對象,那麼相等,而後是檢測時否爲空….. 代碼很簡單,就不一一說了,咱們比較的時候,容易遺忘一些條件,直接走到了for循環最後一步,其實,不到無可奈何,沒不要for循環。
/// <summary> ///給對象的指定屬性賦值 /// </summary> /// <param name="instance">The object whose property to set.</param> /// <param name="propertyName">The name of the property to set.</param> /// <param name="value">The value to set the property to.</param> public static void SetProperty(object instance, string propertyName, object value) { if (instance == null) throw new ArgumentNullException(nameof(instance)); if (propertyName == null) throw new ArgumentNullException(nameof(propertyName)); var instanceType = instance.GetType(); var pi = instanceType.GetProperty(propertyName); if (pi == null) throw new NopException("No property '{0}' found on the instance of type '{1}'.", propertyName, instanceType); if (!pi.CanWrite) throw new NopException("The property '{0}' on the instance of type '{1}' does not have a setter.", propertyName, instanceType); if (value != null && !value.GetType().IsAssignableFrom(pi.PropertyType)) value = To(value, pi.PropertyType); pi.SetValue(instance, value, new object[0]); }
我也是第一次知道,竟然還能這麼玩。
/// <summary> /// 將一個值轉換成目標類型。 /// </summary> /// <param name="value">The value to convert.</param> /// <param name="destinationType">The type to convert the value to.</param> /// <returns>The converted value.</returns> public static object To(object value, Type destinationType) { return To(value, destinationType, CultureInfo.InvariantCulture); } /// <summary> /// 將一個值轉換成目標類型. /// </summary> /// <param name="value">The value to convert.</param> /// <param name="destinationType">The type to convert the value to.</param> /// <param name="culture">Culture</param> /// <returns>The converted value.</returns> public static object To(object value, Type destinationType, CultureInfo culture) { if (value != null) { var sourceType = value.GetType(); var destinationConverter = TypeDescriptor.GetConverter(destinationType); if (destinationConverter != null && destinationConverter.CanConvertFrom(value.GetType())) return destinationConverter.ConvertFrom(null, culture, value); var sourceConverter = TypeDescriptor.GetConverter(sourceType); if (sourceConverter != null && sourceConverter.CanConvertTo(destinationType)) return sourceConverter.ConvertTo(null, culture, value, destinationType); if (destinationType.IsEnum && value is int) return Enum.ToObject(destinationType, (int)value); if (!destinationType.IsInstanceOfType(value)) return Convert.ChangeType(value, destinationType, culture); } return value; } /// <summary> /// 將一個值轉換成目標類型 /// </summary> /// <param name="value">The value to convert.</param> /// <typeparam name="T">The type to convert the value to.</typeparam> /// <returns>The converted value.</returns> public static T To<T>(object value) { //return (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture); return (T)To(value, typeof(T)); }
有了這個方法,咱們就不用傻傻想着用Convent….到底Convernt點什麼呢?哈哈,直接To<T>(),是否是很帥?
/// <summary> /// 深度優先的遞歸刪除 /// </summary> /// <param name="path">Directory path</param> public static void DeleteDirectory(string path) { if (string.IsNullOrEmpty(path)) throw new ArgumentNullException(path); //find more info about directory deletion //and why we use this approach at https://stackoverflow.com/questions/329355/cannot-delete-directory-with-directory-deletepath-true foreach (var directory in Directory.GetDirectories(path)) { DeleteDirectory(directory); } try { Directory.Delete(path, true); } catch (IOException) { Directory.Delete(path, true); } catch (UnauthorizedAccessException) { Directory.Delete(path, true); } }
一開始,我也不明白爲何要弄得這麼複雜,要刪除目錄,直接Directory.Delete(path)就行了不是嗎? 其實不是的,若是目錄不爲空,會報System.IO.IOException: The directory is not empty.的錯誤的,因此要遞歸,層層刪除,聽說Win系統的資源管理器,刪除目錄,其實就是這個邏輯實現的。
/// <summary> /// 獲取兩個時間之間相差的年份 /// </summary> /// <param name="startDate"></param> /// <param name="endDate"></param> /// <returns></returns> public static int GetDifferenceInYears(DateTime startDate, DateTime endDate) { //source: http://stackoverflow.com/questions/9/how-do-i-calculate-someones-age-in-c //this assumes you are looking for the western idea of age and not using East Asian reckoning. var age = endDate.Year - startDate.Year; if (startDate > endDate.AddYears(-age)) age--; return age; }
對,若是endDate.Year - startDate.Year是不對的,就好像你是去年的8月份出生的,而如今才五月份,那麼你如今還不能稱爲1歲同樣的道理。一樣的方法還能夠用來獲取月、日、時、分、秒的間隔。
/// <summary> /// 映射虛擬路徑到物理路徑 /// </summary> /// <param name="path">The path to map. E.g. "~/bin"</param> /// <returns>The physical path. E.g. "c:\inetpub\wwwroot\bin"</returns> public static string MapPath(string path) { path = path.Replace("~/", "").TrimStart('/').Replace('/', '\\');
CommonHelper.BaseDirectory = hostingEnvironment.ContentRootPath;
return Path.Combine(BaseDirectory ?? string.Empty, path); }
大體就是這麼多,可能以上方法你們都知道,可是本身寫出來可能不夠巧妙(老江湖除外),記錄下來,但願對你們有所幫助,也用於自我加深映像。
有興趣的能夠去github上下載nopcommerce的源碼來看看。
另外,杭州買房,預算有限,杭州城區估計是買不起了,如今在糾結海寧、安吉、德清、桐鄉、桐廬等這樣的周邊地區,若是有杭州有買房經驗的同行大哥但願能給小弟一點指點,哈哈~~