I have recently created a new application that is using MVC4 and EF5. The tables already existed, so we are using Database first. This is all new to me, it is my first time in MVC or EF.
I have some models in my EMDX file, and in one of them I inherited IValidatableObject and put some code in the Validate function. This was working fine, then I changed my view to use a ViewModel, now I get an error from validate, and I am stumped. It is still calling my validate function, but no longer posting it back to the screen, i just get a yellow screen.
Error:
Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
Model:
public partial class Names : IValidatableObject {
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
// some logic, this works
}
}
ViewModel:
public class NamesVM {
public Names Name { get; set; }
// some other stuff in this model, but not part of this problem
}
Controller: Edit Function:
[HttpPost]
public ActionResult Edit(NamesVM nvm) {
if (ModelState.IsValid) {
dbCommon.Entry(nvm.Name).State = EntityState.Modified;
dbCommon.SaveChanges();
return RedirectToAction("Index");
}
return View(nvm);
}
View:
#model NamesVM
<div class="editor-label">
#Html.LabelFor(model => model.Name.Id)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name.Id)
#Html.ValidationMessageFor(model => model.Name.Id)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Name.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name.Name)
#Html.ValidationMessageFor(model => model.Name.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Name.Active)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name.Active)
#Html.ValidationMessageFor(model => model.Name.Active)
</div>
<input type="submit" value="Save" />
The codes works fine if everything is correct on the screen, but when validation fails, i don't get a nice error, I get a yellow screen. I am sure I am missing something, I just don't know what.
finally solved it. The IValidatableObject needs to be moved to the ViewModel, as well as the logic for it.
Model
public partial class Names { //: IValidatableObject { //Remove the IValidateableObject from here
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
// this whole method can be commented out
//public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
// some logic, this works
//}
}
View Model
public class NamesVM : IValidatableObject { // add the IValidatableObject to your view model
public Names Name { get; set; }
// some other stuff in this model, but not part of this problem
// and move your validation logic to here
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
// some logic, this works
}
}
Related
I defined a model look like this
public class X {
int a;
MyClass b;
}
and I have an action look like this
public IActionResult Test(X x){}
now when user submit data I want to manipulate that data and then assign it to b.
how can I do it?
You didn't provide the detailed explanation such as how you want to submit data , suppose you have a book model class that contains a child Author property :
public class Book
{
public int id { get; set; }
public string name { get; set; }
public Author author { get; set; }
}
public class Author {
public int authorId { get; set; }
public string authorName { get; set; }
}
In your view , using Tag Helpers in forms , you can bind to Author.authorName :
#model Book
<form asp-controller="Home" asp-action="RegisterInput" method="post">
BookID: <input asp-for="id" /> <br />
BookName: <input asp-for="name" /><br />
AuthorID: <input asp-for="author.authorId" /> <br />
AuthorName: <input asp-for="author.authorName" /><br />
<button type="submit">Register</button>
</form>
In controller , you will find the input values will automatically bind to related properties :
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> RegisterInput(Book book)
{
return View();
}
Walking through this tutorial everything is fine but the data doesn't save.
Tables has been created and edit page shows the fields but no record has been saved. After clicking the save button I get this message:
Your Car ContentType has been created.
Not found
The page you are looking for does not exist.
Any idea?
Models:
public class CarPart : ContentPart<CarPartRecord> {
public string Name {
get { return Record.Name; }
set { Record.Name = value; }
}
public string Description {
get { return Record.Description; }
set { Record.Description = value; }
}
public bool IsPublished {
get { return Record.IsPublished; }
set { Record.IsPublished = value; }
}
}
public class CarPartRecord : ContentPartRecord {
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual bool IsPublished { get; set; }
}
Handlers:
public class CarPartHandler : ContentHandler {
public CarPartHandler(IRepository<CarPartRecord> repository) {
Filters.Add(StorageFilter.For(repository));
}
}
Drivers:
public class CarPartDriver : ContentPartDriver<CarPart> {
protected override string Prefix { get { return "Car"; } }
protected override DriverResult Editor(CarPart part, dynamic shapeHelper) {
return ContentShape("Parts_Car_Edit", () => shapeHelper.EditorTemplate(TemplateName: "Parts/Car", Model: part, Prefix: Prefix));
}
protected override DriverResult Editor(CarPart part, IUpdateModel updater, dynamic shapeHelper) {
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
}
Migrations:
public int Create(){
SchemaBuilder.DropTable("CarPartRecord");
SchemaBuilder.CreateTable("CarPartRecord", table => table
.ContentPartRecord()
.Column<string>("Name",column => column.WithLength(50).NotNull().Unique())
.Column<string>("Description",column => column.WithLength(500))
.Column<bool>("IsPublished",column => column.WithDefault(true).NotNull()));
return 1;
}
public int UpdateFrom1() {
ContentDefinitionManager.AlterPartDefinition("CarPart",part => part.Attachable());
return 2;
}
Errors:
Orchard.Exceptions.DefaultExceptionPolicy - Default - An unexpected exception was caught
http...../OrchardLocal/Admin/Contents/Create/Car
NHibernate.Exceptions.GenericADOException: could not execute batch command.[SQL: SQL not available] ---> System.Data.SqlClient.SqlException: Cannot insert the value NULL into column 'Name', table 'OrchardDB.dbo.Mega_Car_CarPartRecord'; column does not allow nulls. INSERT fails.
NHibernate.AssertionFailure - Default - An AssertionFailure occurred - this may indicate a bug in NHibernate or in your custom types.
http..../OrchardLocal/Admin/Contents/Create/Car
NHibernate.AssertionFailure: null id in Orchard.ContentManagement.Records.ContentTypeRecord entry (don't flush the Session after an exception occurs)
Orchard.Web\Modules\M.Car\Views\EditorTemplates\Parts
#using System.Web.Mvc.Html
#model M.Car.Models.CarPart
<fieldset>
<legend>Car Fields</legend>
<div class="editor-label">#Html.LabelFor(x => x.Name)</div>
<div class="editor-field">
#Html.EditorFor(x => x.Name)
#Html.ValidationMessageFor(x => x.Name)
</div>
<div class="editor-label">#Html.LabelFor(x => x.Description)</div>
<div class="editor-field">
#Html.EditorFor(x => x.Description)
#Html.ValidationMessageFor(x => x.Description)
</div>
<div class="editor-label">#Html.LabelFor(x => x.IsPublished)</div>
<div class="editor-field">
#Html.EditorFor(x => x.IsPublished)
#Html.ValidationMessageFor(x => x.IsPublished)
</div>
</fieldset>
I have the following model in my MVC 4 Internet Application
public class EventModel
{
public int EventId { get; set;}
public string EventTitle { get; set;}
public string ImageUrl { get; set;}
public string VideoUrl { get;set}
}
Here's the Create.cshtml code for the Image and Video
#using(Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<div>
#Html.LabelFor(model =>model.ImageUrl)
</div>
<div>
#Html.EditorFor(model =>model.ImageUrl)
#Html.ValidationMessageFor(model =>model.ImageUrl)
</div>
<div>
#Html.LabelFor(model =>model.VideoUrl)
</div>
<div>
#Html.EditorFor(model =>model.VideoUrl)
#Html.ValidationMessageFor(model =>Video.ImageUrl)
</div>
}
This is my controller code for the post method on create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(EventModel eventmodel)
{
if (ModelState.IsValid)
{
_db.EventModels.Add(eventmodel);
_db.SaveChanges();
return RedirectToAction("Index");
}
return View(eventmodel);
}
How can I make the create page display buttons to upload image from the users computer. How do i modify my controller action to store the paths on the database and then how do I retrieve the image on the index.cshtml page.
For file uploading try this:
In controller:
[HttpPost]
public ActionResult Create(EventModel eventmodel, HttpPostedFileBase file)
{
if (ModelState.IsValid)
{
var filename = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/Uploads/Photo/"), filename);
file.SaveAs(path);
tyre.Url = filename;
_db.EventModels.AddObject(eventmodel);
_db.SaveChanges();
return RedirectToAction("Index");
}
return View(eventmodel);
}
And View:
<div>
Image
<input type="file" name="file" id="file" />
#Html.HiddenFor( model => model.ImageUrl)
#Html.ValidationMessageFor( model => model.Url )
</div>
I've been having a little problem creating a simple widget. All I want to do is create a widget that has the a MediaPickerField and 2 int and 2 string fields that are stored in the database using a part. Everything works as advertised. ContentRecord is created and the image field is properly stored, except that the four fields are not being stored. There are no error in the logs and cannot seem to see why this is happening. Has anyone come across this before? Thank you.
Migration.cs
public int UpdateFrom1()
{
SchemaBuilder.CreateTable("SchoenNavButtonPartRecord",
table => table.ContentPartRecord()
.Column<string>("Url", col=> col.WithLength(2140))
.Column<string>("Text")
.Column<int>("ButtonWidth")
.Column<int>("ButtonHeight"));
ContentDefinitionManager.AlterTypeDefinition("SchoenNavButton", builder =>
builder.WithPart("CommonPart")
.WithPart("SchoenNavButtonPart")
.WithPart("TitlePart")
.Creatable());
ContentDefinitionManager.AlterPartDefinition("SchoenNavButtonPart", builder =>
builder.WithField("ButtonImage", field=>
field.OfType("MediaPickerField")
.WithDisplayName("Button Image")
.WithSetting("Hint", "Select Image for Button")));
return 2;
}
PartRecord
public class SchoenNavButtonPartRecord : ContentPartRecord
{
public virtual string Url { get; set; }
public virtual string Text { get; set; }
public virtual int ButtonWidth { get; set; }
public virtual int ButtonHeight { get; set; }
}
Part
public class SchoenNavButtonPart : ContentPart<SchoenNavButtonPartRecord>
{
public string Url { get; set; }
public string Text { get; set; }
[DefaultValue(296)]
public int ButtonWidth { get; set; }
[DefaultValue(188)]
public int ButtonHeight { get; set; }
}
Handler
public class SchoenNavButtonHandler : ContentHandler
{
public SchoenNavButtonHandler(IRepository<SchoenNavButtonPartRecord> buttonImageLinkRepository)
{
Filters.Add(StorageFilter.For(buttonImageLinkRepository));
}
}
Driver
public class SchoenNavButtonPartDriver : ContentPartDriver<SchoenNavButtonPart>
{
public SchoenNavButtonPartDriver()
{
}
protected override string Prefix
{
get
{
return "SchoenNavButton";
}
}
protected override DriverResult Display(SchoenNavButtonPart part, string displayType, dynamic shapeHelper)
{
var fields = ((ContentPart) part).Fields.OfType<MediaPickerField>();
MediaPickerField mediaPickerField = null;
if(fields.Any())
{
mediaPickerField = fields.ElementAt(0);
}
return ContentShape("Parts_SchoenNavButton",
() => shapeHelper.Parts_SchoenNavButton(
SchoenNavButtonPart: part,
ImageUrl: mediaPickerField == null ? "#" : mediaPickerField.Url
));
}
protected override DriverResult Editor(SchoenNavButtonPart part, dynamic shapeHelper)
{
return ContentShape("Parts_SchoenNavButton_Edit", () =>
shapeHelper.EditorTemplate(
TemplateName: "Parts/SchoenNavButton",
Model: part,
Prefix: Prefix));
}
protected override DriverResult Editor(SchoenNavButtonPart part, Orchard.ContentManagement.IUpdateModel updater, dynamic shapeHelper)
{
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
}
EditorTemplage
#model FishySoftware.SchoenBuilders.Models.SchoenNavButtonPart
<fieldset>
<legend>Button Details</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Url, T("Url"))
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.Url)
#Html.ValidationMessageFor(model => model.Url)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Text, T("Text"))
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.Text)
#Html.ValidationMessageFor(model => model.Text)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ButtonWidth, T("Button Width"))
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.ButtonWidth)
#Html.ValidationMessageFor(model => model.ButtonWidth)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ButtonHeight, T("Button Height"))
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.ButtonHeight)
#Html.ValidationMessageFor(model => model.ButtonHeight)
</div>
</fieldset>
Orchard is always using content of Record classes to save data to the database. You, on the other hand, are saving data from the browser inside the Part class and you haven't properly connected your Part and Record classes.
To do that, you'll have to change your ShoenNavButtonPart to this:
public class SchoenNavButtonPart : ContentPart<SchoenNavButtonPartRecord>
{
public string Url {
get { return Record.Url; }
set { Record.Url = value; }
}
public string Text {
get { return Record.Text; }
set { Record.Text = value; }
}
[DefaultValue(296)]
public int ButtonWidth {
get { return Record.ButtonWidth; }
set { Record.ButtonWidth = value; }
}
[DefaultValue(188)]
public int ButtonHeight {
get { return Record.ButtonHeight; }
set { Record.ButtonHeight = value; }
}
}
I have a very simple data structure with two models. The first containing UserName, UserQuestion and userLocationID and another with LocationName and LocationID, the locationID in the first table is related to the LocationName the second table. However I've not specified any relationship. I've set up the data structure using the code first method in used here .
I would like to create a form which has two text inputs for a user to enter their name and question and a select box that is populated with all the locationNames from the second table. However I can't seem to create the model that allows me to do so. Do I need to make a separate ViewModel?
Does anyone know of a simple tutorial that will explain how to do this?
I'm quite new at MVC, and the dot net framework. . And I've had a look at this answer but I can't seem to modify it to fit my needs. So Apologies if I'm asking for something really basic.
I can give an example in one controller, one view and three C# classes. To use this code, create an empty MVC2 project in visual studio and add a reference to Entity Framework dll version 4.1. If you need help as to where to put these files I recommend Steve Sanderson's MVC2 book.
public class User
{
public int ID { get; set; }
public string UserName { get; set; }
public string Question { get; set; }
public virtual Location Category { get; set; }
}
public class Location
{
public int ID { get; set; }
public string LocationName { get; set; }
}
Repository
using System.Data.Entity;
using System.Collections.Generic;
using System.Linq;
public class Repository : System.Data.Entity.DbContext
{
public DbSet<User> User { get; set; }
public DbSet<Location> Locations { get; set; }
public Repository()
{
this.Database.Connection.ConnectionString =
#"Server=.;Database=Test;Integrated Security=SSPI";
if (!this.Database.Exists())
{
this.Database.Create();
this.Locations.Add(new Location { LocationName = "Queensway" });
this.Locations.Add(new Location { LocationName = "Shepherds Bush" });
this.SaveChanges();
}
}
public IEnumerable<Location> GetLocations()
{
return this.Locations.Where(x => x.ID > -1);
}
public Location GetLocation(int id)
{
return this.Locations.First(x => x.ID == id);
}
public void SaveUser(User user)
{
this.User.Add(user);
this.SaveChanges();
}
}
Controllers\HomeContoller.cs:
using System.Web.Mvc;
public class HomeController : Controller
{
Repository repo = new Repository();
[HttpGet]
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(User user, int categoryId)
{
user.Category = repo.GetLocation(categoryId);
repo.SaveUser(user);
return View();
}
}
Views\Home\Index.aspx
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage<User>" %>
<html>
<body>
<% using (Html.BeginForm())
{%>
Username: <%: Html.TextBoxFor(model => model.UserName) %><br />
Question: <%: Html.TextBoxFor(model => model.Question) %><br />
Location: <select name="categoryId">
<% foreach (var location in new Repository().GetLocations())
{%>
<option value="<%= location.ID %>">
<%= location.LocationName %></option>
<%} %>
<br />
</select>
<p>
<input type="submit" value="Create" />
</p>
<% } %>
</body>
</html>