Building my first module in Orchard, and everything but the save (on some fields) is working. I get no errors on save, the fields just show their default value with no values being inserted/updated in the database table.
I built the module with only one property at first, and then added more to the recordpart once I confirmed things updated from the admin. Now the new fields show up in the editor, but only the original property (SoldOut) is saving on update or create. I saw another post that recommended deleting the mappings.bin file, but that didn't change anything. Thanks for any help!
Here's the main classes:
public class ConferencePartRecord : ContentPartRecord
{
public virtual bool OnlyShowTeaser { get; set; }
public virtual int TheYear { get; set; }
public virtual DateTime StartDate { get; set; }
public virtual DateTime EndDate { get; set; }
public virtual DateTime EarlyReg { get; set; }
public virtual DateTime RegularReg { get; set; }
public virtual DateTime LateReg { get; set; }
public virtual DateTime ConfirmDate { get; set; }
public virtual bool SoldOut { get; set; }
public virtual bool ConferenceSpace { get; set; }
public virtual int EarlyBirdException { get; set; }
public virtual string NextConf { get; set; }
}
public class ConferencePart : ContentPart<ConferencePartRecord>
{
public bool OnlyShowTeaser
{
get { return Record.OnlyShowTeaser; }
set { Record.OnlyShowTeaser = value; }
}
public int TheYear
{
get { return Record.TheYear; }
set { Record.TheYear = value; }
}
public DateTime StartDate
{
get { return Record.StartDate; }
set { Record.StartDate = value; }
}
public DateTime EndDate
{
get { return Record.EndDate; }
set { Record.EndDate = value; }
}
public DateTime EarlyReg
{
get { return Record.EarlyReg; }
set { Record.EarlyReg = value; }
}
public DateTime RegularReg
{
get { return Record.RegularReg; }
set { Record.RegularReg = value; }
}
public DateTime LateReg
{
get { return Record.LateReg; }
set { Record.LateReg = value; }
}
public DateTime ConfirmDate
{
get { return Record.ConfirmDate; }
set { Record.ConfirmDate = value; }
}
public bool ConferenceSpace
{
get { return Record.ConferenceSpace; }
set { Record.ConferenceSpace = value; }
}
public int EarlyBirdException
{
get { return Record.EarlyBirdException; }
set { Record.EarlyBirdException = value; }
}
public String NextConf
{
get { return Record.NextConf; }
set { Record.NextConf = value; }
}
public bool SoldOut
{
get { return Record.SoldOut; }
set { Record.SoldOut = value; }
}
}
Here is my driver class:
public class ConferenceDriver : ContentPartDriver<AeriesConference.Models.ConferencePart>
{
protected override DriverResult Display(ConferencePart part, string displayType, dynamic shapeHelper)
{
return ContentShape("Parts_Conference", () => shapeHelper.Parts_Conference(SoldOut: part.SoldOut));
}
//GET
protected override DriverResult Editor(ConferencePart part, dynamic shapeHelper)
{
return ContentShape("Parts_Conference_Edit",
() => shapeHelper.EditorTemplate(
TemplateName: "Parts/Conference",
Model: part,
Prefix: Prefix));
}
//POST
protected override DriverResult Editor(ConferencePart part, IUpdateModel updater, dynamic shapeHelper)
{
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
}
... the migrations.cs class (so you can see my updates - which are all reflected in the DB)
public int Create() {
// Creating table ConferenceRecord
SchemaBuilder.CreateTable("ConferencePartRecord", table => table
.ContentPartRecord()
.Column("SoldOut", DbType.Boolean)
);
return 1;
}
public int UpdateFrom1()
{
// Create (or alter) a part called "ConferencePart" and configure it to be "attachable".
ContentDefinitionManager.AlterPartDefinition("ConferencePart", part => part
.Attachable());
return 2;
}
public int UpdateFrom2()
{
SchemaBuilder.AlterTable("ConferencePartRecord", table => table.AddColumn("OnlyShowTeaser", DbType.Boolean));
SchemaBuilder.AlterTable("ConferencePartRecord", table => table.AddColumn("TheYear", DbType.Int16));
SchemaBuilder.AlterTable("ConferencePartRecord", table => table.AddColumn("StartDate", DbType.DateTime));
SchemaBuilder.AlterTable("ConferencePartRecord", table => table.AddColumn("EndDate", DbType.DateTime));
SchemaBuilder.AlterTable("ConferencePartRecord", table => table.AddColumn("EarlyReg", DbType.DateTime));
SchemaBuilder.AlterTable("ConferencePartRecord", table => table.AddColumn("RegularReg", DbType.DateTime));
SchemaBuilder.AlterTable("ConferencePartRecord", table => table.AddColumn("LateReg", DbType.DateTime));
SchemaBuilder.AlterTable("ConferencePartRecord", table => table.AddColumn("ConfirmDate", DbType.DateTime));
SchemaBuilder.AlterTable("ConferencePartRecord", table => table.AddColumn("ConferenceSpace", DbType.Boolean));
SchemaBuilder.AlterTable("ConferencePartRecord", table => table.AddColumn("EarlyBirdException", DbType.Int16));
SchemaBuilder.AlterTable("ConferencePartRecord", table => table.AddColumn("NextConf", DbType.String));
return 3;
}
}
and, finally, my handler class:
public class ConferencePartHandler : ContentHandler
{
public ConferencePartHandler(IRepository<ConferencePartRecord> repository)
{
Filters.Add(StorageFilter.For(repository));
}
}
Of course, I found the issue 5 minutes after posting the question. I had missed one of the datetime fields in the cshtml file for the editor, and when it was saving to the database it was dying on the update since the date column won't take a null.
Related
I can't display values in grid from ICollection. I use this lib http://mvc6-grid.azurewebsites.net/.
I tried to create a cycle to iterate through ICollection but it didn't work
//Model
public partial class CsoSupport {
public CsoSupport()
{
CsoSupportMonitoring = new HashSet<CsoSupportMonitoring>();
}
public string ExpectedResult { get; set; }
public ICollection<CsoSupportMonitoring> CsoSupportMonitoring { get; set; }
}
//View
#(Html.Grid(Model.csoFinancialSupportModel).Build(columns =>
{
columns.Add(model => model.ExpectedResult).Titled("ExpectedResult"); //works
columns.Add(model => model.CsoSupportMonitoring).Titled("Date"); //not working
}
I want to display dates from model.csoSupportMonitoring
Maybe create a property specifically for display? And do something like the following.
//Class
public partial class CsoSupport {
public CsoSupport()
{
CsoSupportMonitoring = new HashSet<CsoSupportMonitoring>();
}
public string ExpectedResult { get; set; }
public string CsoSupportMonitoringDates {
get {
return string.Join(",", CsoSupportMonitoring.Dates):
}
}
public ICollection<CsoSupportMonitoring> CsoSupportMonitoring { get; set; }
}
//View
#(Html.Grid(Model.csoFinancialSupportModel).Build(columns =>
{
columns.Add(model => model.ExpectedResult).Titled("ExpectedResult");
columns.Add(model => model.CsoSupportMonitoringDates).Titled("Date");
}
When I try update database the property Childrens not saving but SortOrder
and display name are success.
This is my model:
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Int64 Id { get; set; }
public Int64? LanguageId { get; set; }
[ForeignKey("LanguageId")]
public Language Language { get; set; }
public int SortOrder { get; set; }
public string DisplayName { get; set; }
[ForeignKey("Childrens")]
public Int64? ParentId { get; set; }
public ICollection<Menu> Childrens { get; set; }
I called this function with new parameters:
public void SaveMenu(List<Menu> newMenu)
{
foreach(Menu _addMenu in newMenu)
{
this.Update(_addMenu);
}
this.Commit();
}
public void Update(T entity)
{
try
{
DbEntityEntry _dbEntityEntry = this.DbContext.Entry(entity);
DbContext.Entry(entity).State = EntityState.Modified;
if (_dbEntityEntry.State == EntityState.Detached)
this.dbSet.Attach(entity);
_dbEntityEntry.State = EntityState.Modified;
}
}
public bool Commit()
{
try
{
this.DbContext.SaveChanges();
}
catch (Exception ex)
{
return false;
}
return true;
}
And the childrens are not change but sortOrder success to change.
Why is it happening?
EDIT:
I found solution the problem because the children objects are dummy and need to get the "real" object from the DB
public void SaveMenu(List<Menu> newMenu)
{
foreach(Menu _addMenu in newMenu)
{
List<Menu> listMenu = new List<Menu>();
foreach (Menu child in _addMenu.Childrens)
{
Menu _menuFromDB = this.Query().FirstOrDefault(s => s.Id == child.Id);
_menuFromDB.SortOrder = child.SortOrder;
listMenu.Add(_menuFromDB);
}
_addMenu.Childrens = listMenu;
this.Update(_addMenu,null);
}
this.Commit();
}
After changing relationship of Question and PossibleAnswer from Many-to-many to One-to-many I got an exception: "The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: Multiplicity constraint violated. The role 'PossibleAnswer_Question_Source' of the relationship 'WebUI.Models.PossibleAnswer_Question' has multiplicity 1 or 0..1." What does it mean?
Here is my model:
public class Question
{
public int ID { get; set; }
public string Text { get; set; }
public bool IsAssociatedWithProfessor { get; set; }
public bool IsAssociatedWithAssistant { get; set; }
public virtual ICollection<PossibleAnswer> PossibleAnswers { get; set; }
public virtual ICollection<Results> Results { get; set; }
}
public class PossibleAnswer
{
public int ID { get; set; }
public string Text { get; set; }
public virtual Question Question { get; set; }
}
And view model for Question (I know that ToQuestion should be in controller, I will rearrange it later):
public class QuestionVM
{
public QuestionVM() {
}
public QuestionVM(Question question) : this()
{
ID = question.ID;
Text = question.Text;
IsAssociatedWithProfessor = question.IsAssociatedWithProfessor;
IsAssociatedWithAssistant = question.IsAssociatedWithAssistant;
}
public int? ID { get; set; }
public string Text { get; set; }
public bool IsAssociatedWithProfessor { get; set; }
public bool IsAssociatedWithAssistant { get; set; }
private IEnumerable<string> _possibleAnswers;
public IEnumerable<string> PossibleAnswers
{
get
{
return _possibleAnswers ?? new List<string>(){"", "", "", "", ""};
}
set
{
_possibleAnswers = value;
}
}
public Question ToQuestion()
{
Question question = new Question
{
Text = this.Text,
IsAssociatedWithProfessor = this.IsAssociatedWithProfessor,
IsAssociatedWithAssistant = this.IsAssociatedWithAssistant,
PossibleAnswers = new List<PossibleAnswer>()
};
//ID will be null if creating new question
if(ID != null)
{
question.ID = (int) ID;
}
foreach (string possibleAnswer in this.PossibleAnswers)
{
if (!String.IsNullOrEmpty(possibleAnswer))
{
question.PossibleAnswers.Add(new PossibleAnswer { Text = possibleAnswer });
}
}
return question;
}
}
Here is my post method for creating new question:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddQuestion(QuestionVM questionVM)
{
try
{
if (ModelState.IsValid)
{
Question question = questionVM.ToQuestion();
context.Questions.Add(question);
context.SaveChanges();
return RedirectToAction("Questions");
}
}
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Trenutno nije moguće snimiti promjene, pokušajte ponovo.");
}
return View(questionVM);
}
Line:question.PossibleAnswers.Add(new PossibleAnswer { Text = possibleAnswer });
causes the exception since I didn't save possible answers in database before I am adding them to question... But how can I add them to database without use of DbContext (since it is not a good practice to use DbContext in method which converts view model to model)?
Error is coming because PossibleAnswers are not being marked as added entity.
So update your QuestionVM like below:
public class QuestionVM
{
public QuestionVM() {
}
public QuestionVM(Question question) : this()
{
ID = question.ID;
Text = question.Text;
IsAssociatedWithProfessor = question.IsAssociatedWithProfessor;
IsAssociatedWithAssistant = question.IsAssociatedWithAssistant;
}
public int? ID { get; set; }
public string Text { get; set; }
public bool IsAssociatedWithProfessor { get; set; }
public bool IsAssociatedWithAssistant { get; set; }
private IEnumerable<string> _possibleAnswers;
public IEnumerable<string> PossibleAnswers
{
get
{
return _possibleAnswers ?? new List<string>(){"", "", "", "", ""};
}
set
{
_possibleAnswers = value;
}
}
public Question ToQuestion()
{
Question question = new Question
{
Text = this.Text,
IsAssociatedWithProfessor = this.IsAssociatedWithProfessor,
IsAssociatedWithAssistant = this.IsAssociatedWithAssistant,
PossibleAnswers = new List<PossibleAnswer>()
};
//ID will be null if creating new question
if(ID != null)
{
question.ID = (int) ID;
}
return question;
}
public List<PossibleAnswer> GetPosibleAnswers()
{
var listOfPossibleAnswers = new List<PossibleAnswer>();
foreach (string possibleAnswer in this.PossibleAnswers)
{
if (!String.IsNullOrEmpty(possibleAnswer))
{
listOfPossibleAnswers.Add(new PossibleAnswer { Text = possibleAnswer });
}
}
return listOfPossibleAnswers;
}
}
and then update your code like below.
if (ModelState.IsValid)
{
Question question = questionVM.ToQuestion();
context.Questions.Add(question);
context.SaveChanges();
question.PossibleAnswers.AddRange(questionVM.GetPosibleAnswers());
context.SaveChanges();
return RedirectToAction("Questions");
}
I've a problem with orchard.
I've created a widget that is a video player, it's a simple widget with the class ContentPartRecord:
public class VideoPlayerPartRecord : ContentPartRecord
{
public virtual string MediaFile { get; set; }
public virtual int Width { get; set; }
public virtual int Height { get; set; }
public virtual bool AutoStart { get; set; }
public virtual bool Repeat { get; set; }
}
and the class ContentPart:
public class VideoPlayerPart : ContentPart<VideoPlayerPartRecord>
{
[Required(ErrorMessage = "Media File required")]
[Display(Name = "Media File: ")]
public string MediaFile
{
get { return Record.MediaFile; }
set { Record.MediaFile = value; }
}
[Required(ErrorMessage = "Width required")]
[Display(Name = "Width: ")]
public int Width
{
get { return Record.Width; }
set { Record.Width = value; }
}
[Required(ErrorMessage = "Height required")]
[Display(Name = "Height: ")]
public int Height
{
get { return Record.Height; }
set { Record.Height = value; }
}
[Display(Name = "Auto Start: ")]
public bool AutoStart
{
get { return Record.AutoStart; }
set { Record.AutoStart = value; }
}
[Display(Name = "Repeat: ")]
public bool Repeat
{
get { return Record.Repeat; }
set { Record.Repeat = value; }
}
}
this is the file migration:
public class Migrations : DataMigrationImpl {
public int Create() {
// Creating table default_Raise_VideoPlayer_VideoPlayePartRecord
SchemaBuilder.CreateTable("default_Raise_VideoPlayer_VideoPlayePartRecord", table => table
.ContentPartRecord()
.Column("MediaFile", DbType.String)
.Column("Width", DbType.Int32)
.Column("Height", DbType.Int32)
.Column("AutoStart", DbType.Boolean)
.Column("Repeat", DbType.Boolean)
);
ContentDefinitionManager.AlterPartDefinition(typeof(VideoPlayerPart).Name, cfg => cfg
.Attachable());
return 1;
}
public int UpdateFrom1() {
ContentDefinitionManager.AlterTypeDefinition("VideoPlayerWidget", cfg => cfg
.WithPart("VideoPlayerPart")
.WithPart("WidgetPart")
.WithPart("CommonPart")
.WithSetting("Stereotype", "Widget"));
return 2;
}
}
the problem is that when I insert the widget it is added but I can't see it. Why??
You need to add a Handler for your widget to persist the item, and a placement.info entry to present it.
http://docs.orchardproject.net/Documentation/Writing-a-content-part
(code taken from article)
using Maps.Models;
using Orchard.ContentManagement.Handlers;
using Orchard.Data;
namespace Maps.Handlers {
public class MapHandler : ContentHandler {
public MapHandler(IRepository<MapRecord> repository) {
Filters.Add(StorageFilter.For(repository));
}
}
}
and Placement.info
<Placement>
<Place Parts_Map="Content:10"/>
<Place Parts_Map_Edit="Content:7.5"/>
</Placement>
If it doesn't save into a database - its probably the handler. If it doesn't display on screen, it's probably placement.info
Also you didn't mention having a driver or a view, but I'd guess it is the Handler or the placement info you are missing. Also, check spelling and convention very carefully when relating the driver, migration and placement info, as there are several places you can use the wrong text string if you create your parts manually.
I created a custom module using this guide from Orchard documentation, but for some reason I can't see the fields in the content type when I want to create a new one.
this is my model:
public class CustomerPartRecord : ContentPartRecord
{
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual int PhoneNumber { get; set; }
public virtual string Address { get; set; }
public virtual string Profession { get; set; }
public virtual string ProDescription { get; set; }
public virtual int Hours { get; set; }
}
public class CustomerPart : ContentPart<CustomerPartRecord>
{
[Required(ErrorMessage="you must enter your first name")]
[StringLength(200)]
public string FirstName { get { return Record.FirstName; } set { Record.FirstName = value; } }
[Required(ErrorMessage = "you must enter your last name")]
[StringLength(200)]
public string LastName { get { return Record.LastName; } set { Record.LastName = value; } }
[Required(ErrorMessage = "you must enter your phone number")]
[DataType(DataType.PhoneNumber)]
public int PhoneNumber { get { return Record.PhoneNumber; } set { Record.PhoneNumber = value; } }
[StringLength(200)]
public string Address { get { return Record.Address; } set { Record.Address = value; } }
[Required(ErrorMessage = "you must enter your profession")]
[StringLength(200)]
public string Profession { get { return Record.Profession; } set { Record.Profession = value; } }
[StringLength(500)]
public string ProDescription { get { return Record.ProDescription; } set { Record.ProDescription = value; } }
[Required(ErrorMessage = "you must enter your hours")]
public int Hours { get { return Record.Hours; } set { Record.Hours = value; } }
}
this is the Handler:
class CustomerHandler : ContentHandler
{
public CustomerHandler(IRepository<CustomerPartRecord> repository)
{
Filters.Add(StorageFilter.For(repository));
}
}
the Driver:
class CustomerDriver : ContentPartDriver<CustomerPart>
{
protected override DriverResult Display(CustomerPart part, string displayType, dynamic shapeHelper)
{
return ContentShape("Parts_Customer", () => shapeHelper.Parts_Customer(
FirstName: part.FirstName,
LastName: part.LastName,
PhoneNumber: part.PhoneNumber,
Address: part.Address,
Profession: part.Profession,
ProDescription: part.ProDescription,
Hours: part.Hours));
}
//GET
protected override DriverResult Editor(CustomerPart part, dynamic shapeHelper)
{
return ContentShape("Parts_Customer", () => shapeHelper.EditorTemplate(
TemplateName:"Parts/Customer",
Model: part,
Prefix: Prefix));
}
//POST
protected override DriverResult Editor(CustomerPart part, IUpdateModel updater, dynamic shapeHelper)
{
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
the migration:
public class Migrations : DataMigrationImpl
{
public int Create()
{
// Creating table CustomerPartRecord
SchemaBuilder.CreateTable("CustomerPartRecord", table => table
.ContentPartRecord()
.Column("FirstName", DbType.String)
.Column("LastName", DbType.String)
.Column("PhoneNumber", DbType.Int32)
.Column("Address", DbType.String)
.Column("Profession", DbType.String)
.Column("ProDescription", DbType.String)
.Column("Hours", DbType.Int32)
);
return 1;
}
public int UpdateFrom1()
{
ContentDefinitionManager.AlterPartDefinition("CustomerPart",
builder => builder.Attachable());
return 2;
}
public int UpdateFrom2()
{
ContentDefinitionManager.AlterTypeDefinition("Customer", cfg => cfg
.WithPart("CommonPart")
.WithPart("RoutePart")
.WithPart("BodyPart")
.WithPart("CustomerPart")
.WithPart("CommentsPart")
.WithPart("TagsPart")
.WithPart("LocalizationPart")
.Creatable()
.Indexed());
return 3;
}
}
Can someone please tell me if I am missing something?
Did you add an entry in the placement.info file? I forget that sometimes. Without placement, it won't know where to put your content so it just doesn't get rendered.