处理数据库字段中的字母枚举代码

时间:2014-01-02 19:33:09

标签: c# enums datamodel

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

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

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

哪个提供了可读性,但不是类型安全的,如果小写值进入数据库,可能会有危险(不太可能但不是不可能)。

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

2 个答案:

答案 0 :(得分:2)

您可以将其实现为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写回数据库的代码。

答案 1 :(得分:1)

根据具体情况,我有两种方法可以解决这个问题。

第一种方法是使用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的调用才能添加或修改条目。您无法访问该字段的全名。

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


使用方法1:

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

使用方法2:

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语句将被编译为in-code hashmap,就像Dictionary<TKey, TValue>是一个hashmap一样。

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