我花了五 (5) 天的大部分时间来弄清楚如何使这项工作成功:
我使用 enum
用于通过单选按钮列表、下拉列表(这些是多个选项中的一 (1) 个选项)以及复选框列表和多选列表(这些是一个(1) 对所有的几个选择)。
现在,有了单选按钮列表和下拉列表,我可以毫无问题地保存在 Orchard 数据库中。用户选择一个选项,该选项将保存为特定的 enum
选入数据库。
但是,对于复选框列表或多选列表,我无法让 Orchard/NHibernate 保存多选 enum
的。
我已经尝试了一切我可以在 SO 上或通过 Google 搜索找到的方法。唯一“可行”的解决方案(对于这种情况来说有点矫枉过正)是创建一个新表(通过迁移)/部分/部分记录组合,在某些情况下仅用于存储 7-8 个选择。然后,当然,我可以做类似 public virtual IList<NewContentPartRecord> { get; set; }
的事情.
是的,我看过Creating 1-N and N-N Relations在 Orchard 文档中。有人认为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 会允许这种类型的行为,而不需要我创建一个新表来引用(在这种情况下,这似乎有点过分了)。
如果有人能提供帮助,我将不胜感激。我对这个问题束手无策。赏金?现金?你说出来。是的,我就是这么绝望。 :)
最佳答案
你可以装饰你的 enum MyEnum
与 [Flags]
属性并将其项的值设置为 2 的不同幂。例如,您的枚举可能如下所示:
[Flags]
public enum MyEnum
{
Enum1 = 1,
Enum2 = 2,
Enum3 = 4,
Enum4 = 8,
Enum5 = 16
}
现在,您的 MyEnumCheckBox
PartRecord
的属性(property)类的类型为 int
:
public virtual int MyEnumCheckBox { get; set; }
您可以在 Part
中创建代理属性类(class)。例如:
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 属性的信息 here .它主要用于使您能够为单个枚举字段使用多个枚举值,而这正是您要查找的内容。
编辑:
我花时间构建了一个自定义模块来演示此技术。我已经对其进行了测试,它按应有的方式工作。所以这是来源:
迁移.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>
最后, View :
@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>
但是,在实现此技术时,您必须牢记两点:
- 枚举在内部被视为整数,这意味着它们的值占用 32 位。这反过来意味着
MyEnum
不能为此定义超过 32 个枚举项 - 正如 Bertrand 指出的那样,数据库搜索项目会更加困难(尽管并非不可能,因为主要数据库允许您使用按位运算符)
可以通过在数据库和模型之间使用不同的映射函数来绕过这两个约束。
这是什么意思?
在我向您展示的示例中,数据库值(和 MultipleEnumPickerRecord
值)的类型为 int
而在 MultipleEnumPickerPart
我已经将该整数“映射”为 List<MyEnum>
.这在数据库中使用的空间更少,并且比使用其他一些映射函数更快。
例如,您可以使用 string
输入您的数据库和 MultipleEnumPickerRecord
然后对 List<MyEnum>
进行某种映射MultipleEnumPickerPart
内部.最流行的字符串映射函数是
- 逗号分隔映射 - 例如,如果有人选择了
Enum1
和Enum4
,您可以将其映射到字符串"Enum1,Enum4"
- 以分号分隔的映射 - 您可以将前面的示例映射到
"Enum1;Enum4"
要选择的分隔符类型应基于您知道您的字符串不会使用的字符。要从数据库中的字符串解构列表,您可以使用简单的 value.Split(',').ToList();
(如果您使用“,”作为分隔符)。
这样,您就不会只受 32 个枚举项的限制,而且由于值被保存为字符串,因此在数据库中搜索某个值非常简单。缺点是字符串会在数据库中占用更多空间(int 会占用字符串中一个字符的空间),并且字符串操作函数比上面示例中演示的按位函数慢一些。
关于asp.net-mvc-3 - 尝试根据 Orchard (NHibernate) 中的枚举保存复选框选择,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13870455/