【问题标题】:Handling alphabetic enumerated codes in a database field处理数据库字段中的字母枚举代码
【发布时间】:2014-01-20 08:10:38
【问题描述】:

我在数据库中有一个字段(我无法更改其架构),其中包含一组特定的值。我们称它们为HMLH 代表高,M 代表中,L 代表低。在C# 中,我希望能够以一种类型安全的方式引用这些值,但这种方式在代码中也是可读的。

目前有很多这种模式在存储库中乱扔垃圾:

public static class Priority
{
    public const string High = "H";
    public const string Medium = "M";
    public const string Low = "L";
}

这确实提供了可读性,但不是类型安全的,如果小写值进入数据库可能会很危险(不太可能但并非不可能)。

有没有更好的方法来处理这种模式?

【问题讨论】:

  • 查看智能枚举,仍然不完美,但它们可以保护您免受大小写问题的影响。

标签: c# enums datamodel


【解决方案1】:

您可以将其实现为 enum 和为其封装逻辑的静态类的组合,如下所示:

public enum Priority { High, Medium, Low }

public static class Priorities {
    public static string GetCode(this Priority priority) {
        switch (priority) {
        case Priority.High: return "H";
        case Priority.Medium: return "M";
        case Priority.Low: return "L";
        }
        throw new ArgumentException("priority");
    }
    public static Priority GetPriority(string priorityCode) {
        switch (priorityCode) {
        case "H": return Priority.High;
        case "M": return Priority.Medium;
        case "L": return Priority.Low;
        }
        throw new ArgumentException("priorityCode");
    }
}

现在您可以使用Priorities.GetPriority(codeFromDatabase) 从数据库代码中创建Priority 枚举的元素,然后调用

priority.GetCode()

获取将Priority写回数据库的代码。

【讨论】:

    【解决方案2】:

    根据情况,我有两种处理方法。

    第一种是使用enumDictionary<TKey, TValue> 将字符映射到enum 中的条目。

    enum Priority : byte
    {
        High,
        Medium,
        Low
    }
    static class Priorities
    {
        private static Dictionary<char, Priority> _toPriority = new Dictionary<char, Priority>();
        private static Dictionary<Priority, char> _fromPriority = new Dictionary<Priority, char>();
    
        static Priorities()
        {
            var priorities = Enum.GetNames(typeof(Priority));
            var values = (Priority[])Enum.GetValues(typeof(Priority));
            for (var i = 0; i < priorities.Length; i++)
            {
                _toPriority.Add(priorities[i][0], values[i]);
                _fromPriority.Add(values[i], priorities[i][0]);
            }
        }
    
        public static Priority GetPriority(string field)
        {
            Priority res;
            if (!TryGetPriority(field, out res))
                throw new ArgumentException("Invalid priority on field.", "field");
            return res;
        }
    
        public static bool TryGetPriority(string field, out Priority priority)
        {
            if (field == null || field.Length == 0) { priority = default(Priority); return false; }
            return _toPriority.TryGetValue(field[0], out priority);
        }
    
        public static char GetCode(Priority priority)
        {
            return _fromPriority[priority];
        }
    }
    

    另一种方法是创建一个struct,它在公共静态只读字段中创建自己。

    struct Priority
    {
        public static readonly Priority High = new Priority('H');
        public static readonly Priority Medium = new Priority('M');
        public static readonly Priority Low = new Priority('L');
    
        static Priority()
        {
            register(High);
            register(Medium);
            register(Low);
        }
    
        public static bool TryGetPriority(char code, out Priority priority)
        {
            return _map.TryGetValue(code, out priority);
        }
        public static Priority GetPriority(char code)
        {
            Priority priority;
            if (!TryGetPriority(code, out priority))
                throw new ArgumentException("Code doesn't represent an existing priority.", "code");
            return priority;
        }
    
        public override int GetHashCode()
        {
            return _code.GetHashCode();
        }
        public override bool Equals(object obj)
        {
            if (!(obj is Priority)) return false;
            return ((Priority)obj)._code == _code;
        }
    
        public override string ToString()
        {
            return _code.ToString();
        }
    
        public static implicit operator char(Priority @this) { return @this._code; }
        public static explicit operator Priority(char code)
        {
            Priority result;
            if (!_map.TryGetValue(code, out result))
                throw new InvalidCastException();
            return result;
        }
    
        private static readonly Dictionary<char, Priority> _map = new Dictionary<char, Priority>();
        private static void register(Priority p)
        {
            _map.Add(char.ToLowerInvariant(p._code), p);
            _map.Add(char.ToUpperInvariant(p._code), p);
        }
    
        private readonly char _code;
        private Priority(char code) { _code = code; }
    }
    

    方法 1:
    优点:您只需定义 enum,结果将自动更新。您可以访问全名 (enumInstance.ToString()) 和代码。
    缺点:您需要显式调用转换方法才能在 charPriority 之间进行切换。

    方法 2:
    优点: 该类型将隐式转换为 char,并且可以从 char 强制转换。
    缺点: 您必须同时更新对registerenum 的调用才能添加或修改条目。您无法访问该字段的全名。

    方法二的两个缺点都可以轻松解决。第一个可以通过使用反射来发现所有公共字段来解决。第二个是通过将其作为参数添加到构造函数或也通过反射。


    使用方法一:

    Priority p = Priority.High; // Assign literal
    MessageBox.Show(p.ToString()); // High
    MessageBox.Show(Priorities.GetCode(p).ToString()); // H
    
    Priority p = Priorities.GetPriority('L'); // Cast from character
    MessageBox.Show(p.ToString()); // Low
    MessageBox.Show(Priorities.GetCode(p).ToString()); // L
    
    Priority p; // Safe assigning
    if (!Priorities.TryGetPriority('M', out p))
        return;
    MessageBox.Show(p.ToString()); // Medium
    MessageBox.Show(Priorities.GetCode(p).ToString()); // M
    

    使用方法二:

    Priority p = Priority.High; // Assign literal
    MessageBox.Show(p.ToString()); // H
    
    Priority p = (Priority)'L'; // Cast from character
    MessageBox.Show(p.ToString()); // L
    
    Priority p; // Safe assigning
    if (!Priority.TryGetPriority('M', out p))
        return; // Handle invalid scenario
    MessageBox.Show(p.ToString()); // M
    

    我个人认为这个解决方案比依赖两个开关和定义要干净得多。性能方面(除非你有一个非常大的数据库,否则它并不重要)它的执行与 switch 语句非常相似。正确条件下的 switch 语句将编译为代码内哈希图,就像 Dictionary&lt;TKey, TValue&gt; 是哈希图一样。

    如果你想要多字符串,只需将char 更改为string

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多