3

私は 5 日間の大半を費やして、これを機能させる方法を見つけようとしました。

を使用enumして、ラジオ ボタン リスト、ドロップ ダウン リスト (これらは複数の選択肢から 1 つを選択)、チェック ボックス リストおよび複数選択リスト (これらはは、複数の選択肢から 1 つ (1) からすべて) です。

これで、ラジオ ボタン リストとドロップダウン リストがあれば、Orchard データベースに問題なく保存できます。ユーザーが選択肢を選択すると、その選択肢が特定の選択肢としてenumデータベースに保存されます。

ただし、チェック ボックス リストまたは複数選択リストを使用すると、Orchard/NHibernate で複数選択された を保存できませんenum

ここSOまたはGoogle検索で見つけることができるすべてを試しました。この状況では過剰な唯一の「実行可能な」解決策は、場合によっては 7 ~ 8 個の選択肢を格納するためだけに、新しいテーブル (移行による)/part/part レコードの組み合わせを作成することです。もちろん、次のようなこともできますpublic virtual IList<NewContentPartRecord> { get; set; }

はい、オーチャードのドキュメントで1-N および NN 関係の作成を見てきました。誰かがそれLazyField<T>が解決策かもしれないと考えました。しかし、(少なくとも私が見つけた情報または私が調べたコードサンプルでは)LazyField<T>別のテーブルシナリオを扱っているようです。

達成したいことのために別のテーブルは必要ないことを教えてください。繰り返しますが、これはやり過ぎのようです。

これが私がやろうとしていることの簡単な例です:

public enum MyEnum
{
    Enum1,
    Enum2,
    Enum3,
    Enum4,
    Enum5
}

Selector.csラジオ ボタン、ドロップダウン、チェック ボックス、または複数選択リストを自動的に選択するのに役立ちます。

public class MySelectorAttribute : SelectorAttribute
{
    public override IEnumerable<SelectListItem> GetItems()
    {
        return Selector.GetItemsFromEnum<MyEnum>();
    }
}

PartRecord.cs:_

[MyEnumSelector]
public virtual IList<string> MyEnumCheckBox { get; set; }

Part.cs:_

public IList<string> MyEnumCheckBox
{
    get { return Record.MyEnumCheckBox; }
    set { Record.MyEnumCheckBox = value; }
}

注: を使用する<string>と、「テーブルが存在しません」というエラーが表示されます。代わりに使用<MyEnum>すると、キャスト エラーが発生します (generic.list と generic.icollection またはいくつかのバリエーション)。

私はさまざまなエラーメッセージIEnumerableですべてを試しました。ICollection

Orchard/NHibernate では、参照する新しいテーブルを作成する必要なく、この種の動作が可能になると想像する必要があります (これも、このシナリオではやり過ぎのように思えます)。

誰かが助けることができれば、私はそれを大いに感謝します. 私はこの問題で頭がいっぱいです。バウンティ?現金?あなたはそれに名前を付けます。はい、私は絶望的です。:)

4

2 に答える 2

4

enum MyEnum with属性を装飾し[Flags]、その項目の値を 2 の異なる累乗に設定できます。たとえば、列挙型は次のようになります。

[Flags]
public enum MyEnum
{
    Enum1 = 1,
    Enum2 = 2,
    Enum3 = 4,
    Enum4 = 8,
    Enum5 = 16
}

これで、 クラスMyEnumCheckBoxのプロパティのタイプは次のようになります。PartRecordint

public virtual int MyEnumCheckBox { get; set; }

Partクラス内にプロキシ プロパティを作成できます。例えば:

private IList<MyEnum> _myCheckBox;

[MyEnumSelector]
public IList<MyEnum> MyCheckBox
{
    get 
    {
        if (_myCheckBox == null)
        {
            _myCheckBox = new List<MyEnum>();

            foreach (MyEnum item in Enum.GetValues(typeof(MyEnum)))
            {
                if (((MyEnum)Record.MyEnumCheckBox & item) == item)
                    _myCheckBox.Add(item);
            }
        }

        return _myCheckBox;
    }
    set
    {
        _myCheckBox = value;
        Record.MyEnumCheckBox = 0;

        foreach (var item in value)
        {
           Record.MyEnumCheckBox |= (int)item;
        }
    }
}

Flags 属性の詳細については、こちらを参照してください。基本的に、探しているものとまったく同じ単一の列挙フィールドに複数の列挙値を使用できるようにするのに役立ちます。


編集:

私は時間をかけてカスタム モジュールを作成し、この手法を示しました。私はそれをテストしましたが、正常に動作します。だからここにソースがあります:

Migrations.cs

public int Create() 
{
  SchemaBuilder.CreateTable("MultipleEnumPickerRecord", table => table
    .ContentPartRecord()
    .Column<int>("SelectedItems"));

  ContentDefinitionManager.AlterPartDefinition("MultipleEnumPickerPart", p => p.Attachable());

  return 1;
}

モデル:

[Flags]
public enum MyEnum
{
    Enum1 = 1, // bit-wise 00001 or 2^0
    Enum2 = 2, // bit-wise 00010 or 2^1
    Enum3 = 4, // bit-wise 00100 or 2^2
    Enum4 = 8, // bit-wise 01000 or 2^3
    Enum5 = 16 // bit-wise 10000 or 2^4
}

