NHibernate:枚举以列出并将值存储在数据库中 [英] NHibernate: Enum to list and store values in database

查看:88
本文介绍了NHibernate:枚举以列出并将值存储在数据库中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am using enum's and a custom Selector class to help choose between radiobuttons, dropdowns, checkboxes, etc. I am using NHibernate. With a single selection (radiobuttons, dropdowns), the value from attribute [Display(Name = "[Some Text]")] will be populated in the database table (NOTE: I am using an extension to use Display(Name)). However, with multiple selections (checkboxes, multilist), I cannot figure out how to get the values of the enum selections into the database.

这是模型的一部分(每个部分都位于单独的文件中)(我给了它们通用名称,以免进一步混淆问题):

Here are parts of my model (each in separate files) ( I gave them generic names so as not to further confuse the issue):

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类(在与问题相关的情况下),可帮助选择单选按钮,复选框,下拉菜单等:

And here is the Selector.cs class (in case it's relevant to the problem) that help's to choose radiobuttons, checkboxes, dropdowns, etc.:

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配对,该模板具有一些逻辑以找出要选择的对象(单选按钮,复选框等).

The Selector class is paired with a template Selector.cshtml that has some logic to figure out which to pick (radiobutton, checkboxes, etc.).

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

I am getting various errors trying either List<string>, List<MyEnum>, IList<string>, IList<MyEnum>, IEnumerable<MyEnum> and IEnumerable<MyEnum>. This error only comes with checkboxes or multilists since they use List<string>. Dropdowns, for example, work fine with no errors. Here is a sample dropdown model (can reuse enum above) that works and will allow mapping to the DB through 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; }
    }

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

Here are some of the errors I am getting based on what I've tried:

List<string>错误:

List<string> error:

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

NHibernate.Transaction.ITransactionFactory - DTC transaction prepre phase failed NHibernate.PropertyAccessException: Invalid Cast (check your mapping for property type mismatches); setter of MyNameSpace.Models.MyRecord ---> System.InvalidCastException: Unable to cast object of type 'NHibernate.Collection.Generic.PersistentGenericBag1[System.String]' to type 'System.Collections.Generic.List1[System.String]'.

List<MyEnum>错误:

List<MyEnum> error:

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

NHibernate.Transaction.ITransactionFactory - DTC transaction prepre phase failed System.InvalidCastException: Unable to cast object of type 'System.Collections.Generic.List1[MyNameSpace.Models.MyEnum]' to type 'System.Collections.Generic.ICollection1[System.String]'.

IList<string>错误:

IList<string> error:

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

NHibernate.AdoNet.AbstractBatcher - Could not execute command: INSERT INTO MyEnumCheckBox (MyRecord_id, Value) VALUES (@p0, @p1) System.Data.SqlServerCe.SqlCeException (0x80004005): The specified table does not exist. [ MyEnumCheckBox ]

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

The other variations I tried were similar errors, except that if I used <MyEnum> it would show an error like this:

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

System.Collections.Generic.List1[MyNameSpace.Models.MyEnum]' to type 'System.Collections.Generic.ICollection1[System.String]'.

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

Any thoughts on how to use enum's in this scenario when trying to insert multiple selected enums using NHibernate?

推荐答案

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

Since using lists is used for separate tables by default in Nhibernate, you'll have to use a mapping functions to map a single value from the database to a list of values in your model.

我已经演示了操作方法

I've demonstrated how to do it here with a working sample, but I'll sum it up once more on your question.

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

There are 2 major mapping function types based on your problem. They both include that you:

  1. MyEnumSelector属性从Record类移动到模型类,
  2. 更改RecordMyEnumCheckBox属性和
  3. 的类型
  4. 更改模型的MyEnumCheckBox属性,以使其与RecordMyEnumCheckBox属性相互映射
  1. move your MyEnumSelector attribute from the Record class to your model class,
  2. change the type of your Record's MyEnumCheckBox property and
  3. change your model's MyEnumCheckBox property so that it maps to and from your Record's MyEnumCheckBox property

所以这些映射函数是:

  1. intList<MyEnumSelector>-这是我在链接文章中显示的相同技术.它包括用[Flags]属性标记您的MyEnumSelector枚举,并将其项设置为具有2的后续幂的值.您的RecordMyEnumCheckBox属性的类型应为int.然后,映射部分涉及将RecordMyEnumCheckBox属性的位与对应的MyEnumSelector值列表进行映射
  2. stringList<MyEnumSelector>-这涉及将RecordMyEnumCheckBox属性设置为string类型.然后,映射部分涉及将RecordMyEnumCheckBox属性的定界字符串映射到对应的MyEnumSelector值列表中或从中映射.分隔符可以是逗号,分号或其他字符,您不会将其用作MyEnumSelector项目值的名称.
  1. int to List<MyEnumSelector> - this is the same technique I've shown in the linking post. It includes marking your MyEnumSelector enum with the [Flags] attribute and setting it's items to be of values of subsequent powers of 2. Your Record's MyEnumCheckBox property should be of type int. The mapping part then involves mapping the bits of the Record's MyEnumCheckBox property to and from the list of corresponding MyEnumSelector values
  2. string to List<MyEnumSelector> - this involves setting your Record's MyEnumCheckBox property to be of type string. The mapping part then involves mapping the delimited string of the Record's MyEnumCheckBox property to and from the list of corresponding MyEnumSelector values. Delimiter could be comma or semicolon or some other character that you won't use as a name of your MyEnumSelector item values.

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

Major differences of these 2 approaches are:

  1. 枚举大小-使用int作为数据库类型时,您只能使用32位数据.这意味着枚举中不能包含超过32个项目. String没有这种限制
  2. 易于在数据库中搜索特定值-使用​​int时,您必须处理按位操作的数据库运算符,这些运算符非常混乱并且不那么容易使用,而您可以使用简单的LIKE string映射
  3. 时的运算符
  4. 数据库中使用的大小-int始终使用4个字节(32位),而string映射仅对字符串内的一个字符使用该数量(32位)(如果类型为charvarchartext,并且使用ncharnvarcharntext时大小加倍-64位),因此它将为数据库中的每一行使用更多空间
  5. 映射速度-int映射中使用的按位映射相当快,而string映射使用的字符串处理函数要慢得多.但是,如果要处理少量数据,这应该不是问题.但是,如果您要处理大量数据,这可能是个大问题.
  1. Enum size - when using int as your database type your're limited to 32 bits of data. This means that you can't have more than 32 items in your enum. Strings don't have this kind of limitation
  2. Ease of searching the database for specific values - when using int, you'll have to deal with bit-wise database operators which are quite messy and not that easy to work with, while you could use simple LIKE operator when using string mapping
  3. Size used in database - int uses always 4 bytes (32-bits) while string mapping uses that amount (32-bits) for only one character inside the string (if it's of type char, varchar or text, and double the size - 64-bits when using nchar, nvarchar or ntext) so it will use much more space for each row in the database
  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.

这篇关于NHibernate:枚举以列出并将值存储在数据库中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