将部分记录保存到数据库

时间:2013-12-19 17:33:47

标签: nhibernate orchardcms orchardcms-1.7

正在创建我的第一个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));
        }
    }

1 个答案:

答案 0 :(得分:2)

您的错误消息非常清楚地解释了这一点:

  

System.Data.SqlClient.SqlException:无法将值NULL插入列'FloorPlanName',表'Orchard.dbo.CommunityWebsiteSolutions_ApartmentPartRecord';列不允许空值。 INSERT失败。

出现问题是因为:

  1. 您在Record类中使用可以为stringint?类型的可空类型,这意味着您希望允许空值。
  2. 但是,您在数据库迁移中指定要禁止空值。
  3. 当C#实例化您的Record类时,它会使用默认值初始化字段,对于可空类型,该值为null。
  4. 您可以执行以下操作之一:

    1. 使您的数据库列可以为空(删除NotNull
    2. 使您的Record类使用不可为空的类型(例如,int而不是int?)。请注意,这不是string等参考类型的选项。
    3. 通过为类提供构造函数,为Record类的字段提供非null的默认值。这可以说是不好的做法,因为你将在基类中调用虚拟属性,但在NHibernate中似乎没问题。
    4. 通过为您的部件提供OnInitializing处理程序,将非空的默认值赋予Record类的字段,该处理程序将放置在您的Handler类中。
    5. <强>更新

      您评论说您希望字段由驱动程序类的TryUpdateModel函数中的Editor填充。这最终会发生,但发生的实际事件序列是这样的(您可以在CreatePOST的{​​{1}}方法中看到这一点):

      1. Orchard.Core.Contents.Controllers.AdminController使用内容类型ID在内存中创建内容项。此步骤调用ContentManager.New()以获取内容类型的相应内容部分,这些内容部分在处理程序中定义。
      2. OnInitializing,其中包含草稿模式中的内容项。 此步骤实际上会尝试将项目持久保存到数据库。
      3. ContentManager.Create()。这是实际调用内容类型的相应驱动程序ContentManager.UpdateEditor()的调用。
      4. 检查Editor并在事件失败时回滚交易。
      5. 如果在标记为ModelState的列中包含NULL值,则步骤2将失败,因为该字段在该点具有默认值。对于这些列,您必须在步骤2之前使用NotNull或使用记录部分上的构造函数填写它们。

        换句话说,驱动程序中的OnInitializing实际上是将更改直接应用于已经TryUpdateModel d的实体,现在已连接到NHibernate会话。