NopCommerce开源项目中很基础但是很实用的C# Helper方法

/ 默认分类 / 没有评论 / 293 浏览

刚过了个五一,在杭州到处看房子,不知道杭州最近怎么了,杭州买房的人这么多,房价涨得太厉害,这几年翻倍翻倍地涨,刚过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
    <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">readonly</span><span style="color: #000000;"> Regex _emailRegex;
    </span><span style="color: #008000;">//</span><span style="color: #008000;">we use EmailValidator from FluentValidation. So let's keep them sync - </span><span style="text-decoration: underline; color: #008000;">https://github.com/JeremySkinner/FluentValidation/blob/master/src/FluentValidation/Validators/EmailValidator.cs</span>
    <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">string</span> _emailExpression = <span style="color: #800000;">@"</span><span style="color: #800000;">^((([a-z]|\d|[!#\$%&amp;'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&amp;'\*\+\-\/=\?\^_`{\|}~]|[\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])))$</span><span style="color: #800000;">"</span><span style="color: #000000;">;

    </span><span style="color: #0000ff;">#endregion</span>

   

    <span style="color: #0000ff;">#region</span> Methods

    <span style="color: #808080;">///</span> <span style="color: #808080;">&lt;summary&gt;</span>
    <span style="color: #808080;">///</span><span style="color: #008000;"> 检查Email(是否为空,是否超长,格式是否规范)
    </span><span style="color: #808080;">///</span> <span style="color: #808080;">&lt;/summary&gt;</span>
    <span style="color: #808080;">///</span> <span style="color: #808080;">&lt;param name="email"&gt;</span><span style="color: #008000;">The email.</span><span style="color: #808080;">&lt;/param&gt;</span>
    <span style="color: #808080;">///</span> <span style="color: #808080;">&lt;returns&gt;&lt;/returns&gt;</span>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">string</span> EnsureSubscriberEmailOrThrow(<span style="color: #0000ff;">string</span><span style="color: #000000;"> email)
    {
        </span><span style="color: #0000ff;">var</span> output =<span style="color: #000000;"> EnsureNotNull(email);
        output </span>=<span style="color: #000000;"> output.Trim();
        output </span>= EnsureMaximumLength(output, <span style="color: #800080;">255</span><span style="color: #000000;">);

        </span><span style="color: #0000ff;">if</span> (!<span style="color: #000000;">IsValidEmail(output))
        {
            </span><span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span> NopException(<span style="color: #800000;">"</span><span style="color: #800000;">Email is not valid.</span><span style="color: #800000;">"</span><span style="color: #000000;">);
        }

        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> output;
    }

    </span><span style="color: #808080;">///</span> <span style="color: #808080;">&lt;summary&gt;</span>
    <span style="color: #808080;">///</span><span style="color: #008000;"> 用正则表达式校验Email
    </span><span style="color: #808080;">///</span> <span style="color: #808080;">&lt;/summary&gt;</span>
    <span style="color: #808080;">///</span> <span style="color: #808080;">&lt;param name="email"&gt;</span><span style="color: #008000;">Email to verify</span><span style="color: #808080;">&lt;/param&gt;</span>
    <span style="color: #808080;">///</span> <span style="color: #808080;">&lt;returns&gt;</span><span style="color: #008000;">true if the string is a valid e-mail address and false if it's not</span><span style="color: #808080;">&lt;/returns&gt;</span>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">bool</span> IsValidEmail(<span style="color: #0000ff;">string</span><span style="color: #000000;"> email)
    {
        </span><span style="color: #0000ff;">if</span> (<span style="color: #0000ff;">string</span><span style="color: #000000;">.IsNullOrEmpty(email))
            </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span><span style="color: #000000;">;

        email </span>=<span style="color: #000000;"> email.Trim();

        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> _emailRegex.IsMatch(email);
    }</span></pre>

这个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;
        </span><span style="color: #0000ff;">if</span> (a1 == <span style="color: #0000ff;">null</span> || a2 == <span style="color: #0000ff;">null</span><span style="color: #000000;">)
            </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span><span style="color: #000000;">;

        </span><span style="color: #0000ff;">if</span> (a1.Length !=<span style="color: #000000;"> a2.Length)
            </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span><span style="color: #000000;">;

        </span><span style="color: #0000ff;">var</span> comparer = EqualityComparer&lt;T&gt;<span style="color: #000000;">.Default;
        </span><span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">var</span> i = <span style="color: #800080;">0</span>; i &lt; a1.Length; i++<span style="color: #000000;">)
        {
            </span><span style="color: #0000ff;">if</span> (!comparer.Equals(a1[i], a2[i])) <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span><span style="color: #000000;">;
        }
        </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">true</span><span style="color: #000000;">;
    }</span></pre>

