Use the enumeration class in ASP.Net Core instead of enumeration

Preface: I believe we often encounter various state value when writing the code, and to avoid hard-coding the magic number and code appear, usually we will define an enumeration value to represent the various states, until I saw Java in this way the use of enumeration, I think C #, can you write, you share my sentiment today.

 

First, we usually use this enumeration

(1) switch is used to enumerate

 public enum  EmployeeType
    {
        Manager,
        Servant,
        AssistantToTheRegionalManager
    }
public class Employee
    {
        public EmployeeType Type { get; set; }
        public decimal Bonus { get; set; }
    }
static void ProcessBonus(Employee employee)
        {
            switch (employee.Type)
            {
                case EmployeeType.Manager:
                    employee.Bonus = 1000m;
                    break;
                case EmployeeType.Servant:
                    employee.Bonus = 0.01m;
                    break;
                case EmployeeType.AssistantToTheRegionalManager:
                    employee.Bonus = 1.0m;
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

In the absence of a forward Don I also like to write code sucks, violated the principle of opening and closing, scalability poor. In the code specification is not allowed in the wording of. For the above notation can be used to reconstruct the design pattern. Later articles will continue to update the design patterns.

(2) Conversion Type

EnumTricks.IsVolumeHigh((Volume)27);
EnumTricks.High((int)Medium);

 

Second, enumeration bad place

About Enumeration MSDN document what was said:

“The enum keyword is used to declare an enumeration, a distinct type that consists of a set of named constants called the enumerator list. Every enumeration type has an underlying type, which can be any integral type except char. The default underlying type of the enumeration elements is int. By default, the first enumerator has the value 0, and the value of each successive enumerator is increased by 1.

(1) there is no type of security

Enumeration is a simple value types, can provide protection against an invalid value, and without any behavior. They are useful because they are the magic number of improvements, but that's it. To constrain the type of possible values, the enumeration may not be able to help you, because you can still provide an invalid type. For example, this enumeration has three values, the default type int. Value in the range 1-3.

 public enum Volume
    {
        Low = 1,
        Medium,
        High
    }
public static class EnumTricks
    {
        public static bool IsVolumeHigh(Volume volume)
        {
            var result = false;

            switch (volume)
            {
                case Volume.Low:
                    Console.WriteLine("Volume is low.");
                    break;

                case Volume.Medium:
                    Console.WriteLine("Volume is medium.");
                    break;

                case Volume.High:
                    Console.WriteLine("Volume is high.");
                    result = true;
                    break;
            }

            return result;
        }
    }
 static void Main(string[] args)
        {
            EnumTricks.IsVolumeHigh((Volume)27);



            Console.ReadKey();
        }
public static class EnumTricks
    {
        public static bool IsVolumeHigh(Volume volume)
        {
            var result = false;

            switch (volume)
            {
                case Volume.Low:
                    Console.WriteLine("Volume is low.");
                    break;

                case Volume.Medium:
                    Console.WriteLine("Volume is medium.");
                    break;

                case Volume.High:
                    Console.WriteLine("Volume is high.");
                    result = true;
                    break;
            }

            return result;
        }

        public static int EnumToInt(Volume volume)
        {
            return (int)volume;
        }

        public static Volume IntToEnum(int intValue)
        {
            return (Volume)intValue;
        }

        public static Volume StringToEnum(string stringValue)
        {
            return (Volume)Enum.Parse(typeof(Volume), stringValue);
        }

        public static int StringToInt(string stringValue)
        {
            var volume = StringToEnum(stringValue);
            return EnumToInt(volume);
        }
        public static string EnumToString(Volume volume)
        {
            return volume.ToString();
        }
    }
View Code

 

This should fail, at least at run time. It did not. It's really strange ...... during compile time or runtime will not call an error is detected. You will find yourself in a false sense of security. If we pass in the enumeration converted to string, take a look at what is different in both cases:

 I do not know if you usually use at the time of enumeration, whether consciously check the incoming is a valid value. You may be used Enum.IsDefined () to check whether the value is a valid int value

Solution: If int values ​​within the definition of enumeration values, use Enum.IsDefined () to find. If within the range, it returns True, otherwise False.

(2) Transformation

Have you tried to convert enum to int, int converted to enum, string convert enum, convert a string to an int enum values? The following code:

 public static class EnumTricks
    {
        public static bool IsVolumeHigh(Volume volume)
        {
            var result = false;

            switch (volume)
            {
                case Volume.Low:
                    Console.WriteLine("Volume is low.");
                    break;

                case Volume.Medium:
                    Console.WriteLine("Volume is medium.");
                    break;

                case Volume.High:
                    Console.WriteLine("Volume is high.");
                    result = true;
                    break;
            }

            return result;
        }

        public static int EnumToInt(Volume volume)
        {
            return (int)volume;
        }

        public static Volume IntToEnum(int intValue)
        {
            return (Volume)intValue;
        }

        public static Volume StringToEnum(string stringValue)
        {
            return (Volume)Enum.Parse(typeof(Volume), stringValue);
        }

        public static int StringToInt(string stringValue)
        {
            var volume = StringToEnum(stringValue);
            return EnumToInt(volume);
        }
    }

 Is not our daily code has such a type conversion code is not that bad, just type conversion is also a loss of performance, if we can change the way you can achieve the same but also to avoid the above problem is it better, so our code and better maintenance and expansion, let's solve this problem by using enumerate class.

Third, the use enum class instead of an enumeration type

public class Enumeration: IComparable
    {
        private readonly int _value;
        private readonly string _displayName;

        protected Enumeration()
        {
        }

        protected Enumeration(int value, string displayName)
        {
            _value = value;
            _displayName = displayName;
        }

        public int Value
        {
            get { return _value; }
        }

        public string DisplayName
        {
            get { return _displayName; }
        }

        public override string ToString()
        {
            return DisplayName;
        }

        public static IEnumerable<T> GetAll<T>() where T : Enumeration, new()
        {
            var type = typeof(T);
            var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);

            foreach (var info in fields)
            {
                var instance = new T();
                var locatedValue = info.GetValue(instance) as T;

                if (locatedValue != null)
                {
                    yield return locatedValue;
                }
            }
        }

        public override bool Equals(object obj)
        {
            var otherValue = obj as Enumeration;

            if (otherValue == null)
            {
                return false;
            }

            var typeMatches = GetType().Equals(obj.GetType());
            var valueMatches = _value.Equals(otherValue.Value);

            return typeMatches && valueMatches;
        }

        public override int GetHashCode()
        {
            return _value.GetHashCode();
        }

        public static int AbsoluteDifference(Enumeration firstValue, Enumeration secondValue)
        {
            var absoluteDifference = Math.Abs(firstValue.Value - secondValue.Value);
            return absoluteDifference;
        }

        public static T FromValue<T>(int value) where T : Enumeration, new()
        {
            var matchingItem = parse<T, int>(value, "value", item => item.Value == value);
            return matchingItem;
        }

        public static T FromDisplayName<T>(string displayName) where T : Enumeration, new()
        {
            var matchingItem = parse<T, string>(displayName, "display name", item => item.DisplayName == displayName);
            return matchingItem;
        }

        private static T parse<T, K>(K value, string description, Func<T, bool> predicate) where T : Enumeration, new()
        {
            var matchingItem = GetAll<T>().FirstOrDefault(predicate);

            if (matchingItem == null)
            {
                var message = string.Format("'{0}' is not a valid {1} in {2}", value, description, typeof(T));
                throw new ApplicationException(message);
            }

            return matchingItem;
        }

        public int CompareTo(object other)
        {
            return Value.CompareTo(((Enumeration)other).Value);
        }
    }
View Code
public class Volume: Enumeration
    {
        private Volume() { throw new Exception(""); }
        private Volume(int value, string displayName): base(value, displayName) { }


        public static readonly Volume Low = new Volume(1, nameof(Low).ToLowerInvariant());
        public static readonly Volume Medium = new Volume(2, nameof(Medium).ToLowerInvariant());
        public static readonly Volume High = new Volume(3, nameof(High).ToLowerInvariant());


        public static IEnumerable<Volume> List() =>
            new[] { Low, Medium, High };

        public static Volume From(int value)
        {
            var state = List().SingleOrDefault(s => s.Value == value);

            if (state == null)
            {
                throw new Exception($"Possible values for Volume: {String.Join(",", List().Select(s => s.Value))}");
            }

            return state;
        }

        public static Volume FromName(string name)
        {
            var state = List()
                .SingleOrDefault(s => String.Equals(s.DisplayName, name, StringComparison.CurrentCultureIgnoreCase));

            if (state == null)
            {
                throw new Exception($"Possible values for Volume: {String.Join(",", List().Select(s => s.DisplayName))}");
            }

            return state;
        }
    }
static void Main(string[] args)
        {
            //EnumTricks.IsVolumeHigh((Volume)27);

            //var tmp = Enum.IsDefined(typeof(Volume), 3);
            //var str = EnumTricks.EnumToString((Volume)27);
            //var str2 = EnumTricks.EnumToString((Volume)3);


            //Console.WriteLine($"Volume 27:{str}");
            //Console.WriteLine($"Volume 3:{str2}");

            Console.WriteLine("------------------------------------------------------------");
            
            Console.WriteLine(Volume.High.Value);
            Console.WriteLine(Volume.High.DisplayName);

            var volume = Volume.From(2);
            var volume2 = Volume.FromName("high");
            var none = Volume.From(27);

            Console.ReadKey();
        }

 

Fourth, the application

code show as below:

Error file:

public interface ICommonError
    {
        int GetErrCode();
        string GetErrMsg();
        ICommonError SetErrMsg(string errMsg);
    }
public class Enumeration : IComparable
    {
        private readonly int _value;
        private readonly string _displayName;

        protected Enumeration()
        {
        }

        protected Enumeration(int value, string displayName)
        {
            _value = value;
            _displayName = displayName;
        }

        public int Value
        {
            get { return _value; }
        }

        public string DisplayName
        {
            get { return _displayName; }
        }

        public override string ToString()
        {
            return DisplayName;
        }

        public static IEnumerable<T> GetAll<T>() where T : Enumeration, new()
        {
            var type = typeof(T);
            var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);

            foreach (var info in fields)
            {
                var instance = new T();
                var locatedValue = info.GetValue(instance) as T;

                if (locatedValue != null)
                {
                    yield return locatedValue;
                }
            }
        }

        public override bool Equals(object obj)
        {
            var otherValue = obj as Enumeration;

            if (otherValue == null)
            {
                return false;
            }

            var typeMatches = GetType().Equals(obj.GetType());
            var valueMatches = _value.Equals(otherValue.Value);

            return typeMatches && valueMatches;
        }

        public override int GetHashCode()
        {
            return _value.GetHashCode();
        }

        public static int AbsoluteDifference(Enumeration firstValue, Enumeration secondValue)
        {
            var absoluteDifference = Math.Abs(firstValue.Value - secondValue.Value);
            return absoluteDifference;
        }

        public static T FromValue<T>(int value) where T : Enumeration, new()
        {
            var matchingItem = parse<T, int>(value, "value", item => item.Value == value);
            return matchingItem;
        }

        public static T FromDisplayName<T>(string displayName) where T : Enumeration, new()
        {
            var matchingItem = parse<T, string>(displayName, "display name", item => item.DisplayName == displayName);
            return matchingItem;
        }

        private static T parse<T, K>(K value, string description, Func<T, bool> predicate) where T : Enumeration, new()
        {
            var matchingItem = GetAll<T>().FirstOrDefault(predicate);

            if (matchingItem == null)
            {
                var message = string.Format("'{0}' is not a valid {1} in {2}", value, description, typeof(T));
                throw new ApplicationException(message);
            }

            return matchingItem;
        }

        public int CompareTo(object other)
        {
            return Value.CompareTo(((Enumeration)other).Value);
        }
    }
