【问题标题】:How can I convert/store multiple ENUM selections?如何转换/存储多个 ENUM 选择?
【发布时间】:2013-01-04 04:13:59
【问题描述】:

我可以使用以下代码将enum 转换为string。但是,它只存储一个选定的值。在选择了两个值的情况下,当我使用 NHibernate 存储它时,它会被截断。

这是我的代码:

MyEnum { One, Two, Three, Four, Five }

private static readonly string[] myEnum =
    Enum.GetNames(typeof(MyEnum));
public string MyProperty
{
    get {
        var value = new MyEnum();
        int i = (int)value;
        return i >= 0 && i < myEnum.Length ?
            myEnum[i] : i.ToString(); }
    set { 
        Record.MyProperty= value == 
            null ? null : String.Join(",", value); }
}

Record 只是public virtual string MyProperty { get; set; }

谁能提供我将如何存储的示例,例如以逗号分隔的形式,选择多个enum(例如,“一、二、五”由用户选择,所有三个都是存储在数据库中)?

更新:

我正在尝试在get{} 中执行此操作:

foreach (int i in Enum.GetValues(typeof(MyEnum)))
{
    return i >= 0 && i < myEnum.Length ? myEnum[i] : i.ToString();
}

但我收到not all code paths return a value 错误。

问题更新:

如果我用两个 string 这样做:

part.MyProperty = record.MyProperty;

使用@Jamie Ide 下面的IEnumerable&lt;MyEnum&gt; 答案将不起作用,因为我无法将string 转换为MyEnum

我将如何编写最后一部分以从以下答案中的@Jamie Ide 获取IEnumerable&lt;MyEnum&gt; 代码?

【问题讨论】:

  • @frictionlesspulley 您能否在答案中汇总一个示例,然后我可以检查它是否有效?谢谢!
  • 您显示的 MyProperty 在其设置器中接受单个字符串并对其执行 string.Join ,这看起来“出乎意料”。你所拥有的一切都需要并返回字符串 - 你为什么不更多地暴露 MyEnum ?为什么是 myEnum?它包含与 someEnum.ToString() 给你的相同的值。为什么不将其存储为 ISet
  • not all code paths return a value 错误的原因是因为如果您的枚举中没有值(即循环永远不会运行),则不会返回任何内容。您可以在末尾添加一个虚拟的 return "" 以使编译器满意。
  • 是否需要存储为字符串?您是否考虑过存储为intlong 并将[Flags] 属性添加到枚举,然后进行按位比较以获得枚举列表?
  • @Josh 我尝试了一些类似的方法,但无法使其正常工作。

标签: asp.net-mvc-3 nhibernate


【解决方案1】:

正如我在评论中提到的,除非绝对要求将枚举存储为字符串,否则我建议使用 [Flags] 属性设置枚举并创建将枚举存储为 int 的约定。这是我使用的(注意我使用的是 FluentNH):

约定

public class EnumConvention : IUserTypeConvention
{
    public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
    {
        // You can use this if you don't want to support nullable enums
        // criteria.Expect(x => x.Property.PropertyType.IsEnum);

        criteria.Expect(x => x.Property.PropertyType.IsEnum ||
            (x.Property.PropertyType.IsGenericType &&
             x.Property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>) &&
             x.Property.PropertyType.GetGenericArguments()[0].IsEnum)
            );
    }

    public void Apply(IPropertyInstance target)
    {
        target.CustomType(target.Property.PropertyType);
    }
}

枚举
请注意,您必须小心设置值以保持按位比较正常工作。

// Note I use long to support enough enum values
[Flags]
public enum MyEnum : long
{
    Foo = 1,
    Bar = 1 << 1,
    Baz = 1 << 2
}

我相信,除此之外,您不必做任何事情。无需迭代设置或检索,什么都没有。要测试值是否存在,您可以在枚举上使用HasFlag() 扩展方法。

更新

要修改 MvcGrabBag 代码以支持这种枚举,您需要更改 GetItemsFromEnum 方法,如下所示:

public static IEnumerable<SelectListItem> GetItemsFromEnum<T>(T enumeration = default(T)) where T : struct
{
    FieldInfo[] fields = enumeration.GetType().GetFields(BindingFlags.Public | BindingFlags.Static);

    return from field in fields
                let value = Enum.Parse(enumeration.GetType(), field.Name)
                let descriptionAttributes = field.GetCustomAttributes(typeof(DescriptionAttribute), true)
                select new SelectListItem
                {
                    Text = descriptionAttributes.Length > 0
                                ? ((DescriptionAttribute)descriptionAttributes[0]).Description
                                : field.Name,
                    Value = Convert.ToInt64(value).ToString(),
                    Selected = (Convert.ToInt64(enumeration) & Convert.ToInt64(value)) == Convert.ToInt64(value)
                };
}