搜先检测地址引用是否相同,如果相同,肯定时同一个对象,那么相等,然后是检测时否为空…..   代码很简单,就不一一说了,我们比较的时候,容易遗忘一些条件,直接走到了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));
        </span><span style="color: #0000ff;">var</span> instanceType =<span style="color: #000000;"> instance.GetType();
        </span><span style="color: #0000ff;">var</span> pi =<span style="color: #000000;"> instanceType.GetProperty(propertyName);
        </span><span style="color: #0000ff;">if</span> (pi == <span style="color: #0000ff;">null</span><span style="color: #000000;">)
            </span><span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span> NopException(<span style="color: #800000;">"</span><span style="color: #800000;">No property '{0}' found on the instance of type '{1}'.</span><span style="color: #800000;">"</span><span style="color: #000000;">, propertyName, instanceType);
        </span><span style="color: #0000ff;">if</span> (!<span style="color: #000000;">pi.CanWrite)
            </span><span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span> NopException(<span style="color: #800000;">"</span><span style="color: #800000;">The property '{0}' on the instance of type '{1}' does not have a setter.</span><span style="color: #800000;">"</span><span style="color: #000000;">, propertyName, instanceType);
        </span><span style="color: #0000ff;">if</span> (value != <span style="color: #0000ff;">null</span> &amp;&amp; !<span style="color: #000000;">value.GetType().IsAssignableFrom(pi.PropertyType))
            value </span>=<span style="color: #000000;"> To(value, pi.PropertyType);
        pi.SetValue(instance, value, </span><span style="color: #0000ff;">new</span> <span style="color: #0000ff;">object</span>[<span style="color: #800080;">0</span><span style="color: #000000;">]);
    }</span></pre>

我也是第一次知道,居然还能这么玩。

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);
        }
    </span><span style="color: #808080;">///</span> <span style="color: #808080;">&lt;summary&gt;</span>
    <span style="color: #808080;">///</span><span style="color: #008000;">  将一个值转换成目标类型.
    </span><span style="color: #808080;">///</span> <span style="color: #808080;">&lt;/summary&gt;</span>
    <span style="color: #808080;">///</span> <span style="color: #808080;">&lt;param name="value"&gt;</span><span style="color: #008000;">The value to convert.</span><span style="color: #808080;">&lt;/param&gt;</span>
    <span style="color: #808080;">///</span> <span style="color: #808080;">&lt;param name="destinationType"&gt;</span><span style="color: #008000;">The type to convert the value to.</span><span style="color: #808080;">&lt;/param&gt;</span>
    <span style="color: #808080;">///</span> <span style="color: #808080;">&lt;param name="culture"&gt;</span><span style="color: #008000;">Culture</span><span style="color: #808080;">&lt;/param&gt;</span>
    <span style="color: #808080;">///</span> <span style="color: #808080;">&lt;returns&gt;</span><span style="color: #008000;">The converted value.</span><span style="color: #808080;">&lt;/returns&gt;</span>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">object</span> To(<span style="color: #0000ff;">object</span><span style="color: #000000;"> value, Type destinationType, CultureInfo culture)
    {
        </span><span style="color: #0000ff;">if</span> (value != <span style="color: #0000ff;">null</span><span style="color: #000000;">)
        {
            </span><span style="color: #0000ff;">var</span> sourceType =<span style="color: #000000;"> value.GetType();

            </span><span style="color: #0000ff;">var</span> destinationConverter =<span style="color: #000000;"> TypeDescriptor.GetConverter(destinationType);
            </span><span style="color: #0000ff;">if</span> (destinationConverter != <span style="color: #0000ff;">null</span> &amp;&amp;<span style="color: #000000;"> destinationConverter.CanConvertFrom(value.GetType()))
                </span><span style="color: #0000ff;">return</span> destinationConverter.ConvertFrom(<span style="color: #0000ff;">null</span><span style="color: #000000;">, culture, value);

            </span><span style="color: #0000ff;">var</span> sourceConverter =<span style="color: #000000;"> TypeDescriptor.GetConverter(sourceType);
            </span><span style="color: #0000ff;">if</span> (sourceConverter != <span style="color: #0000ff;">null</span> &amp;&amp;<span style="color: #000000;"> sourceConverter.CanConvertTo(destinationType))
                </span><span style="color: #0000ff;">return</span> sourceConverter.ConvertTo(<span style="color: #0000ff;">null</span><span style="color: #000000;">, culture, value, destinationType);

            </span><span style="color: #0000ff;">if</span> (destinationType.IsEnum &amp;&amp; value <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">int</span><span style="color: #000000;">)
                </span><span style="color: #0000ff;">return</span> Enum.ToObject(destinationType, (<span style="color: #0000ff;">int</span><span style="color: #000000;">)value);

            </span><span style="color: #0000ff;">if</span> (!<span style="color: #000000;">destinationType.IsInstanceOfType(value))
                </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> Convert.ChangeType(value, destinationType, culture);
        }
        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> value;
    }

    </span><span style="color: #808080;">///</span> <span style="color: #808080;">&lt;summary&gt;</span>
    <span style="color: #808080;">///</span><span style="color: #008000;"> 将一个值转换成目标类型
    </span><span style="color: #808080;">///</span> <span style="color: #808080;">&lt;/summary&gt;</span>
    <span style="color: #808080;">///</span> <span style="color: #808080;">&lt;param name="value"&gt;</span><span style="color: #008000;">The value to convert.</span><span style="color: #808080;">&lt;/param&gt;</span>
    <span style="color: #808080;">///</span> <span style="color: #808080;">&lt;typeparam name="T"&gt;</span><span style="color: #008000;">The type to convert the value to.</span><span style="color: #808080;">&lt;/typeparam&gt;</span>
    <span style="color: #808080;">///</span> <span style="color: #808080;">&lt;returns&gt;</span><span style="color: #008000;">The converted value.</span><span style="color: #808080;">&lt;/returns&gt;</span>
    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> T To&lt;T&gt;(<span style="color: #0000ff;">object</span><span style="color: #000000;"> value)
    {
        </span><span style="color: #008000;">//</span><span style="color: #008000;">return (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture);</span>
        <span style="color: #0000ff;">return</span> (T)To(value, <span style="color: #0000ff;">typeof</span><span style="color: #000000;">(T));
    }</span></pre>

有了这个方法,我们就不用傻傻想着用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);
        </span><span style="color: #008000;">//</span><span style="color: #008000;">find more info about directory deletion
        </span><span style="color: #008000;">//</span><span style="color: #008000;">and why we use this approach at </span><span style="text-decoration: underline; color: #008000;">https://stackoverflow.com/questions/329355/cannot-delete-directory-with-directory-deletepath-true</span>

        <span style="color: #0000ff;">foreach</span> (<span style="color: #0000ff;">var</span> directory <span style="color: #0000ff;">in</span><span style="color: #000000;"> Directory.GetDirectories(path))
        {
            DeleteDirectory(directory);
        }

        </span><span style="color: #0000ff;">try</span><span style="color: #000000;">
        {
            Directory.Delete(path, </span><span style="color: #0000ff;">true</span><span style="color: #000000;">);
        }
        </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (IOException)
        {
            Directory.Delete(path, </span><span style="color: #0000ff;">true</span><span style="color: #000000;">);
        }
        </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (UnauthorizedAccessException)
        {
            Directory.Delete(path, </span><span style="color: #0000ff;">true</span><span style="color: #000000;">);
        }
    }</span></pre>

一开始,我也不明白为什么要弄得这么复杂,要删除目录,直接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的源码来看看。

 

另外,杭州买房,预算有限,杭州城区估计是买不起了,现在在纠结海宁、安吉、德清、桐乡、桐庐等这样的周边地区,如果有杭州有买房经验的同行大哥希望能给小弟一点指点,哈哈~~