3

enumラジオボタン、ドロップダウン、チェックボックスなどを選択するために、とカスタムクラスを使用してい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>、さまざまなエラーが発生します。このエラーは、を使用するため、チェックボックスまたはマルチリストでのみ発生します。たとえば、ドロップダウンはエラーなしで正常に機能します。これは、動作し、NHibernateを介してDBにマッピングできるサンプルドロップダウンモデル(上記で再利用可能)です。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; }
    }

これが私が試したことに基づいて私が得ているエラーのいくつかです:

List<string>エラー:

NHibernate.Transaction.ITransactionFactory-DTCトランザクションの事前準備フェーズが失敗しましたNHibernate.PropertyAccessException:無効なキャスト(プロパティタイプの不一致についてマッピングを確認してください); MyNameSpace.Models.MyRecordのセッター--->System.InvalidCastException:タイプ'NHibernate.Collection.Generic.PersistentGenericBag1 1[System.String]' to type 'System.Collections.Generic.List[System.String]'のオブジェクトをキャストできません。

List<MyEnum>エラー:

NHibernate.Transaction.ITransactionFactory-DTCトランザクションの事前準備フェーズが失敗しましたSystem.InvalidCastException:タイプ'System.Collections.Generic.List1 1[MyNameSpace.Models.MyEnum]' to type 'System.Collections.Generic.ICollection[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]'。

NHibernateを使用してenum複数の選択されたを挿入しようとするときに、このシナリオでのを使用する方法について何か考えはありますか?enum

4

3 に答える 3

5

Nhibernateではデフォルトでリストの使用が個別のテーブルに使用されるため、マッピング関数を使用して、データベースからモデル内の値のリストに単一の値をマップする必要があります。

ここでは、実際のサンプルを使用してそれを行う方法を示しましたが、あなたの質問についてもう一度要約します。

問題に基づいて、2つの主要なマッピング関数タイプがあります。それらは両方ともあなたを含みます:

  1. MyEnumSelector属性をRecordクラスからモデルクラスに移動し、
  2. RecordMyEnumCheckBoxプロパティのタイプを変更し、
  3. モデルのプロパティを変更して、モデルがのプロパティとのMyEnumCheckBox間でマップされるようにしますRecordMyEnumCheckBox

したがって、これらのマッピング関数は次のとおりです。

  1. intto-List<MyEnumSelector>これは私がリンクの投稿で示したのと同じテクニックです。MyEnumSelectorこれには、列挙型を属性でマークし、その[Flags]アイテムを後続の2の累乗の値に設定することが含まれます。RecordMyEnumCheckBoxプロパティはタイプである必要がありintます。次に、マッピング部分には、対応する値のリストとの間でRecord'sMyEnumCheckBoxプロパティのビットをマッピングすることが含まれます。MyEnumSelector
  2. stringto-これには、のプロパティをタイプにList<MyEnumSelector>設定することが含まれます。次に、マッピング部分には、'sプロパティの区切り文字列を、対応する値のリストとの間でマッピングすることが含まれます。区切り文字は、カンマ、セミコロン、またはアイテム値の名前として使用しないその他の文字にすることができます。RecordMyEnumCheckBoxstringRecordMyEnumCheckBoxMyEnumSelectorMyEnumSelector

これら2つのアプローチの主な違いは次のとおりです。

  1. 列挙型サイズ-intデータベースタイプとして使用する場合、データは32ビットに制限されます。これは、列挙型に32を超えるアイテムを含めることができないことを意味します。Stringsこの種の制限はありません
  2. データベースで特定の値を簡単に検索intできます-を使用する場合は、ビット単位のデータベース演算子を処理する必要があります。これは非常に面倒で操作が簡単ではありませんが、マッピングを使用する場合は単純なLIKE演算子を使用できます。string
  3. データベースで使用されるサイズ-int常に4バイト(32ビット)をstring使用しますが、マッピングでは文字列内の1文字のみにその量(32ビット)を使用します(タイプcharが、varcharまたはの場合textは、サイズを2倍にします-を使用する場合は64ビットncharnvarcharまたはntext)データベースの各行にはるかに多くのスペースを使用するようにします
  4. マッピングの速度-マッピングで使用されるビット単位のintマッピングは非常に高速ですが、stringマッピングははるかに遅い文字列操作関数を使用します。ただし、少量のデータを処理している場合は、これは問題にはなりません。しかし、大量のデータを処理する場合、これは大きな問題になる可能性があります。
于 2012-12-15T14:13:02.177 に答える
1

チェックボックスの値を列挙型の int 値にします。

いくつかの提案:

  • 独自の列挙型の代わりにSystem.DayOfWeek列挙型を使用します。
  • enum は、NHibernate のカスタム型としてマップできます。
  • 曜日は 1 つの単語であるため、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 に答える