请注意,我将其保留为通用只是因为我不知道该工具包的哪些其他方面依赖于该签名。不过,您可以看到这不是必需的——您可以删除 &lt;T&gt; 并拥有像 public static IEnumerable&lt;SelectListItem&gt; GetItemsFromEnum(Enum enumeration) 这样的签名,它会正常工作。

还请注意,此代码使用我的支持从Description 属性派生的名称的约定。我用它来让我的标签更易于阅读。枚举看起来像这样:

[Flags]
public enum MyEnum : long
{
    [Description("Super duper foo")]
    Foo = 1,
    [Description("Super duper bar")]
    Bar = 1 << 1,
    // With no description attribute it will use the ToString value
    Baz = 1 << 2
}

【讨论】:

  • 谢谢你,但我提到的问题是我在属性上使用了自定义“选择器”属性(即[MySelector])。因为我在使用enum 时使用它来自动选择复选框、单选按钮、下拉菜单等,所以我在验证页面时收到The Field ... must be a number 作为错误。我认为这是因为我使用的“选择器”将enum 放在SelectList 中。代码很长,我不想浪费你的时间,但如果你想看,请告诉我。
  • 是的,我很想看看。我有类似的要求来显示复选框列表中的选择,所以如果我能弄清楚如何使您的自定义选择器与标志一起使用,它将为我节省一些 foreach 循环。
  • 您将不得不修改Selector 类,以便在GetItemsFromEnum 方法中分配的值不是枚举的字符串值,而是int 值。您必须修改整个块,以便在循环中转换为字符串。
  • 我的意思是在Selector 类中。有一个名为GetItemsFromEnum 的方法使用linq select 创建SelectList。您可以看到它提取了枚举的字符串值,使用对.Wordify() 的调用来设置文本(我假设它会在驼峰式名称中添加空格),然后将值设置为枚举的字符串值。如果您修改它以便使用枚举的 int 值并将 then 转换为文本值的字符串,那么您最终将获得正确的设置,让模型绑定器识别标志枚举.
  • 用完整方法再次更新。您可能可以尝试使其不通用,看看它是否仍然有效。
【解决方案2】:

这是一个快速而肮脏的解决方案。在此解决方案中,_myEnumString 将被映射为私有字段并存储在数据库中。如 cmets 中所述,可以使用另一个表将数组存储为集合,如this question 的答案所示,或在一个表中,如this question

    public enum MyEnum { One, Two, Three, Four, Five }

public class MyClass
{
    private string _myEnumString;

    public IEnumerable<MyEnum> MyEnums
    {
        get 
        { 
            return Array.ConvertAll(_myEnumString.Split(','), s => (MyEnum)Enum.Parse(typeof(MyEnum), s));
        }
        set
        {
            _myEnumString = string.Join(",", value.Select(v => v.ToString()));
        }
    }

}

更新

假设 record.MyProperty 和 part.MyProperty 都是 MyEnum 名称的分隔字符串(例如“一、二、三”),那么您可以映射 MyProperty 并创建一个只读属性以返回 MyEnum 的集合。

public class MyClass
{
    public string MyProperty { get; set; }

    public IEnumerable<MyEnum> MyEnums
    {
        get 
        { 
            return Array.ConvertAll(MyProperty.Split(','), s => (MyEnum)Enum.Parse(typeof(MyEnum), s));
        }
    }

}

【讨论】:

  • 看起来可行,但现在当我尝试将记录保存在数据库中时,我使用的现有代码出现错误。请参阅我上面帖子中的“问题更新”。谢谢。
  • 抱歉,更新不起作用。如果我使用part.MyProperty = record.MyProperty; 保存在数据库中,我会收到转换错误(stringIEnumerable)。进行更新并不能解决此问题。我怎么写part.MyProperty = [What goes here]
  • 所以这不是真的吗? “假设 record.MyProperty 和 part.MyProperty 都是 MyEnum 名称的分隔字符串......”我不明白你想要做什么。
  • 我的问题的第一部分是我想要做什么。如果我同时创建这两个字符串,我可以保存一个枚举,但是当我尝试使用foreach 循环来尝试存储更多而不是一个时,我得到了我指定的错误。当我尝试您最初建议的代码时,我得到了转换错误(stringIEnumerable)。
猜你喜欢
  • 2015-12-03
  • 1970-01-01
  • 1970-01-01
  • 2021-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-09
  • 1970-01-01
相关资源
最近更新 更多