View Code
public class EmBusinessError : Enumeration, ICommonError
    {
        private int errCode;
        private String errMsg;

        public static readonly EmBusinessError parameterValidationError = new EmBusinessError(10001, "参数不合法");

        private EmBusinessError() { throw new Exception("私有构造函数不能调用"); }
        private EmBusinessError(int value, string displayName) : base(value, displayName) {

            this.errCode = value;
            this.errMsg = displayName;
        }
        
        public int GetErrCode()
        {
            return this.errCode;
        }

        public string GetErrMsg()
        {
            return this.errMsg;
        }

        public void SetErrCode(int errCode)
        {
            this.errCode = errCode;
        }

        public ICommonError SetErrMsg(string errMsg)
        {
            this.errMsg = errMsg;

            return this;
        }
    }
//包装器业务异常类实现
    public class BusinessException : Exception, ICommonError
    {
        private ICommonError commonError;

        //直接接收EmBusinessError的传参用于构造业务异常
        public BusinessException(ICommonError commonError):base()
        {
            this.commonError = commonError;
        }
        public BusinessException(ICommonError commonError, string errMsg):base()
        {
            this.commonError = commonError;
            this.commonError.SetErrMsg(errMsg);
        }
        public int GetErrCode()
        {
            return this.commonError.GetErrCode();
        }

        public string GetErrMsg()
        {
            return this.commonError.GetErrMsg();
        }

        public ICommonError SetErrMsg(string errMsg)
        {
            this.commonError.SetErrMsg(errMsg);

            return this;
        }
        public ICommonError GetCommonError()
        {
            return commonError;
        }
    }
