2013-12-19 100 views
3

正在創建我的第一個果園模塊,我遇到了將表單數據保存回數據庫的問題。根據我看到的大量樣本,我已經正確註冊了所有東西,所以我必須錯過一些小的事情。堅持零件記錄到數據庫

我能夠拿到公寓形式下的新菜單顯示,驗證工作,但是當我填寫表格,完全地和打救我得到:

你的公寓已創建。

檢查數據庫中的記錄是不是在該表並檢查日誌示出了:

2013年12月19日09:15:23416 [19] NHibernate.Transaction.ITransactionFactory - DTC事務prepre 階段失敗NHibernate.Exceptions.GenericADOException:不能 執行批處理命令[SQL:SQL不可用] ---> System.Data.SqlClient.SqlException:無法將值NULL插入 列'FloorPlanName',表 'Orchard.dbo.CommunityWebsiteSolutions_ApartmentPa rtRecord「; 列不允許有空值。 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)); 
     } 
    } 

回答

2

你的錯誤消息,說明這個漂亮的清楚:

System.Data.SqlClient.SqlException:無法將NULL值插入列「FloorPlanName」,表「Orchard.dbo.CommunityWebsiteSolutions_ApartmentPartRecord」;列不允許有空值。 INSERT失敗。

  1. 您使用可空類型,如stringint?類型的記錄類,這意味着你要允許空值:因爲發生

您的問題。

  • 但是,您正在數據庫遷移中指定要禁止空值。
  • 當C#實例化你的Record類時,它使用默認值初始化字段,對於可爲空的類型爲null。
  • 您可以執行下列操作之一:

    1. 讓您的數據庫列可爲空(刪除NotNull
    2. 讓你記錄類使用非可空類型(例如,int,而不是int?) 。請注意,這不適用於參考類型,如string
    3. 通過給類的構造函數賦予Record類的非空默認值。這可能是不好的做法,因爲您將在基類中調用虛擬屬性,但在NHibernate中似乎沒問題。
    4. 通過爲您的零件指定一個OnInitializing處理程序,將其放置在您的Handler類中,從而爲您的Record類的字段賦予非空默認值。

    UPDATE

    你的評論,你期待的領域要由TryUpdateModel在你的驅動程序類的Editor功能填充。這最終發生,但發生的事件的實際順序是這樣的(你可以看到這在Orchard.Core.Contents.Controllers.AdminControllerCreatePOST法):

    1. ContentManager.New()與內容類型ID在內存中創建的內容項目。此步驟調用OnInitializing以獲取處理程序中定義的內容類型的適當內容部分。
    2. ContentManager.Create()與草稿模式中的內容項目。 這一步實際上會嘗試將項目保存到數據庫一次。
    3. ContentManager.UpdateEditor()。這是實際調用內容類型相應驅動程序的Editor的調用。
    4. 檢查ModelState並回滾事務,如果有任何失敗。

    如果您在標記爲NotNull的列中有NULL值,則步驟2將失敗,因爲這些字段在該點具有默認值。對於這些列,您必須在步驟2之前使用OnInitializing或在Record部分使用構造函數來填充它們。

    換句話說,驅動程序中的TryUpdateModel實際上是將更改直接應用於已經爲Create d的實體,現在已連接到NHibernate會話。

    +0

    也許我有點密集,但是當表單在Drivers Editor函數中發佈和處理時,updater.TryUpdateModel會使用POST中的所有值填充零件。之後的某些時間,值保存時會變爲NULL。我想保留記錄類的可空類型,因爲我們不會總是有一個WhenAvailable日期和RentAmount,如果我們沒有它們,我不想在這些字段中設置值。我會嘗試從列定義中刪除NotNulls,但這似乎是錯誤的。 –

    +0

    你沒有用'NotNull'標記'WhenAvailable'和'RentAmount',所以這些列不會引起問題。但是其他專欄如「FloorPlanName」和「NumberOfBedrooms」呢?那些在你的記錄中是可空的類型,那麼爲什麼你在數據庫列中將它們標記爲'NotNull'? –

    +0

    我已經更新了我的答案,並解釋了爲什麼您依賴TryUpdateModel的方法無效,以及爲您的內容部分提供默認值的另一種方式。 –