NHibernate:Enum在数据库中列出和存储值

时间:2012-12-06 12:58:32

标签: asp.net-mvc-3 nhibernate

我正在使用enum和自定义Selector类来帮助选择radiobuttons,下拉列表,复选框等。我正在使用NHibernate。使用单个选项(单选按钮,下拉列表),属性[Display(Name = "[Some Text]")]中的值将填充在数据库表中(注意:我使用扩展名来使用Display(Name))。但是,通过多个选择(复选框,多列表),我无法弄清楚如何将enum选择的值放入数据库中。

以下是我的模型的一部分(每个都在单独的文件中)(编辑:我给了他们通用名称,以免进一步混淆问题):

public enum MyEnum
{
    [Display(Name = "Text for enum1")]
    enum1,
    //Left out 2 - 10 for brevity
    [Display(Name = "Text for enum10")]
    enum10
}
...
public class MyEnumSelectorAttribute : SelectorAttribute
{
    public override IEnumerable<SelectListItem> GetItems()
    {
        return Selector.GetItemsFromEnum<MyEnum>();
    }
}
...
[Display(Name = "This is a checkboxlist (select one or more check boxes)?")]
[MyEnumSelector(BulkSelectionThreshold = 10)]
public virtual List<string> MyEnumCheckBox { get; set; }
...
public List<string> MyEnumCheckBox
{
    get { return Record.MyEnumCheckBox; }
    set { Record.MyEnumCheckBox = value; }
}

这里是Selector.cs类(如果它与问题相关)有助于选择单选按钮,复选框,下拉列表等:

public class Selector
{
    public IEnumerable<SelectListItem> Items { get; set; }

    public string OptionLabel { get; set; }

    public bool AllowMultipleSelection { get; set; }

    public int BulkSelectionThreshold { get; set; }

    public static string GetEnumDescription(string value, Type enumType)
    {
        var fi = enumType.GetField(value.ToString());
        var display = fi
            .GetCustomAttributes(typeof(DisplayAttribute), false)
            .OfType<DisplayAttribute>()
            .FirstOrDefault();
        if (display != null)
        {
            return display.Name;
        }
        return value;
    }

    public static IEnumerable<SelectListItem> GetItemsFromEnum<T>
        (T selectedValue = default(T)) where T : struct
    {
        return from name in Enum.GetNames(typeof(T))
               let enumValue = Convert.ToString((T)Enum.Parse
                   (typeof(T), name, true))

               select new SelectListItem
               {
                   Text = GetEnumDescription(name, typeof(T)),
                   Value = enumValue,
                   Selected = enumValue.Equals(selectedValue)
               };
    }
}

public static class SelectorHelper
{
    public static IEnumerable<SelectListItem> ToSelectList
        (this IEnumerable data)
    {
        return new SelectList(data);
    }

    public static IEnumerable<SelectListItem> ToSelectList
        (this IEnumerable data, string dataValueField, 
        string dataTextField)
    {
        return new SelectList(data, dataValueField, dataTextField);
    }

    public static IEnumerable<SelectListItem> ToSelectList<T>
        (this IEnumerable<T> data, Expression<Func<T, object>> 
        dataValueFieldSelector, Expression<Func<T, string>> 
        dataTextFieldSelector)
    {
        var dataValueField = dataValueFieldSelector.ToPropertyInfo().Name;
        var dataTextField = dataTextFieldSelector.ToPropertyInfo().Name;
        return ToSelectList(data, dataValueField, dataTextField);
    }
}

Selector类与模板Selector.cshtml配对,模板List<string>有一些逻辑可以找出要选择的内容(单选按钮,复选框等)。

