I have a question. I am new on ASP.NET MVC Code First.
I am creating a simple CRUD using Scaffold MVC.
My Model:
public class Manufacture
{
[Key]
public int ManufactureID { get; set; }
[Required]
[Column("Manufacture", TypeName = "varchar")]
[MaxLength(25)]
[Display(Name = "Manufacture")]
public string ManufactureCode { get; set; }
[Column("ManufactureDescription", TypeName = "varchar")]
[MaxLength(50)]
[Display(Name = "Description")]
public string ManufactureDescription { get; set; }
[Column("IsActive", TypeName = "bit")]
[Display(Name = "Active?")]
public bool IsActive { get; set; }
public DateTime CreatedDateTime { get; set; }
}
My Controller:
public ActionResult ManufactureCreate()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ManufactureCreate([Bind(Include = "ManufactureID,ManufactureCode,ManufactureDescription,IsActive,CreatedDateTime")] Manufacture manufacture)
{
if (ModelState.IsValid)
{
if (db.Manufactures.Any(ac => ac.ManufactureCode.Equals(manufacture.ManufactureCode)))
{
return View();
}
else
{
db.Manufactures.Add(manufacture);
db.SaveChanges();
return RedirectToAction("ManufactureCreate");
}
}
return View(manufacture);
}
I want to add a value on "CreatedDateTime" field using current DateTime. When User click the "Save" button on the View. "CreatedDateTime" field will be filled by current DateTime.
How can I do that?
Please advise.
Thank you.
manufacture.CreatedDateTime=DateTime.Now;
add above the line
db.Manufactures.Add(manufacture);
Modify your else block as below:
else
{
manufacture.CreatedDateTime=DateTime.Now;
db.Manufactures.Add(manufacture);
db.SaveChanges();
return RedirectToAction("ManufactureCreate");
}
and yes, you should use ViewModel here as Stephen suggested in the comment.
Related
We are developing a REST API and we're allowing all four of the standard verbs. In the case of an POST/PUT
what is better in best practice c# rest api.
this is my Model
public class UserModel
{
public Int64 ID { get; set; }
[Display(Name ="First Name")]
public string FirstName { get; set; }
[Display(Name="Last Name")]
public string LastName { get; set; }
public string Address { get; set; }
[Display(Name="User Name")]
public string UserName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
[Display(Name ="Added Date")]
public DateTime AddedDate { get; set; }
}
Exemple 1
[HttpPost]
public ActionResult CreateEditUser(UserModel model)
{
if (model.ID == 0)
{
User userEntity = new User
{
//....
}
}
}
Exemple 2
[HttpPost]
public ActionResult CreateEditUser(int id,UserModel model)
{
if (id == 0)
{
User userEntity = new User
{
//.....
}
}
}
what is the better Exemple 1 Or Exemple 2
According the REST guidelines (https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md)
PUT: Replace an object, or create a named object, when applicable
POST: Create a new object based on the data provided, or submit a command
PATCH: Apply a partial update to an object
In your case is it better to split the endpoints into a POST and PUT.
[HttpPost]
public ActionResult CreateUser(UserModel model)
{
userService.Create(model);
return...
}
[HttpPut]
public ActionResult EditUser(UserModel model)
{
userService.Update(model);
return...
}
I know there are many tutorials out there but I am not able to understand them as they are just posting their codes with poor explanation plus HttpPostedFileBase is breaking my project as soon i add this to my model class project stops working with following erorr:
Value cannot be null.
Parameter name: entitySet
. I want to add a foreign key to my Movies model for images and create a new model images and add pictures through it.
Movie Model:
public class Movie
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public Genre Genre { get; set; }
[Display(Name = "Genre")]
public int GenreId { get; set; }
[Required]
public DateTime DateAdded { get; set; }
[Required]
[Display(Name = "Release Date")]
public DateTime DateReleased { get; set; }
[Required]
[Display(Name = "Number in Stock")]
[Range(1, 20)]
public int Stock { get; set; }
public int? ImageId { get; set; }
public Image ImageModel { get; set; }
}
Image Model:
public class Image
{
public int Id { get; set; }
[Required]
public string UrlOfImage { get; set; }
}
Movie Save Controller:
public ActionResult Save(Movie movie)
{
if (ModelState.IsValid)
{
if (movie.Id == 0)
{
movie.DateAdded = DateTime.Now;
_context.Movies.Add(movie);
}
else
{
var movieInDb = _context.Movies.Single(m => m.Id == movie.Id);
movieInDb.Name = movie.Name;
movieInDb.GenreId = movie.GenreId;
movieInDb.Stock = movie.Stock;
movieInDb.DateReleased = movie.DateReleased;
}
_context.SaveChanges();
return RedirectToAction("Index", "Movies");
}
var viewModel = new NewMovieViewModel(movie)
{
Genres = _context.Genres.ToList()
};
ModelState.Clear();
return View("MovieForm", viewModel);
}
NewMovieViewModel
public class NewMovieViewModel
{
public IEnumerable<Genre> Genres { get; set; }
public int? Id { get; set; }
[Required]
[StringLength(255)]
public string Name { get; set; }
[Display(Name = "Genre")]
[Required]
public int? GenreId { get; set; }
[Display(Name = "Release Date")]
[Required]
public DateTime? DateReleased { get; set; }
[Display(Name = "Number in Stock")]
[Range(1, 20)]
[Required]
public int? Stock { get; set; }
public int? ImageId { get; set; }
public Image Image { get; set; }
public string Title
{
get
{
return Id != 0 ? "Edit Movie" : "New Movie";
}
}
public NewMovieViewModel()
{
Id = 0;
}
public NewMovieViewModel(Movie movie)
{
Id = movie.Id;
Name = movie.Name;
DateReleased = movie.DateReleased;
Stock = movie.Stock;
GenreId = movie.GenreId;
ImageId = movie.ImageId;
}
}
You can try this out.
Your view should look like this:
#using(Html.BeginForm("UploadFile","Upload", FormMethod.Post, new {
enctype="multipart/form-data"}))
{
<div>
#Html.TextBox("file", "", new { type= "file"}) <br />
<input type="submit" value="Upload" />
#ViewBag.Message
</div>
}
Your controller should look like this:
[HttpPost]
publicActionResultUploadFile(HttpPostedFileBase file)
{
try
{
if (file.ContentLength > 0)
{
string _FileName = Path.GetFileName(file.FileName);
string _path = Path.Combine(Server.MapPath("~/UploadedFiles"), _FileName);
file.SaveAs(_path);
}
ViewBag.Message = "File Uploaded Successfully!!";
return View();
}
catch
{
ViewBag.Message = "File upload failed!!";
return View();
}
}
The idea would be to save your blob data separately in some blob storage and the call should be an async. It will be completely separate call and maybe for a particular blob container.
Whether your page is Admin or User facing it doesn't matter. to provide good user experience you either would want to upload the image as soon as a user selects an image in a container and get back the URL, which you would be saving in the metadata table.
Or you could allow first saving all data and after that directing user to update/add an image in the record.
You also need to think through how you would be saving data (normal data and blob data) in bulk. and what would be the better approach to do these same operations in case of mobile devices? by the way, nowadays we should always think from mobile client perspective first.
Hope this would be helpful.
You should define parameter HttpPostedFileBase in your method
public Action Save(Movie movie, HttpPostedFileBase image)
And in your view you should have an input with name "image"
<input type="file" name="image" />
Upon my post ActionResult Edit, I am receiving an error. System.Web.Mvc.WebViewPage<TModel>.Model.get returned null
My controller:
[HttpPost]
public ActionResult Edit(editRequestViewModel _editRequestViewModel, int id)
{
try
{
if (ModelState.IsValid)
{
using (var db = new HelpDeskContext())
{
db.Entry(_editRequestViewModel.userRequest).State = System.Data.Entity.EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Digest",new { id = _editRequestViewModel.userRequest.ID });
}
}
else
return View();
}
catch (Exception ex)
{
return View("Error", new HandleErrorInfo(ex, "Change", "Browse"));
}
}
My View includes this for the models field to bind:
#Html.DropDownListFor(model => model.userRequest.forApplication, Model.Applications, "Select Application", new { #class = "form-control" })
My Model has the field as nullable int?:
public int? forApplication { get; set; }
It seems to update the other fields in the model with this field just fine on POST. When the request is first created and saved to the DB, it saves fine in that field when its null. It seems to me that nullable should be OK as a value when its posting (Edit ActionResult)?
EDIT: This is my GET Method that populates the View Model which is passed in to the POST.
public ActionResult Edit(int id)
{
try
{
if (ModelState.IsValid)
{
using (var db = new HelpDeskContext())
{
var query = (from m in db.Requests
where m.ID == id
select new editRequestViewModel()
{
Applications = (from r in db.Applications
select new SelectListItem(){
Text = r.Name,
Value = r.ID.ToString()
}).ToList(),
closeReasons = (from r in db.CloseReasons
select new SelectListItem()
{
Text = r.Name,
Value = r.ID.ToString()
}).ToList(),
userRequest = m
}).FirstOrDefault();
return View(query);
}
}
else
return View();
}
catch (Exception ex)
{
return View("Error", new HandleErrorInfo(ex, "Change", "Browse"));
}
}
And my View has #model HelpDeskSolution.ViewModels.editRequestViewModel
EDIT 2: ViewModel and Model
namespace HelpDeskSolution.Models
{
public class Request
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity), Key()]
public int ID { get; set; }
[Required]
[StringLength(99, MinimumLength = 3)]
public string Title { get; set; }
[StringLength(1000, MinimumLength = 1)]
[Required]
public string Description { get; set; }
[Required]
[Display(Name = "Submit Date")]
public DateTime SubmitDate { get; set; }
public DateTime? CloseDate { get; set; }
[Required]
[StringLength(30)]
public string Author { get; set; }
[Required]
[StringLength(30)]
public string AuthDept { get; set; }
[StringLength(30)]
[Display(Prompt = "Text at top of Epicor Screen...(Optional)")]
public string Module { get; set; }
public int Urgency { get; set; }
[StringLength(30)]
public string Type { get; set; }
public int Status { get; set; }
[StringLength(30)]
[Display(Name = "Request For")]
public string RequestFor { get; set; }
[Required]
public bool Closed { get; set; }
[StringLength(30)]
[Display(Name = "Assign To")]
public string AssignedTo { get; set; }
[Display(Name = "Application")]
public int? forApplication { get; set; }
public int? closeReason { get; set; }
public string ClosedBy { get; set; }
[Display(Name = "ID")]
public int? duplicateOf { get; set; }
}
}
Model:
namespace HelpDeskSolution.ViewModels
{
public class editRequestViewModel
{
public Request userRequest { get; set; }
public List<SelectListItem> Applications { get; set; }
public List<SelectListItem> closeReasons { get; set; }
}
}
Ended up solving this with the direction of #StephenMuecke. The reason I was getting the exception is because upon the return View() in the else portion of the post action, it was attempting to return the view without Applications list, as Stephen said. However that led me to realize that there was first a problem with the Model State, hence why it was even going to the else in the first place. I had another field that was being passed null when it wasnt a nullable type.
I simply changed the type to int? and added a migration and the Action result is A'Okay now.
I am using autocomplete from JQueryUIHelpers in my Asp.Net MVC project with EF6.
Model structure:
public class Employee
{
[Key]
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string SecondName { get; set; }
[NotMapped]
public string FullName => FirstName + " " + SecondName;
public bool IsDriver { get; set; } = false;
public virtual ICollection<Delivery> Deliveries { get; set; }
}
public class Delivery
{
[Key]
public int Id { get; set; }
[Required]
public Employee Driver { get; set; }
public virtual ICollection<EggsMag> Eggs { get; set; }
}
EmployeeController:
public ActionResult Drivers(string term)
{
var drivers = _rep.GetAll(e => e.IsDriver && (e.FirstName.StartsWith(term) || e.SecondName.StartsWith(term)));
return Json((from d in drivers select new { label = d.FullName, value = d.Id }).ToList(), JsonRequestBehavior.AllowGet);
}
DeliveriesController:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "ID,DateOfDelivery,Driver")] Delivery delivery)
{
if (ModelState.IsValid)
{
_rep.Save(delivery);
return RedirectToAction("Index");
}
return View(delivery);
}
View:
#Html.JQueryUI().AutocompleteFor(m => m.Driver.Id, Url.Action("Drivers", "Employees"), "DriverId", null)
Problem Description:
Autocomplete is working correctly but when in Edit view I send POST request I receive all the data, but ModelState.IsValid is false.
The error shows that fields of FirstName and SecondName are empty which is true because I sent just Id of existing Driver, not whole object.
Is there a way to fix it?
Maybe some way to change validation to not check inner model(Driver) fields except Driver.Id existence.
I'm kinda newbie with asp.net MVC environment
i'm trying to delete ROW from 3 table/entity(cause i'm using ado.net entity data model to generate the database automatically) the problem is when my delete function is executed only ROW from 1 table is deleted..
PS: i've also already create the relationship between 3 table
here is my controller:
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
// i've edited this code, i think the problem lies in this code bellow
// start edited code
ms_student ms_student = db.ms_student
.Include(i => i.ms_person)
.Include(i => i.ms_person.ms_user)
.Where(i => i.user_id_student == id)
.Single();
// end edited code
db.ms_student.Remove(ms_student);
db.SaveChanges();
return RedirectToAction("Index");
}
this is my ms_user model:
namespace test.Models
{
using System;
using System.Collections.Generic;
public partial class ms_user
{
public int ID { get; set; }
public string password { get; set; }
public string salt { get; set; }
public Nullable<int> administrative_type_id { get; set; }
public string email_login { get; set; }
public virtual ms_person ms_person { get; set; }
}
}
this is ms_person model:
namespace test.Models
{
using System;
using System.Collections.Generic;
public partial class ms_person
{
public int ID { get; set; }
public Nullable<int> family_id { get; set; }
public string first_name { get; set; }
public string last_name { get; set; }
public string email { get; set; }
public string address { get; set; }
public string phone_address { get; set; }
public string mobile_phone_number { get; set; }
public string gender { get; set; }
public Nullable<System.DateTime> date_of_bith { get; set; }
public Nullable<System.DateTime> date_start { get; set; }
public Nullable<System.DateTime> date_end { get; set; }
public string status { get; set; }
public string identification_ID { get; set; }
public string passport { get; set; }
public Nullable<int> user_type_id { get; set; }
public string file_image { get; set; }
public virtual ms_student ms_student { get; set; }
public virtual ms_user ms_user { get; set; }
}
}
Lastly my ms_person model:
namespace test.Models
{
using System;
using System.Collections.Generic;
public partial class ms_student
{
public int user_id_student { get; set; }
public int student_code { get; set; }
public int course_id { get; set; }
public string degree { get; set; }
public Nullable<int> current_semester { get; set; }
public string cuti_session { get; set; }
public virtual ms_person ms_person { get; set; }
}
}
just you know that the model code is auto generated, i'm using ado.net entity model and the only table/entity that deleted are only the ms_student table(sorry, i'm kinda confused with naming: model or entity or table)
the ID on ms_person are auto increment PK
the ID on ms_user actually the FK also the PK of ms_person
and (foolishly of me for the different naming) user_id_student actually the FK also the PK of ms_person
thank you very much
I think i just found some solution,
i change my delete controller with this:
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
// i change this piece of code, not really sure what it means though
// very appreciate if somebody can describe it in more humanly or sql language
ms_person ms_person = db.ms_person
.Include(i => i.ms_student)
.Include(i => i.ms_user)
.Where(i => i.ID == id)
.Single();
db.ms_person.Remove(ms_person);
db.SaveChanges();
return RedirectToAction("Index");
}
with this all the record from 3 table is deleted, but i'm not sure this is the best solution, because as you can see i just modified the Include() statement, and i'm not really sure this method can be used on other controller as well...
thank you very much, fell free if somebody can put a better solution for my problem.. :D
You want to also remove the ms_person and ms_user?
Do this.
db.ms_student.Remove(ms_student);
if (ms_student.ms_person != null)
{
db.ms_person.Remove(ms_student.ms_person);
if (ms_student.ms_person.ms_user != null)
{
db.ms_user.Remove(ms_student.ms_person.ms_user);
}
}
db.SaveChanges();
[HttpGet]
public ActionResult DeleteFacilitator( Int64 FacID)
{
if(FacID == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var Resul= db.NFE_Facilitator.Find(ID);
if(Resul==null)
{
return HttpNotFound();
}
return View(Resul);
}
[HttpPost,ActionName("DeleteFacilitator")]
public ActionResult DeleteConfirmed( Int64 ID)
{
var Resul= db.NFE_Facilitator.Find(ID);
db.NFE_Facilitator.Remove(Resul);
db.SaveChanges();
return RedirectToAction("SearchFacilitator");
}