正在创建我的第一个Orchard模块,我遇到了将表单数据保存回数据库的问题。从我看到很多样本的情况来看,我已经正确地注册了所有内容,所以我必须遗漏一些小的东西。
我能够在新菜单下显示公寓表格,验证工作正常但当我填写表格并点击保存时我得到:
您的公寓已经创建。
检查数据库记录不在表中,检查日志显示:
2013-12-19 09:15:23,416 [19] NHibernate.Transaction.ITransactionFactory - DTC事务预处理 阶段失败NHibernate.Exceptions.GenericADOException:不能 执行批处理命令。[SQL:SQL不可用] ---> System.Data.SqlClient.SqlException:无法将值NULL插入 列'FloorPlanName',表 'Orchard.dbo.CommunityWebsiteSolutions_ApartmentPartRecord'; 列不允许空值。 INSERT失败。
运行SQL事件探查器会显示一个插入,其中所有列都设置为NULL。
Migrations.cs
SchemaBuilder.CreateTable(typeof(ApartmentPartRecord).Name, table => table
.ContentPartRecord()
.Column<string>("FloorPlanName", c => c.WithLength(25).NotNull())
.Column<string>("FullAddress", c => c.WithLength(256).NotNull()))
.Column<string>("ShortDescription", c => c.WithLength(150).NotNull())
.Column("NumberOfBedrooms", DbType.Int32, c => c.NotNull())
.Column("NumberOfBathrooms", DbType.Int32, c => c.NotNull())
.Column("SquareFootage", DbType.Int32, c => c.NotNull())
.Column("WhenAvailable", DbType.DateTime)
.Column("RentAmount", DbType.Decimal)
);
ContentDefinitionManager.AlterPartDefinition(typeof (ApartmentPart).Name, part => part.Attachable());
ApartmentPart
public class ApartmentPartRecord : ContentPartRecord {
public virtual string FloorPlanName { get; set; }
public virtual string ShortDescription { get; set; }
public virtual string FullAddress { get; set; }
public virtual int? NumberOfBedrooms { get; set; }
public virtual int? NumberOfBathrooms { get; set; }
public virtual int? SquareFootage { get; set; }
public virtual DateTime? WhenAvailable { get; set; }
public virtual decimal? RentAmount { get; set; }
}
public class ApartmentPart : ContentPart<ApartmentPartRecord> {
[Required, StringLength(256)]
[Display(Name = "Address / Unit Number")]
public string FullAddress {
get { return Record.FullAddress; }
set { Record.FullAddress = value; }
}
[Required, StringLength(25)]
[Display(Name = "Floor Plan")]
public string FloorPlanName {
get { return Record.FloorPlanName; }
set { Record.FloorPlanName = value; }
}
[Required, StringLength(150)]
[Display(Name = "Sales Description")]
public string ShortDescription {
get { return Record.ShortDescription; }
set { Record.ShortDescription = value; }
}
[Required]
[Display(Name = "Bedroom Count")]
public int? NumberOfBedrooms {
get { return Record.NumberOfBedrooms; }
set { Record.NumberOfBedrooms = value; }
}
[Required]
[Display(Name = "Bathroom Count")]
public int? NumberOfBathrooms {
get { return Record.NumberOfBathrooms; }
set { Record.NumberOfBathrooms = value; }
}
[Required]
[Display(Name = "Square Footage")]
public int? SquareFootage {
get { return Record.SquareFootage; }
set { Record.SquareFootage = value; }
}
[Display(Name = "First Availability")]
public DateTime? WhenAvailable {
get { return Record.WhenAvailable; }
set { Record.WhenAvailable = value; }
}
[Display(Name = "Rent Amount")]
public decimal? RentAmount {
get { return Record.RentAmount; }
set { Record.RentAmount = value; }
}
}
驱动程序
public class ApartmentPartDriver : ContentPartDriver<ApartmentPart>
{
protected override string Prefix
{
get { return "Apartment"; }
}
//GET
protected override DriverResult Editor(ApartmentPart part, dynamic shapeHelper)
{
return ContentShape("Parts_Apartment_Edit",
() => shapeHelper.EditorTemplate(
TemplateName: "Parts/Apartment",
Model: part,
Prefix: Prefix));
}
//POST
protected override DriverResult Editor(ApartmentPart part, IUpdateModel updater, dynamic shapeHelper)
{
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
}
处理程序
public class ApartmentPartHandler : ContentHandler {
public ApartmentPartHandler(IRepository<ApartmentPartRecord> repository)
{
Filters.Add(StorageFilter.For(repository));
}
}
答案 0 :(得分:2)
您的错误消息非常清楚地解释了这一点:
System.Data.SqlClient.SqlException:无法将值NULL插入列'FloorPlanName',表'Orchard.dbo.CommunityWebsiteSolutions_ApartmentPartRecord';列不允许空值。 INSERT失败。
出现问题是因为:
string
和int?
类型的可空类型,这意味着您希望允许空值。您可以执行以下操作之一:
NotNull
)int
而不是int?
)。请注意,这不是string
等参考类型的选项。OnInitializing
处理程序,将非空的默认值赋予Record类的字段,该处理程序将放置在您的Handler类中。<强>更新强>
您评论说您希望字段由驱动程序类的TryUpdateModel
函数中的Editor
填充。这最终会发生,但发生的实际事件序列是这样的(您可以在CreatePOST
的{{1}}方法中看到这一点):
Orchard.Core.Contents.Controllers.AdminController
使用内容类型ID在内存中创建内容项。此步骤调用ContentManager.New()
以获取内容类型的相应内容部分,这些内容部分在处理程序中定义。OnInitializing
,其中包含草稿模式中的内容项。 此步骤实际上会尝试将项目持久保存到数据库。 ContentManager.Create()
。这是实际调用内容类型的相应驱动程序ContentManager.UpdateEditor()
的调用。Editor
并在事件失败时回滚交易。如果在标记为ModelState
的列中包含NULL值,则步骤2将失败,因为该字段在该点具有默认值。对于这些列,您必须在步骤2之前使用NotNull
或使用记录部分上的构造函数填写它们。
换句话说,驱动程序中的OnInitializing
实际上是将更改直接应用于已经TryUpdateModel
d的实体,现在已连接到NHibernate会话。