3

我正在使用enum's 和一个自定义Selector类来帮助在单选按钮、下拉菜单、复选框等之间进行选择。我正在使用 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在上面重复使用),它可以通过 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; }
    }

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

List<string>错误:

NHibernate.Transaction.ITransactionFactory - DTC 事务准备阶段失败 NHibernate.PropertyAccessException: Invalid Cast (检查你的映射是否有属性类型不匹配); 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):指定的表不存在。[我的枚举检查框]

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

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

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

4

3 回答 3

5

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

我已经用一个工作示例在这里演示了如何做到这一点,但我会在你的问题上再次总结一下。

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

  1. 将您的MyEnumSelector属性从Record类移动到模型类,
  2. 改变你RecordMyEnumCheckBox财产的类型和
  3. 更改模型的MyEnumCheckBox属性,使其映射到您RecordMyEnumCheckBox属性

所以这些映射函数是:

  1. intto List<MyEnumSelector>- 这是我在链接帖子中展示的相同技术。它包括用属性标记你的MyEnumSelector枚举[Flags]并将它的项目设置为 2 的后续幂的值。你RecordMyEnumCheckBox属性应该是 type int。然后,映射部分涉及将Record'MyEnumCheckBox属性的位映射到对应值列表和从对应MyEnumSelector值列表映射出
  2. stringto-List<MyEnumSelector>这涉及将您RecordMyEnumCheckBox属性设置为类型string。然后,映射部分涉及将Record'MyEnumCheckBox属性的分隔字符串映射到相应值列表和从相应MyEnumSelector值列表映射。分隔符可以是逗号或分号或其他一些您不会用作MyEnumSelector项目值名称的字符。

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

  1. 枚举大小 - 当int用作您的数据库类型时,您被限制为 32 位数据。这意味着您的枚举中不能有超过 32 个项目。Strings没有这种限制
  2. 易于在数据库中搜索特定值 - 使用时int,您必须处理非常混乱且不易使用的按位数据库运算符,而在使用映射LIKE时可以使用简单运算符string
  3. 数据库中int使用的大小 - 始终使用 4 个字节(32 位),而string映射使用该数量(32 位)仅用于字符串中的一个字符(如果它的类型为char,varchar或, 并且大小加倍 - 使用,text时为 64 位或) 因此它将为数据库中的每一行使用更多空间ncharnvarcharntext
  4. Speed of mapping - bit-wise mapping used in int mapping is quite fast while string mapping uses string manipulation functions which are much slower. This shouldn't be the problem though if you're dealing with a small amount of data. But if you're going to deal with a massive amount of data this could be a huge issue.
于 2012-12-15T14:13:02.177 回答
1

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

几点建议:

  • 使用System.DayOfWeek枚举代替您自己的枚举。
  • 枚举可以映射为 NHibernate 中的自定义类型。
  • 由于一周中的几天是一个词,因此不需要 Display 属性。控制值应该是枚举的 int 值(您可以直接转换)。
  • 对不同的控件集(下拉菜单、单选按钮等)使用部分视图
于 2012-12-06T13:40:56.720 回答
0

PersistentGenericBag 绝对不是 List<string>,但它确实实现了 IList<string>:

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

当您改用 IList<string> 时,您确定您从代码中的同一位置得到完全相同的错误吗?

于 2012-12-06T13:25:24.007 回答