刚过了个五一, 在杭州到处看房子, 不知道杭州最近怎么了, 杭州买房的人这么多, 房价涨得太厉害, 这几年翻倍翻倍地涨, 刚过 G20, 又要亚运会, 让我这样的刚需用户买不起, 也买不到房子, 搞得人心惶惶, 太恐怖了, 心好累.
这几天, 因为这件事情感觉人都是懵的, 无法静心学习复杂的东西, 所以就看看一些基础, 学习学习 NopCommerce 的 CommonHelper 都写了些啥, 看看别人的代码巧在哪里, 妙在哪里.
NopCommerce 是啥?
nopCommerce 是最好的开源电子商务购物 系统. nopCommerce 免费提供. 今天, 它是最好和最流行的 ASP.NET 电子商务软件. 它已被下载超过 180 万次!
nopCommerce 是一个完全可定制的购物系统. 它稳定且高度可用. nopCommerce 是一个开源的电子商务解决方案, 它是基于 MS SQL 2008(或更高版本) 后端数据库的 ASP.NET(MVC). 我们易于使用的购物车解决方案特别适合已经超过现有系统的商家, 并可能与您当前的网站托管商或我们的托管合作伙伴一起托管. 它拥有开始通过互联网销售物理和数字产品所需的一切.
以上解释引用自该项目的 Github : https://github.com/nopSolutions/nopCommerce
因为这几天没无法静心学习该项目的架构, 所有只拎出该项目的 CommonHelper.cs 来谈谈.
1.E-Mail 校验
- #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.
2.ip 地址校验
- /// <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 方法, 很多小伙伴还不知道吧!
3. 产生指定长度的随机数字字符串
- /// <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 效率更高一些.
4. 产生一个随机数字
- /// <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) 不就产生了一个指定范围的随机数吗? 干嘛搞得这么复杂呢?
原来, Random 是需要一个随机数作为 "种子" 的, 当这个种子相同时, 那么产生的随机数也是相同的, 有同学肯定会说, 我们平时用的时候没有指定 "种子" 数据, 也能产生我想要的随机数啊! 其实, 当我们没有指定 "种子" 的时候, Random 时默认以当前时间作为种子的, 当高并发访问的情况下, 如果使用时间作为种子数据, 这显然就很有可能产生相同的随机数, 这显然就不那么 "随机" 了, 所以该方法看似多余的方法都只是为了利用 RNGCryptoServiceProvider().GetBytes() 产生一个足够随机的 byte[], 然后再把该 byte[] 转换成数字, 那么该数字就能基本不会重复了, 也就是 "种子" 不重复, 所以随机数也不会重复了.
5. 检查两个数组是否相等
- /// <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 循环.
6. 给对象的指定属性赋值
- /// <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]);
- }
我也是第一次知道, 居然还能这么玩.
7. 将一个值转换成目标类型
- /// <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>(), 是不是很帅?
8. 删除目录
- /// <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 系统的资源管理器, 删除目录, 其实就是这个逻辑实现的.
9. 获取两个 Datetime 之间的年份间隔
- /// <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 岁一样的道理. 同样的方法还可以用来获取月, 日, 时, 分, 秒的间隔.
10 虚拟路径转物理路径
- /// <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 的源码来看看.
另外, 杭州买房, 预算有限, 杭州城区估计是买不起了, 现在在纠结海宁, 安吉, 德清, 桐乡, 桐庐等这样的周边地区, 如果有杭州有买房经验的同行大哥希望能给小弟一点指点, 哈哈~~
来源: https://www.cnblogs.com/CoderAyu/p/8982842.html