NHibernate:枚举以列出和存储数据库中的值

M E*_*rty 3 nhibernate asp.net-mvc-3

我正在使用enum和的自定义Selector类来帮助在单选按钮,下拉菜单,复选框等之间进行选择。我正在使用NHibernate。通过单选(单选按钮,下拉菜单),来自attribute的值[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; }
}
Run Code Online (Sandbox Code Playgroud)

这是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);
    }
}
Run Code Online (Sandbox Code Playgroud)

所述Selector类配对与模板Selector.cshtml具有一定逻辑找出哪些挑(单选按钮,复选框等)。

我收到的各种错误试图要么List<string>List<MyEnum>IList<string>IList<MyEnum>IEnumerable<MyEnum>IEnumerable<MyEnum>。由于使用复选框或多重列表,因此只会出现此错误List<string>。例如,下拉菜单可以正常工作而不会出错。这是一个有效的示例下拉模型(可以enum在上面重复使用),并将允许通过NHibernate映射到数据库:

[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; }
    }
Run Code Online (Sandbox Code Playgroud)

以下是根据我尝试过的一些错误信息:

List<string> 错误:

NHibernate.Transaction.ITransactionFactory-DTC事务预准备阶段失败NHibernate.PropertyAccessException:无效的转换(检查属性类型不匹配的映射);MyNameSpace.Models.MyRecord的setter ---> System.InvalidCastException:无法转换类型为“ NHibernate.Collection.Generic.PersistentGenericBag 1[System.String]' to type 'System.Collections.Generic.List1 [System.String]”的对象。

List<MyEnum> 错误:

NHibernate.Transaction.ITransactionFactory-DTC事务预准备阶段失败System.InvalidCastException:无法转换类型为“ System.Collections.Generic.List 1[MyNameSpace.Models.MyEnum]' to type 'System.Collections.Generic.ICollection1 [System.String]”的对象。

IList<string> 错误:

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

我尝试的其他变体是类似的错误,除了如果使用<MyEnum>它会显示如下错误:

System.Collections.Generic.List 1[MyNameSpace.Models.MyEnum]' to type 'System.Collections.Generic.ICollection1 [System.String]'。

enum在尝试enum使用NHibernate 插入多个选定的s 时,在这种情况下如何使用的想法?

Iva*_*rić 5

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

我已经通过一个工作示例演示了如何执行此操作,但是我将再次就您的问题进行总结。

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

  1. MyEnumSelector属性从Record类移到模型类,
  2. 更改您RecordMyEnumCheckBox财产的类型,并
  3. 更改模型的MyEnumCheckBox属性,以使其与您RecordMyEnumCheckBox属性相互映射

因此,这些映射函数为:

  1. intList<MyEnumSelector>-这是我在链接文章中展示的相同技术。它包括MyEnumSelector[Flags]属性标记枚举,并将其项设置为具有2的后续幂的值。您RecordMyEnumCheckBox属性应为类型int。然后,映射部分需要将RecordMyEnumCheckBox属性的位与对应MyEnumSelector值列表进行映射
  2. stringList<MyEnumSelector>-这包括了设置您RecordMyEnumCheckBox属性为类型string。映射部分然后涉及的分隔的串映射RecordMyEnumCheckBox属性和从相应的列表中MyEnumSelector的值。分隔符可以是逗号,分号或其他一些不会用作MyEnumSelector项目值名称的字符。

这两种方法的主要区别是:

  1. 枚举大小- int用作数据库类型时,您只能使用32位数据。这意味着枚举中不能包含超过32个项目。String没有这种限制
  2. 易于在数据库中搜索特定值-使用​​时int,您必须处理按位操作的数据库运算符,这些运算符很杂乱且不易使用,而LIKE在使用string映射时可以使用简单的运算符
  3. -在数据库中使用尺寸int用途总是4个字节(32位),而string映射使用的量(32位)的字符串内只有一个字符(如果它的类型charvarchar或者text,和两倍的尺寸-当使用64位ncharnvarcharntext),因此它将为数据库中的每一行使用更多空间
  4. 映射的速度-映射中使用的按位int映射相当快,而string映射使用的字符串处理功能要慢得多。但是,如果要处理少量数据,这应该不是问题。但是,如果您要处理大量数据,这可能是一个巨大的问题。