3

我花了五 (5) 天的大部分时间试图弄清楚如何完成这项工作:

我使用enum's 通过单选按钮列表、下拉列表(这些是多个选项中的一 (1) 个选项)以及复选框列表和多选列表(这些是多种选择中的一 (1) 种)。

现在,使用单选按钮列表和下拉列表,我可以毫无问题地保存在 Orchard 数据库中。用户选择一个选项,该选项将作为特定选项保存enum到数据库中。

但是,使用复选框列表或多选择列表,我无法让 Orchard/NHibernate 保存多个 selected enum

我已经尝试在 SO 上或通过 Google 搜索可以找到的所有内容。唯一“可行”的解决方案,在这种情况下是多余的,是创建一个新表(通过迁移)/部分/部分记录组合,只是为了在某些情况下存储 7-8 个选择。然后,当然,我可以做类似的事情public virtual IList<NewContentPartRecord> { get; set; }

是的,我在 Orchard 文档中查看了创建 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 v. 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的类属性PartRecord 将是类型int

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;
        }
    }
}

您可以在此处找到有关标志属性的更多信息。它基本上使您能够为单个枚举字段使用多个枚举值,这正是您正在寻找的。


编辑:

我花时间构建了一个自定义模块来演示这种技术。我已经对其进行了测试,并且它以应有的方式工作。所以这里是来源:

迁移.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>

但是,在实施此技术时,您必须牢记两件事:

  1. 枚举在内部被视为整数,这意味着它们的值占用 32 位。这反过来意味着MyEnum不能有超过 32 个枚举项为此定义
  2. 正如 Bertrand 指出的那样,数据库搜索项目会更难(尽管并非不可能,因为主要数据库允许您使用按位运算符)

这两个约束都可以通过在数据库和模型之间使用不同的映射函数来绕过。

这意味着什么?

在我向您展示的示例中,数据库值(和MultipleEnumPickerRecord值)是类型int,而在MultipleEnumPickerPart我已将该整数“映射”到List<MyEnum>. 这在数据库中使用的空间更少,并且比使用其他一些映射函数更快。

例如,您可以使用string数据库的类型,然后对内部MultipleEnumPickerRecord进行某种映射。最流行的字符串映射函数是List<MyEnum>MultipleEnumPickerPart

  • 逗号分隔的映射 - 例如,如果有人选择Enum1and Enum4,您可以将其映射到字符串"Enum1,Enum4"
  • 分号分隔的映射 - 您将前面的示例映射到"Enum1;Enum4"

要选择的分隔符类型应基于您知道字符串不会使用的字符。要从数据库中的字符串解构列表,您可以使用简单的value.Split(',').ToList();(如果您使用 ',' 作为分隔符)。

这样一来,您就不会只受到 32 个枚举项的限制,而且由于该值被保存为字符串,因此在数据库中搜索某个值非常简单。缺点是 string 将在数据库中占用更多空间(int 将从字符串中占用一个字符的空间),并且字符串操作函数比上面示例中演示的按位函数要慢一些。

于 2012-12-14T08:55:16.157 回答
2

您根本无法像那样映射 List<T> 。您需要与另一个记录/表创建适当的关系,或者您可以使用例如逗号分隔的值列表来管理存储。第一种情况当然更简洁一些,但第二种情况更容易,坦率地说,如果您不期望给定记录有太多价值的话。

您可能还想考虑一个枚举字段。

于 2012-12-14T05:06:11.093 回答