Есть ли способ получить строковое значение enum, используя один символ? - PullRequest
0 голосов
/ 23 мая 2018

Я использую одно перечисление в приложении MVC, которое выглядит следующим образом:

public enum School
{
    DVB=1,
    AVD=2,
    ASB=3
}

И я получаю единственный единственный символ из базы данных для каждой школы, например, получаю:

D for DVB
A for AVD
B for ASB

Я хочу напечатать полное имя значения enum.Я могу достичь своей цели следующим образом, то есть я могу редактировать enum и заменять целочисленные значения enum символом.Но я не думаю, что это хорошее решение, потому что существующий код также использует целочисленные значения.

Есть ли другой способ получить строковое значение enum, используя один символ?

Ответы [ 3 ]

0 голосов
/ 23 мая 2018

Есть много способов достичь этого.Очень простой вариант - иметь второе перечисление с альтернативными идентификаторами ваших значений;например,

void Main()
{
    Console.WriteLine((School)Enum.Parse(typeof(ConvertSchool),"A"));
}
public enum School
{
    DVB=1,
    AVD=2,
    ASB=3
}
public enum ConvertSchool
{
    D =1,
    A =2,
    B =3
}

Лучше создать какое-то отображение;например, используйте словарь, чтобы сопоставить каждый ключ символа с соответствующим значением школы:

public enum School
{
    DVB=1,
    AVD=2,
    ASB=3
}
readonly IDictionary SchoolMap = new Dictionary<char,School>() {
    {'D', School.DVB},
    {'A', School.AVD},
    {'B', School.ASB}
};
void Main()
{
    Console.WriteLine(SchoolMap['A']);
}

Тем не менее, они подвержены ошибкам (например, если кто-то создал новую школу без создания новой записи ConvertSchool /составление словаря).Лучшим способом является переключение с enum на class, например

void Main()
{
    Console.WriteLine((School)'A');
}
public class School
{
    //list of enums
    public static readonly List<School> Values = new List<School>();
    //"enum" values
    public static readonly School DVB = new School("DVB",'D',1);
    public static readonly School AVD = new School("AVD",'A',2);
    public static readonly School ASB = new School("ASB",'B',3);
    //properties
    public string Name {get;private set;}
    public char Code {get;private set;}
    public int Index {get;private set;}
    //constructor
    private School (string name, char code, int index)
    {
        if (Values.Exists(x => x.Name.Equals(name)) || Values.Exists(x => x.Code.Equals(code)) || Values.Exists(x => x.Index.Equals(index)))
        {
            throw new ArgumentException(string.Format("The Name, Code, and Index of each School value must be unique.  \nName: '{0}'\nSchool: '{1}'\nIndex: {2}", name, code, index));
        }
        Name = name;
        Code = code;
        Index = index;
        Values.Add(this);
    }
    //implicit conversion
    public static implicit operator School(string schoolName)
    {
        return Values.First(x => x.Name.Equals(schoolName));
    }
    public static implicit operator School(char schoolCode)
    {
        return Values.First(x => x.Code.Equals(schoolCode));
    }
    public static implicit operator School(int index)
    {
        return Values.First(x => x.Index.Equals(index));
    }
    //how should it be displayed?
    public override string ToString()
    {
        return Name;
    }
    //whatever other logic you need
}

В этом последнем примере, если передается символ, для которого нет соответствующей школы (например, Console.WriteLine((School)'X');)вы получите InvalidOperationException.Если вы не хотите здесь ошибок, а скорее получите значения null, замените First на FirstOrDefault в каждом из операторов неявного преобразования.


Альтернативное решение заключается в удержанииотображение на уровне вашей базы данных;либо создание таблицы с сопоставленными значениями, либо даже сохранение этих сопоставлений в определении представления;затем используя вид таблицы, присоединенной к отображению, чтобы получить код из 3 символов вместо кода из 1 символа для каждой школы;например,

--if you have a SchoolMap table relating the 1 char code to the 3 char code:

create table SchoolMap 
(
    Char1Code nchar(1) not null primary key clustered
    , Char3Code nvarchar(3) not null unique
)

create view SchoolView as 
select School.*
, SchoolMap.Char3Code
from School
left outer join SchoolMap 
on SchoolMap.Char1Code = School.Code 


--if you don't have and don't want a table holding the mapping, you can do the same in the view's definition:

create view SchoolView as 
select School.*
, SchoolMap.Char3Code
from School
left outer join 
(
    values ('D','DVB'),('A','AVD'),('B','ASB')
) SchoolMap (Char1Code, Char3Code)
on SchoolMap.Char1Code = School.Code 
0 голосов
/ 23 мая 2018