public class MultipleEnumPickerRecord : ContentPartRecord
{
    public virtual int SelectedItems { get; set; }
}

public class MultipleEnumPickerPart : ContentPart<MultipleEnumPickerRecord> 
{
    private IList<MyEnum> _selectedItems;

    public IList<MyEnum> SelectedItems
    {
        get
        {
            if (_selectedItems == null)
            {
                _selectedItems = new List<MyEnum>();

                foreach (MyEnum item in Enum.GetValues(typeof(MyEnum)))
                {
                    if (((MyEnum)Record.SelectedItems & item) == item)
                        _selectedItems.Add(item);
                }
            }

            return _selectedItems;
        }
        set
        {
            _selectedItems = value;
            Record.SelectedItems = 0;

            foreach (var item in value)
            {
                Record.SelectedItems |= (int)item;
            }
        }
    }
}

ハンドラ:

public class MultipleEnumPickerHandler : ContentHandler
{
    public MultipleEnumPickerHandler(IRepository<MultipleEnumPickerRecord> repository)
    {
        Filters.Add(StorageFilter.For(repository));
    }
}

運転者:

public class MultipleEnumPickerDriver : ContentPartDriver<MultipleEnumPickerPart>
{

    protected override string Prefix { get { return "MultipleEnumPicker"; } }

    protected override DriverResult Editor(MultipleEnumPickerPart part, dynamic shapeHelper)
    {
        return ContentShape("Parts_MultipleEnumPicker_Edit", () => shapeHelper.EditorTemplate(
            TemplateName: "Parts/MultipleEnumPicker", Model: part, Prefix: Prefix));
    }

    protected override DriverResult Editor(MultipleEnumPickerPart part, IUpdateModel updater, 
        dynamic shapeHelper)
    {
        updater.TryUpdateModel(part, Prefix, null, null);
        return Editor(part, shapeHelper);
    }

}

配置:

<Placement>
    <Place Parts_MultipleEnumPicker_Edit="Content:5"/>
</Placement>

最後に、ビュー:

@using ModuleNamespace.Models
@model MultipleEnumPickerPart
<fieldset>
  <div class="editor-label">@Html.LabelFor(x => x.SelectedItems)</div>
  <div class="editor-field">
    <select multiple="multiple" id="@Html.FieldIdFor(x => x.SelectedItems)" name="@Html.FieldNameFor(x => x.SelectedItems)">
      @foreach (MyEnum item in Enum.GetValues(typeof(MyEnum))) {
        var selected = Model.SelectedItems.Contains(item);
        <option value="@((int)item)" @if(selected) {<text>selected="selected"</text>}>
          @T(item.ToString())
        </option>
      }
    </select>
  </div>
</fieldset>

ただし、この手法を実装する際には、次の 2 つの点に注意する必要があります。

  1. 列挙型は内部的に整数として扱われます。つまり、それらの値は 32 ビットを占めます。MyEnumこれは、これが機能するために 32 を超える列挙項目を定義できないことを意味します。
  2. Bertrand が指摘したように、アイテムのデータベース検索は難しくなります (ただし、主要なデータベースではビット単位の演算子を使用できるため、不可能ではありません)。

これらの両方の制約は、データベースとモデルの間で異なるマッピング関数を使用することでバイパスできます。

どういう意味ですか?

私が示した例では、データベースの値 (およびMultipleEnumPickerRecord値) は型ですが、intそのMultipleEnumPickerPart整数を に「マップ」しましたList<MyEnum>。これにより、データベース内のスペースが少なくなり、他のマッピング関数を使用するよりも高速になります。

たとえば、stringデータベースのタイプを使用してから、 の内部にMultipleEnumPickerRecord何らかのマッピングを行うことができます。最も一般的な文字列マッピング関数はList<MyEnum>MultipleEnumPickerPart

  • カンマ区切りのマッピング - たとえば、誰かが and を選択Enum1Enum4た場合、それを文字列にマッピングできます"Enum1,Enum4"
  • セミコロン区切りのマッピング - 前の例を次のようにマッピングします"Enum1;Enum4"

選択する区切り文字のタイプは、文字列で使用しないことがわかっている文字に基づいている必要があります。データベース内の文字列からリストを分解するには、単純なものを使用できますvalue.Split(',').ToList();(区切り文字として「,」を使用している場合)。

この方法では、32 個の列挙項目だけに制限されることはありません。また、値が文字列として保存されるため、データベースで値を検索するのは非常に簡単です。欠点は、文字列がデータベース内でより多くのスペースを必要とし (int は文字列から 1 文字のスペースを占有します)、文字列操作関数は上記のサンプルで示したビット単位の関数よりも多少遅くなります。

于 2012-12-14T08:55:16.157 に答える
2

そのように List<T> をマップすることはできません。別のレコード/テーブルとの適切な関係を作成する必要があります。または、コンマ区切りの値のリストなどを使用してストレージを管理できます。もちろん、最初のケースは少しきれいですが、2 番目のケースはより簡単で、特定のレコードにあまり多くの価値を期待しないのであれば、率直に言ってまったく問題ありません。

enum フィールドを検討することもできます。

于 2012-12-14T05:06:11.093 に答える