我在尝试List<MyEnum>IList<string>IList<MyEnum>IEnumerable<MyEnum>IEnumerable<MyEnum>List<string>时遇到各种错误。此错误仅附带复选框或多列表,因为它们使用enum。例如,下拉列表工作正常,没有错误。下面是一个示例下拉模型(可以重用上面的[Required(ErrorMessage = "Please select one option")] [Display(Name = "This is a dropdown list (select one option)?")] [MyEnumSelector(BulkSelectionThreshold = 0)] //0 selects dropdown public virtual MyEnum? MyEnumDropDown { get; set; } public MyEnum? MyEnumDropDown { get { return Record.MyEnumDropDown; } set { Record.MyEnumDropDown = value; } } ),它可以通过NHibernate映射到数据库:

List<string>

根据我的尝试,以下是我得到的一些错误:

1[System.String]' to type 'System.Collections.Generic.List错误:

  

NHibernate.Transaction.ITransactionFactory - DTC事务预处理阶段失败   NHibernate.PropertyAccessException:无效的Cast(检查映射是否存在属性类型不匹配); MyNameSpace.Models.MyRecord的设定者---&gt; System.InvalidCastException:无法转换类型为'NHibernate.Collection.Generic.PersistentGenericBag List<MyEnum> 1 [System.String]'的对象。

1[MyNameSpace.Models.MyEnum]' to type 'System.Collections.Generic.ICollection错误:

  

NHibernate.Transaction.ITransactionFactory - DTC事务预处理阶段失败   System.InvalidCastException:无法转换类型为'System.Collections.Generic.List IList<string> 1 [System.String]'的对象。

<MyEnum>错误:

  

NHibernate.AdoNet.AbstractBatcher - 无法执行命令:INSERT INTO MyEnumCheckBox(MyRecord_id,Value)VALUES(@ p0,@ p1)   System.Data.SqlServerCe.SqlCeException(0x80004005):指定的表不存在。 [MyEnumCheckBox]

我尝试的其他变体是类似的错误,但如果我使用1[MyNameSpace.Models.MyEnum]' to type 'System.Collections.Generic.ICollection它会显示如下错误:

  

System.Collections.Generic.List enum 1 [System.String]”。

在尝试使用NHibernate插入多个选定的enum时,有关如何在此方案中使用{{1}}的任何想法?

3 个答案:

答案 0 :(得分:4)

由于默认情况下在Nhibernate中使用列表用于单独的表,因此您必须使用映射函数将数据库中的单个值映射到模型中的值列表。

我已经演示了如何使用工作示例here,但我会再次总结你的问题。

根据您的问题,有两种主要的映射函数类型。他们都包括你:

  1. 将您的MyEnumSelector属性从Record类移至模型类
  2. 更改Record的{​​{1}}媒体资源和
  3. 的类型
  4. 更改模型的MyEnumCheckBox媒体资源,以便与MyEnumCheckBox的{​​{1}}媒体资源进行映射
  5. 所以那些映射函数是:

    1. RecordMyEnumCheckBox - 这与我在链接帖子中显示的技术相同。它包括使用int属性标记您的List<MyEnumSelector>枚举,并将其项目设置为后续幂2的值。您的MyEnumSelector的{​​{1}}属性应为类型[Flags]。映射部分然后涉及将Record的{​​{1}}属性的位映射到相应的MyEnumCheckBox值列表中
    2. intRecord - 这涉及将MyEnumCheckBox的{​​{1}}属性设置为MyEnumSelector类型。然后,映射部分涉及将string的{​​{1}}属性的分隔字符串映射到相应的List<MyEnumSelector>值列表中。分隔符可以是逗号或分号或其他一些您不会用作Record项值的名称的字符。
    3. 这两种方法的主要区别是:

      1. 枚举大小 - 使用MyEnumCheckBox作为数据库类型时,限制为32位数据。这意味着您的枚举中不能包含超过32个项目。 string没有这种限制
      2. 轻松搜索数据库中的特定值 - 使用Record时,您将不得不处理那些非常混乱并且不易于使用的逐位数据库运算符,而您可以使用简单{使用MyEnumCheckBox映射
      3. 时的{1}}运算符
      4. 数据库中使用的大小 - MyEnumSelector始终使用4个字节(32位),而MyEnumSelector映射仅对字符串中的一个字符使用该数量(32位)(如果它的类型为{ {1}},intString,并且在使用intLIKEstring时大小加倍 - 64位,因此会使用很多数据库中每行的空间更多
      5. 映射速度 - int映射中使用的逐位映射非常快,而string映射使用速度慢得多的字符串操作函数。如果您处理少量数据,这应该不是问题。但是,如果你要处理大量数据,这可能是个大问题。

答案 1 :(得分:1)

我会将复选框值设为枚举的int值。

一些建议:

  • 使用System.DayOfWeek枚举代替您自己的枚举。
  • 枚举可以在NHibernate中映射为自定义类型。
  • 由于星期几是一个单词,因此不需要Display属性。控件值应该是枚举的int值(可以直接转换)。
  • 对不同的控件集(下拉菜单,收音机等)使用部分视图

答案 2 :(得分:0)

PersistentGenericBag绝对不是List&lt; string&gt;,但它确实实现了IList&lt; string&gt;:

public class PersistentGenericBag<T> : PersistentBag, IList<T>

使用IList&lt; string&gt;时相反,你确定从代码中的相同位置得到完全相同的错误吗?