Самый простой способ - это спланировать и принять решение о переносе вашей базы данных и сохранении целочисленного значения.

В любом случае, исходя из ваших комментариев в вопросе, вы используете Entity Framework и картуваши лица для представления модели.Следующие шаги могут помочь вам без размышлений, добавив новый атрибут или новое перечисление.

Допустим, вы используете сущность с именем MyEntity, которая на самом деле содержит свойство с именем MySchool.

Сначала вместо использования School enum для свойства type MySchool вы просто используете строковый тип.Таким образом, EF просто извлечет данные из базы данных и поместит их непосредственно в свойство.Нет необходимости в конвертации.Таким образом, у вас будет что-то вроде этого:

public class MyEntity
{
    public string MySchool { get; set; }
}

Во-вторых, всегда в свой класс MyEntity добавьте еще одно свойство, назовите его MySchoolEnum, которое будет иметь тип School enum и не будет отображено вбаза данных, таким образом, у вас есть:

[NotMapped] // <-- This tell EF that this property is not mapped to the database.
public School? MyShcoolEnum
{
    get
    {
        // Getting the value of this property depends on the value of the database
        // which is stored into MySchool property.
        switch (MySchool)
        {
            case "D": return School.DVB;
            case "A": return School.AVD;
            case "B": return School.ASB;
            default: return null;
        }
    }

    set
    {
        // When setting this property 
        // you automatically update the value of MySchool property
        switch (value)
        {
            case School.DVB: this.MySchool = "D";
                break;
            case School.AVD: this.MySchool = "A";
                break;
            case School.ASB: this.MySchool = "B";
                break;
            default: this.MySchool = null;
                break;
        }
    }
}

Side Примечание: С EF Core существует более элегантный способ делать подобные вещи без добавления нового свойства.С EF Core 2.1 мы можем избавиться от этого свойства и использовать Преобразование значений .

Наконец, все модели представлений, которые должны работать со столбцом MySchool базы данных, не должны использовать MySchoolсвойство, но сопоставьте их соответствующее свойство с MyShcoolEnum свойством вашей сущности.

Ваш код сущности должен быть похож на это:

public class MyEntity
{
    public string MySchool { get; set; }

    [NotMapped] // <-- This tell EF that this property is not mapped to the database.
    public School? MyShcoolEnum
    {
        get
        {
            // Getting the value of this property depends on the value of the database
            // which is stored into MySchool property.
            switch (MySchool)
            {
                case "D": return School.DVB;
                case "A": return School.AVD;
                case "B": return School.ASB;
                default: return null;
            }
        }

        set
        {
            // When setting this property you automatically update the value of MySchool property
            switch (value)
            {
                case School.DVB: this.MySchool = "D";
                    break;
                case School.AVD: this.MySchool = "A";
                    break;
                case School.ASB: this.MySchool = "B";
                    break;
                default: this.MySchool = null;
                    break;
            }
        }
    }
}
0 голосов
/ 23 мая 2018

Очень простой класс для "декорирования" значений атрибута с атрибутом [MapTo("x")]:

[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class MapToAttribute : Attribute
{
    public readonly string Key;

    public MapToAttribute(string key)
    {
        Key = key;
    }
}

public static class MapToUtilities<T> where T : struct
{
    private static readonly Dictionary<string, T> keyValues = Init();

    private static Dictionary<string, T> Init()
    {
        if (!typeof(T).IsEnum)
        {
            throw new ArgumentException(nameof(T));
        }

        var values = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static);

        var keyValues2 = new Dictionary<string, T>();

        foreach (FieldInfo fi in values)
        {
            var attr = fi.GetCustomAttribute<MapToAttribute>();
            if (attr == null)
            {
                continue;
            }

            string key = attr.Key;

            T value = (T)fi.GetValue(null);

            keyValues2.Add(key, value);
        }

        return keyValues2;
    }

    public static T KeyToValue(string key)
    {
        return keyValues[key];
    }

    public static string ValueToKey(T value)
    {
        var cmp = EqualityComparer<T>.Default;
        return keyValues.First(x => cmp.Equals(x.Value, value)).Key;
    }
}

Использование:

public enum School
{
    [MapTo("D")]
    DVB = 1,
    [MapTo("A")]
    AVD = 2,
    [MapTo("B")]
    ASB = 3
}

, а затем:

School sc = MapToUtilities<School>.KeyToValue("A");
string key = MapToUtilities<School>.ValueToKey(sc);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...