View Code

 

异常中间件:

public class ExceptionHandlerMiddleWare
    {
        private readonly RequestDelegate next;
     
        /// <summary>
        /// 
        /// </summary>
        /// <param name="next"></param>
        public ExceptionHandlerMiddleWare(RequestDelegate next)
        {
            this.next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            try
            {
                await next(context);
            }
            catch (Exception ex)
            {
                await HandleExceptionAsync(context, ex);
            }
        }

        private static async Task HandleExceptionAsync(HttpContext context, Exception exception)
        {
            if (exception == null) return;
            await WriteExceptionAsync(context, exception).ConfigureAwait(false);
        }

        private static async Task WriteExceptionAsync(HttpContext context, Exception exception)
        {
            var response = context.Response;
            response.ContentType = "application/json;charset=utf-8";
            var result = new CommonReturnType();
           

            if (exception is BusinessException)
            {
                var businessException = (BusinessException)exception;

                var errModel = new { errCode= businessException.GetErrCode(), errMsg= businessException.GetErrMsg() };

                result = CommonReturnType.Create(errModel, "fail");

                
            }
           

            await response.WriteAsync(JsonConvert.SerializeObject(new { data = result.GetData(), status = result.GetStatus() }) ).ConfigureAwait(false);
        }

    }

Response文件夹:

 public class CommonReturnType
    {
        //表明对应请求的返回处理结果 "success" 或 "fail"
        private string status;

        //若status=success,则data内返回前端需要的json数据
        //若status=fail,则data内使用通用的错误码格式
        private object data;

        //定义一个通用的创建方法
        public static CommonReturnType Create(object result)
        {
            return CommonReturnType.Create(result, "success");
        }
        
        public static CommonReturnType Create(object result, string status)
        {
            CommonReturnType type = new CommonReturnType();
            type.SetStatus(status);
            type.SetData(result);

            return type;
        }

        public string GetStatus()
        {
            return status;
        }

        public void SetStatus(string status)
        {
            this.status = status;
        }

        public object GetData()
        {
            return data;
        }

        public void SetData(object data)
        {
            this.data = data;
        }
    }
View Code

 

最后推荐一个类库,这是我在Nuget上发现的枚举类库,地址:https://github.com/ardalis/SmartEnum

好了,先分享到这里,希望对你有帮助和启发。

参考资料:

(1)https://docs.microsoft.com/zh-cn/dotnet/standard/microservices-architecture/microservice-ddd-cqrs-patterns/enumeration-classes-over-enum-types

(2)https://ardalis.com/enum-alternatives-in-c 

 

 

作者:郭峥

出处:http://www.cnblogs.com/runningsmallguo/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

Guess you like

Origin www.cnblogs.com/runningsmallguo/p/10962414.html