I am trying to save changes to in a basic CRUD. I have edited my view for 3 columns in my model (table has 7 columns).
I tried attach method which was referenced in a different post which did not work. Any thoughts would be appreciated.
Model
public class AssetRequest
{
public int Id { get; set; }
[DataType(DataType.Date)]
[Display(Name = "Request date")]
public DateTime AssetRequestDate { get; set; }
[Display(Name = "Facility")]
public int FacilityId { get; set; }
[Required]
[Display(Name = "Asset requested")]
public int AssetId { get; set; }
[Display(Name ="Serial no.")]
public string AssetSN { get; set; }
[Required]
[Display(Name = "Request type")]
public int RequestTypeId { get; set; }
[Required]
[DataType(DataType.Date)]
[Display(Name = "Job date")]
public DateTime JobRequestDate { get; set; }
[Required]
[Display(Name = "Request status")]
public int RequestStatusId { get; set; }
[Display(Name = "Tracking no.")]
public string TrackingNo { get; set; }
[Display(Name = "Comments")]
public string Comments { get; set; }
[Display(Name = "Sending facility")]
public string SendingFacilityt { get; set; }
public virtual Asset Asset { get; set; }
public virtual Facility Facility { get; set; }
public virtual ApplicationUser User { get; set; }
public virtual RequestType RequestType { get; set; }
public virtual RequestStatus RequestStatus { get; set; }
}
}
Controller
public async Task<ActionResult> Edit([Bind(Include = "RequestStatusId, TrackingNo, Comments")] AssetRequest assetRequest)
{
if (ModelState.IsValid)
{
//db.AssetRequestTable.Attach(assetRequest);
db.Entry(assetRequest).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("All");
}
}
View
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>AssetRequest</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Id)
<div class="form-group">
#Html.LabelFor(model => model.DistrictId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("DistrictId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.DistrictId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.AssetId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("AssetId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.AssetId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.RequestStatusId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("RequestStatusId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.RequestStatusId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.TrackingNo, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.TrackingNo, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.TrackingNo, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Comments, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Comments, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Comments, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "All")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
You ned to include the Id property (which is the primary key) to the Include list so that EF can get the item and update it.
public async Task<ActionResult> Edit([Bind(Include = "Id,RequestStatusId, TrackingNo,
Comments")] AssetRequest assetRequest)
{
// your code
}
Looks like you are using your entity model as the parameter to update the entity values. A better approach to prevent over posting is to use a view model.
Related
I am a solo and very beginner learner. I am trying to create a simple code first app with a database using EF6. I cannot understand how to insert the data of a entity inside another by the frontend.
I have two entities:
public class Movie
{
[Key]
public int Id { get; set; }
public string Title{ get; set; }
public int ActorId { get; set; }
public ICollection<Actor> Actors { get; set; }
}
public class Actor
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey("MovieId")]
public ICollection<Movie> Movies { get; set; }
}
The controller.
public ActionResult AddMovie()
{
var actorsList = (from Name in ctx.Attors select Name).ToList();
ViewBag.Actors = new SelectList(actorsList, "Name", "Name");
return View(new Film());
}
[HttpPost]
public ActionResult PerformAddMovie(Movie m)
{
try
{
ctx.Movies.Add(m);
ctx.SaveChanges();
return RedirectToAction("Index", "Home");
}
catch(Exception ex)
{
ModelState.AddModelError("", ex.Message);
}
return RedirectToAction("Index", "Home");
}
#model Cinema.Models.Movie
#{
ViewBag.Title = "AddMovie";
}
<h2>AddFilm</h2>
#{
var list = ViewBag.Actors as SelectList;
}
#using (Html.BeginForm("PerformAddMovie", "Movie", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Film</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Title, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Title, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ActorId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ActorId, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ActorId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Actors, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(m => m.Actors, list, "---Select---", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.Actors, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
After adding some movies into the database by the frontend web page, in the addmovie web page I can select one of them by the dropdown list, but when I save the movie nothing happens inside the third table created with movieid and actorid, it is always empty.
What am I doing wrong?
The Model is wrong
public class Movie
{
[Key]
public int Id { get; set; }
public string Title{ get; set; }
public int ActorId { get; set; }
public virtual Actor Actor { get; set; } // It should be one to one relationship
}
public class Actor
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
//[ForeignKey("MovieId")] This is unneccessary
public ICollection<Movie> Movies { get; set; }
}
Then u can select the Actor Id as key while display actor name in the select list
ViewBag.Actors = new SelectList((from s in db.Actor
select new {Id = s.Id, Name = s.Name }),
"Id", "Name");
Remove this under your html as the Id is attached to the dropdown list
<div class="form-group">
#Html.LabelFor(model => model.ActorId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ActorId, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ActorId, "", new { #class = "text-danger" })
</div>
</div>
change the dropdownlist to this
<div class="form-group">
#Html.LabelFor(model => model.ActorId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(m => m.ActorId, (Selectlist)ViewBag.Actor, "---Select---", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.ActorId, "", new { #class = "text-danger" })
</div>
</div>
I'm just a beginner in MVC, so please help me out and bear with my question. I have a scenario, but I can't figure it out how to create a proper ViewModel for this. I have a ProjectViewModel which consists of 4 tables related to one another.
So far my this is my ProjectViewModel.
public class ProjectViewModel
{
public int? ProjectId { get; set; }
public string ProjectName { get; set; }
public string ProjectLocation { get; set; }
public string ProjectDescription { get; set; }
public double? WorkArea { get; set; }
public string ModeOfPayment { get; set; }
public string Duration { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public double? TotalDirectCost { get; set; }
public double? ProfitSupervision { get; set; }
public double? TotalProjectCost { get; set; }
public List<ScopeOfWork> ScopeOfWork { get; set; }
}
public class ScopeOfWork
{
public int? ScopeOfWorkId { get; set; }
public string ScopeOfWorkName { get; set; }
public List<Materials> Materials { get; set; }
public int? ProjectId { get; set; }
}
public class Materials
{
public string MaterialName { get; set; }
public int? Quantity { get; set; }
public double? Cost { get; set; }
public int? ScopeOfWorkId { get; set; }
//This should be a select list
public IEnumerable<SelectListItem> Category { get; set;
}
Supposed to be a Project can have many ScopeOfWork object and the ScopeOfWork object can have many material object. And I will fill the category property of list of categories in the controller.
I just tried to create a view for List ScopeOfWork Object to see if the ScopeOfWork object will show but didn't show in the view.
Controller:
[HttpGet]
public ActionResult _CreateProject()
{
return View();
}
//POST: Create Project
[HttpPost]
public ActionResult _CreateProject(ProjectViewModel project)
{
return View(project);
}
View:
#model MigratingDB.Models.ProjectViewModel
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>ProjectViewModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.ProjectId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ProjectId, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ProjectId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ProjectName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ProjectName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ProjectName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ProjectLocation, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ProjectLocation, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ProjectLocation, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ProjectDescription, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ProjectDescription, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ProjectDescription, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.WorkArea, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.WorkArea, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.WorkArea, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ModeOfPayment, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ModeOfPayment, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ModeOfPayment, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Duration, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Duration, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Duration, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.StartDate, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.StartDate, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.StartDate, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.EndDate, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.EndDate, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.EndDate, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.TotalDirectCost, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.TotalDirectCost, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.TotalDirectCost, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ProfitSupervision, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ProfitSupervision, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ProfitSupervision, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.TotalProjectCost, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.TotalProjectCost, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.TotalProjectCost, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ProjectStatus, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ProjectStatus, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ProjectStatus, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Kindly give suggestions to setup a correct ViewModel for this and that will allow me to create project with multiple ScopeOfWork and its corresponding materials for each ScopeOfWork created.
UPDATED: I separated the classes in each ViewModel
ProjectViewModel
public class ProjectViewModel
{
//tbl Project
public int? ProjectId { get; set; }
public string ProjectName { get; set; }
public string ProjectLocation { get; set; }
public string ProjectDescription { get; set; }
public double? WorkArea { get; set; }
public string ModeOfPayment { get; set; }
public string Duration { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public double? TotalDirectCost { get; set; }
public double? ProfitSupervision { get; set; }
public double? TotalProjectCost { get; set; }
//SelectList
public int? ProjectStatus { get; set; }
//SelectList
public int? ForemanId { get; set; }
//SelectList
public int? ClientId { get; set; }
//tbl ScopeOfWork
public List<ScopeOfWorkViewModel> ScopeOfWork { get; set; }
}
ScopeOfWorkViewModel
public class ScopeOfWorkViewModel
{
public int? ScopeOfWorkId { get; set; }
public string ScopeOfWorkName { get; set; }
public List<MaterialsViewModel> Materials { get; set; }
public int? ProjectId { get; set; }
}
MaterialsViewModel
public class MaterialsViewModel
{
public string MaterialName { get; set; }
public int? Quantity { get; set; }
public double? Cost { get; set; }
public int? ScopeOfWorkId { get; set; }
public IEnumerable<SelectListItem> Category { get; set; }
}
Here is my model:
public class Auction
{
[Key]
[Required]
public long Id { get; internal set; }
[Required]
[Display(Name = "Title")]
public string Title { get; set; }
[Required]
[Display(Name = "Description")]
public string Description { get; set; }
[Required]
[Display(Name = "Product Name")]
public string productName { get; set; }
[Required]
[Display(Name = "Product Price")]
public string productPrice { get; set; }
[Required]
[Display(Name = "Auction Start Time")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy MM, dd}", ApplyFormatInEditMode = true)]
public DateTime StartTime { get; set; }
[Required]
[Display(Name = "Auction End Time")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime EndTime { get; set; }
[Required]
[DataType(DataType.Currency)]
[Display(Name = "Auction Start Price")]
public decimal StartPrice { get; set; }
[DataType(DataType.Currency)]
[Display(Name = "Auction Current Price")]
public decimal? CurrentPrice { get; set; }
}
}
Here is my DataContext class:
public class AuctionsDataContext : DbContext
{
public DbSet<Auction> Auctions { get; set; }
static AuctionsDataContext()
{
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<AuctionsDataContext>());
}
}
Here is my View :
#model Mohn.Models.Auction
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Auction</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Id)
<div class="form-group">
#Html.LabelFor(model => model.Title, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Title, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Description, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Description, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.productName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.productName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.productName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.productPrice, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.productPrice, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.productPrice, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.StartTime, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.StartTime, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.StartTime, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.EndTime, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.EndTime, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.EndTime, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.StartPrice, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.StartPrice, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.StartPrice, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CurrentPrice, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CurrentPrice, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CurrentPrice, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
And here is my Edit part that does not worked in the controller:
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Auction auction = db.Auctions.Find(id);
if (auction == null)
{
return HttpNotFound();
}
return View(auction);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "Id,Title,Description,productName,productPrice,StartTime,EndTime,StartPrice,CurrentPrice")] Auction auction)
{
if (ModelState.IsValid)
{
db.Entry(auction).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(auction);
}
The problem is, when I pass my Auction id in the URL, then it will retrieve data to the view except start time. And I cannot submit the details, it will cause an exception.
Start time will not display on my view,
I am showing a screenshot here:
enter image description here
Even if I submit the start date with given details on my View, it will throw an exception
The screenshot shows it:
enter image description here
An exception of type 'System.Data.Entity.Infrastructure.DbUpdateConcurrencyException' occurred in EntityFramework.dll but was not handled in user code
Additional information: Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted
Please help me to solve this, and I am new to Asp.net MVC 5, Entity Framework, I want to know what exactly happen...
Thanks your attention....and help..:)
I guess the problem is in the ID...when I debug the program it is passed 0 to the controller...How to change this?...
You need to remove internal for setter method id property. Due to internal model binder was unable to set value to it
[Key]
[Required]
public long Id { get; internal set; }
Change it to
[Key]
[Required]
public long Id { get; set; }
First of all your Start Time format annotation is "{0:yyyy MM, dd}" that seems "{0:yyyy-MM-dd}" is true. after that because of your [DataType(DataType.Currency)] annotation and problem to transfer this field data, this exception has occured.
I have no idea why I am getting this exception. I am trying to implement a simple upload image functionality.
I am getting the error when I am trying to save the image along with the rest of the data.
I suspect the the problem is in the view, because I used this code from my model and action method in another project and the code worked.
Can someone help with this. I believe I am close.
Model :
public class Company
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CompanyId { get; set; }
public byte[] ImageData { get; set; }
[NotMapped]
public HttpPostedFileBase UploadImage { get; set; }
[NotMapped]
public string ImageBase64 => System.Convert.ToBase64String(ImageData);
public string CompanyName { get; set; }
public string CompanyAddress { get; set; }
public string CompanyCountry { get; set; }
public string CompanyCity { get; set; }
public string CompanyPostalCode { get; set; }
public string CompanyPhoneNumber { get; set; }
public string CAId { get; set; }
}
Controller :
public ActionResult Create([Bind(Include = "CompId,ImageData,CompanyName,CompanyAddress,CompanyCountry,CompanyCity,CompanyPostalCode,CompanyPhoneNumber,EmailCA")] Company company, HttpPostedFileBase UploadImage)
{
if (ModelState.IsValid)
{
byte[] buf = new byte[UploadImage.ContentLength];
UploadImage.InputStream.Read(buf, 0, buf.Length);
company.ImageData = buf;
db.Companies.Add(company);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(company);
}
View :
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Company</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.ImageData, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<div class="col-md-10">
#Html.TextBoxFor(model => model.ImageData, new { type = "file" })
#*<input type="file" name="ImageData" class="input-files" />*#
</div>
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CompanyName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CompanyName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CompanyName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CAId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CAId, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CAId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
In your view your using #Html.TextBoxFor(model => model.ImageData, new { type = "file" }) where I think you should use #Html.TextBoxFor(model => model.UploadImage, new { type = "file" }) instead.
And in order to make that pass I think you need to add the UploadImage property to the Bind(Include) string as following:
public ActionResult Create([Bind(Include = "CompId,ImageData,CompanyName,CompanyAddress,CompanyCountry,CompanyCity,CompanyPostalCode,CompanyPhoneNumber,EmailCA,UploadImage")] Company company)
I have the following table relationships:
ProfileMeta 1 ----- 0...1 ProfileDetail
I'm getting a run-time error, after I click on submit on the Profile/Create page
Cannot insert the value NULL into column 'ID', table 'ContosoUniversity1.dbo.ProfileMeta'; column does not allow nulls. INSERT fails.
I've properly referenced ProfileMeta as a ForeignKey in Models/ProfileDetail.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Web;
namespace ContosoUniversity.Models
{
//ProfileMeta is Principal Class
//ProfileDetail is Dependent Class
public class ProfileDetail
{
//classNameID or ID is interpreted by EF as PK.
public int ID { get; set; }
//ForeignKey("<Navigation Property Name>")
[Key, ForeignKey("ProfileMeta")]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.None)]
public int ProfileMetaID {get; set;}
public string UserName { get; set; }
public string Age { get; set; }
public string Location { get; set; }
public string Gender { get; set; }
//Optional Details
public string HighSchool { get; set; }
public string UndergraduateSchool { get; set; }
public string GraduateSchool { get; set; }
public virtual ProfileMeta ProfileMeta { get; set; }
}
}
Models/ProfileMeta.cs:
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Web;
namespace ContosoUniversity.Models
{
//ProfileMeta is Principal
//ProfileDetail is Dependent
public class ProfileMeta
{
public int ID { get; set; }
public string Username { get; set; }
public string password { get; set; }
public virtual ProfileDetail ProfileDetail {get; set;}
public virtual ICollection<MessageDetail> MessageDetails { get; set; }
public virtual ICollection<ConversationMeta> ConversationMetas { get; set; }
}
}
For a functional signup/register page, I have created a Model: "Register", that references ProfileMeta and ProfileDetail.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace ContosoUniversity.Models
{
public class Register
{
public ProfileMeta ProfileMeta_ { get; set; }
public ProfileDetail ProfileDetail_ { get; set; }
}
}
Views/Profile/Create.cshtml:
#model ContosoUniversity.Models.Register
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Profile</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.ProfileMeta_.Username, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ProfileMeta_.Username, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ProfileMeta_.Username, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ProfileMeta_.password, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ProfileMeta_.password, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ProfileMeta_.password, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ProfileDetail_.Age, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ProfileDetail_.Age, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ProfileDetail_.Age, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ProfileDetail_.Location, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ProfileDetail_.Location, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ProfileDetail_.Location, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ProfileDetail_.Gender, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ProfileDetail_.Gender, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ProfileDetail_.Gender, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ProfileDetail_.HighSchool, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ProfileDetail_.HighSchool, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ProfileDetail_.HighSchool, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ProfileDetail_.UndergraduateSchool, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ProfileDetail_.UndergraduateSchool, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ProfileDetail_.UndergraduateSchool, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ProfileDetail_.GraduateSchool, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ProfileDetail_.GraduateSchool, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ProfileDetail_.GraduateSchool, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
ProfileController.cs:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Register register)
{
if (ModelState.IsValid)
{
//Add 1 ProfileMeta row and 1 linked ProfileDetail row
ProfileMeta profileMeta = new ProfileMeta();
profileMeta.Username = register.ProfileMeta_.Username;
profileMeta.password = register.ProfileMeta_.password;
ProfileDetail profileDetail = new ProfileDetail();
//profileDetail.ID = register.ProfileDetail_.ID;
//How to assign FK below?
profileDetail.ProfileMetaID = register.ProfileDetail_.ID;
profileDetail.UserName = register.ProfileDetail_.UserName;
profileDetail.Age = register.ProfileDetail_.Age;
profileDetail.Location = register.ProfileDetail_.Location;
profileDetail.ProfileMeta = profileMeta;
//profileDetail.UserName = register.ProfileDetail_.UserName;
//profileDetail.Age = register.ProfileDetail_.Age;
//profileDetail.Location = register.ProfileDetail_.Location;
profileMeta.ProfileDetail = profileDetail;
db.ProfileMetas.Add(profileMeta);
db.ProfileDetails.Add(profileDetail);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(register);
}
Why can't I add a row of ProfileMeta and a corresponding row of ProfileDetail? I thought the database is automatically generating Primary Key (or ID)'s.
Is it necessary to explicitly set the Navigation Properties for a given Model object in the controller?
Also, do I need to explicitly set the Foreign Key: "ProfileMetaID" in the ProfileDetail object I had created?
First thing, I would like to suggest to use EF code conventions instead of explicitly defining the relationships in EF (Your declaration is correct , it's just a personal preference for me and i think it's cleaner).
So instead of this:
//ForeignKey("<Navigation Property Name>")
[Key, ForeignKey("ProfileMeta")]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.None)]
public int ProfileMetaID {get; set;}
public virtual ProfileMeta ProfileMeta { get; set; }
You can do:
public int ProfileMetaID { get; set; }
public virtual ProfileMeta profileMeta { get; set; }
EF will create the FK automatically for you in the DB.
As for your question , I highly recommend that you use ViewModel and include all the attributes you want work with. but if you want to use your existing models , You can use ProfileMeta as your base model and bind the values of ProfileDetail using model.profileDetail.Age
here's an example:
Your models (Short version)
public class ProfileDetail
{
[Key, DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string UserName { get; set; }
public string Age { get; set; }
public string Location { get; set; }
public string Gender { get; set; }
public int ProfileMetaID { get; set; }
public virtual ProfileMeta profileMeta { get; set; }
}
public class ProfileMeta
{
[Key, DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string Username { get; set; }
public string password { get; set; }
public virtual ProfileDetail profileDetail {get; set;}
}
Your controller:
(No need to explicitly set the values of your attributes since the relationship and EF will take care of it.)
[HttpGet]
public ActionResult Create()
{
return View(new ProfileMeta());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Register register)
{
if (ModelState.IsValid)
{
db.ProfileMetas.Add(profileMeta);
db.ProfileDetails.Add(profileDetail);
db.SaveChanges();
}
}
In your View (Just a snapshot)
<div class="form-group">
#Html.LabelFor(model => model.Username, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Username, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Username, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.password, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.password, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.password, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.profileDetail.Age, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.profileDetail.Age, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.profileDetail.Age, "", new { #class = "text-danger" })
</div>
</div>
And finally here's a fiddle to illustrate the whole concept : https://dotnetfiddle.net/N0prMA
In the fiddle you will see that if you provide an age it will submit it back and display it in the page.