Create/update in mvc using EF model not working - c#

I am using an existing sql database in my MVC application. For one of the tables, the create/update functions do not work. I am assuming it is because my application fails to retrieve the auto generated ID's defined in SQL and hence inserts a null value into a non nullable field resulting in the application breaking. So, my question is how do i retrieved the auto generated fields defined in my sql database to show in my MVC5 application. Many thanks to anybody who can assist.
Below is my database table for Customers:
The Model:
public partial class Customer
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Customer()
{
this.Cards = new HashSet<Card>();
this.Stores = new HashSet<Store>();
}
public int CustomerID { get; set; }
public int DiscountLevelID { get; set; }
public int LoyaltyLevelID { get; set; }
public string CustomerCompanyName { get; set; }
public string CustomerName { get; set; }
public string CustomerSurname { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public string CustomerGUID { get; set; }
public int CustomerStatus { get; set; }
public string CustomerAddress { get; set; }
public string CustomerTel { get; set; }
public string CustomerCel { get; set; }
public Nullable<int> CustomerNumber { get; set; }
public string CustomerContact { get; set; }
public string CustomerLogo { get; set; }
public string CustomerLogoPath { get; set; }
public int LastStoreCustomerSyncID { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Card> Cards { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Store> Stores { get; set; }
}
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "CustomerID,DiscountLevelID,LoyaltyLevelID,CustomerCompanyName,CustomerName,CustomerSurname,CustomerGUID,CustomerStatus,CustomerAddress,CustomerTel,CustomerCel,CustomerNumber,CustomerContact,CustomerLogo,CustomerLogoPath,LastStoreCustomerSyncID")] Customer customer)
{
if (ModelState.IsValid)
{
db.Customers.Add(customer);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(customer);
}
// GET: Companies/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Customer customer = db.Customers.Find(id);
if (customer == null)
{
return HttpNotFound();
}
return View(customer);
}
// POST: Companies/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "CustomerID,DiscountLevelID,LoyaltyLevelID,CustomerCompanyName,CustomerName,CustomerSurname,CustomerGUID,CustomerStatus,CustomerAddress,CustomerTel,CustomerCel,CustomerNumber,CustomerContact,CustomerLogo,CustomerLogoPath,LastStoreCustomerSyncID")] Customer customer)
{
if (ModelState.IsValid)
{
db.Entry(customer).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(customer);
}
The View
<div class="form-group">
#Html.LabelFor(model => model.CustomerGUID, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CustomerGUID, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CustomerGUID, "", new { #class = "text-danger" })
</div>
</div>

So, my question is how do i retrieved the auto generated fields
defined in my sql database to show in my MVC5 application.
Follow steps
Right click on the designer surface of the EDMX designer and click Update Model From Database...
All entities are refreshed by default, new entities are only added if you select them.
EDIT: If it is not refreshing well.
Select all the tables and view-s in the EDMX designer.
Delete them.
Then, update model from database
Right click On Model1.tt and select 'Run Custom Tool' save and Build Now see classes are generated.
Right click On Model1.Context.tt and select 'Run Custom Tool' save and Build Now see property IN Context class is generated like
P.S
Read this link also, it is very useful: http://blog.jongallant.com/2012/08/entity-framework-manual-update/

You should pass CustomerID to Controller as parameter. Don't give permission to CustomerID for null values and make it AUTO_INCREMENT
<div class="form-group">
#Html.TextBoxFor(model => model.CustomerID, new {style = 'display:none'})
#Html.LabelFor(model => model.CustomerGUID, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CustomerGUID, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CustomerGUID, "", new { #class = "text-danger" })
</div>
</div>

Related

How to implement custom LogIn with ASP.NET Core 3.1?

I have a testing database on my local server. In the database, I have a table called "Korisnici" (eng. Users). Using EntityFrameworkCore I generated classes from a database, and here is generated "Korisnici" class:
public partial class Korisnici
{
public Korisnici()
{
BankovniRacuni = new HashSet<BankovniRacuni>();
Dokumenti = new HashSet<Dokumenti>();
ObracuniZarada = new HashSet<ObracuniZarada>();
Poslodavci = new HashSet<Poslodavci>();
PrihodiPoslodavca = new HashSet<PrihodiPoslodavca>();
RashodiPoslodavca = new HashSet<RashodiPoslodavca>();
Takse = new HashSet<Takse>();
Zaposleni = new HashSet<Zaposleni>();
}
public int Id { get; set; }
public string Ime { get; set; }
public string Prezime { get; set; }
[Required]
[Display(Name = "Korisnicko ime")]
public string KorisnickoIme { get; set; }
public string Email { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Lozinka")]
public string Lozinka { get; set; }
public int? TipKorisnika { get; set; }
public virtual TipoviKorisnika TipKorisnikaNavigation { get; set; }
public virtual ICollection<BankovniRacuni> BankovniRacuni { get; set; }
public virtual ICollection<Dokumenti> Dokumenti { get; set; }
public virtual ICollection<ObracuniZarada> ObracuniZarada { get; set; }
public virtual ICollection<Poslodavci> Poslodavci { get; set; }
public virtual ICollection<PrihodiPoslodavca> PrihodiPoslodavca { get; set; }
public virtual ICollection<RashodiPoslodavca> RashodiPoslodavca { get; set; }
public virtual ICollection<Takse> Takse { get; set; }
public virtual ICollection<Zaposleni> Zaposleni { get; set; }
}
This class is used as a model for one View called "Index.cshtml":
#model Korisnici
<img src="/Content/images/LogoFinal.png" />
<div class="row">
<div class="col-md-8">
<section id="loginForm">
#using (Html.BeginForm("Login", "Account"))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(m => m.KorisnickoIme, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.TextBoxFor(m => m.KorisnickoIme, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.KorisnickoIme, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => m.Lozinka, new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.PasswordFor(m => m.Lozinka, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Lozinka, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Uloguj se" class="btn btn-primary" />
</div>
</div>
}
</section>
</div>
</div>
When I click on a submit button, Login action from Controler "AccountControler" is called.
public class AccountController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult Login(LoginViewModel model)
{
// Acces database and search for account
var dbContext = new AdministracijaZingDevDBContext();
var korisnik = dbContext.Korisnici
.Where(k => k.KorisnickoIme == model.KorisnickoIme)
.Where(k => k.Lozinka == model.Lozinka)
.FirstOrDefault();
if (korisnik != null)
{
HttpContext.Session.SetString("UserName" , model.KorisnickoIme);
return RedirectToAction("Index", "Main");
}
return RedirectToAction("Index", "Home");
}
}
I have inserted testing data in a database with one record of the Korisnici table.
When I enter correct data into the LogIn form, nothing happens (the user didn't pass login).
just to check, you created migration files and updated the db and so on ?
(This should be in a comment, but I lack the reputation)
also you shouldn't create a new context, but inject it into the constructor of your account controller
( or beter still ... you should inject a repository or better use a unit of work-design and CQRS-design)
Take care and good luck.
so you type ctor tab tab
which would give you
public AccountController(){}
and than you add a parameter in the AccountController-function like
public AccountController(MyContext context){}
right click => quick actions and refactoring => create and assign property MyContext
but you should create at least a repositorypattern and inject something like IKorisniciRepository.
It would be easier if you placed your repo on gitHub so I can test before writing.
Try
public class AccountController : Controller
{
private readonly AdministracijaZingDevDBContext Context {get;}
public AccountController (AdministracijaZingDevDBContext context) {Context = context;}
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult Login(LoginViewModel model)
{
// Access database and search for account
var korisnik = Context.Korisnici
.Where(k => k.KorisnickoIme == model.KorisnickoIme)
.Where(k => k.Lozinka == model.Lozinka)
.FirstOrDefault();
if (korisnik != null)
{
HttpContext.Session.SetString("UserName" , model.KorisnickoIme);
return RedirectToAction("Index", "Main");
}
return RedirectToAction("Index", "Home");
}
}

How to populate a dropdownlist based on Id from a SQL Database

I am trying to create a drop down list in one of my CRUD screens that are based on a different model in my program. Right now it inputs the values based on ID but I want it to be able to populate the names of instructors in a drop-down list.
I've tried a few different things using the select list but it didn't seem to work.
This is my model:
[Table("Section")]
public class Section
{
[Key]
public int Id { get; set; }
[DisplayName("Section")]
public int? section { get; set; }
public Nullable <int> instructor_id { get; set; }
public int location_id { get; set; }
public int modality_id { get; set; }
public int DOW_id { get; set; }
public int course_id { get; set; }
public virtual DOW DOW { get; set; }
public virtual Instructor Instructor { get; set; }
public virtual Location Location { get; set; }
public virtual Modality Modality { get; set; }
public virtual Course Course { get; set; }
This is my controller:
// GET: Sections/Create
public ActionResult Create()
{
return View();
}
// POST: Sections/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,section,startTime,endTime,startDate,endDate,isTap,isActive,instructor_id,location_id,modality_id,DOW_id,course_id")] Section section)
{
if (ModelState.IsValid)
{
db.Sections.Add(section);
db.SaveChanges();
return RedirectToAction("Index");
}
//ViewBag.instruct = new SelectList(db.Instructors, "Id", "lname", section.instructor_id);
return View(section);
}
And this is my view
<div class="form-group">
#Html.LabelFor(model => model.instructor_id, "instructor_id" , htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.instructor_id, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.instructor_id, "", new { #class = "text-danger" })
</div>
</div>
You need to change the Get Method in Controller as follows:
// GET: Sections/Create
public ActionResult Create()
{
ViewBag.Instructors = db.Instructors.ToList();
return View();
}
Your HTML need to be changed as follows:
<div class="form-group">
#Html.LabelFor(model => model.instructor_id, "instructor_id" , htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.instructor_id, new SelectList(ViewBag.Instructors, "Id", "Name"), new { #class = "form-control" } )
#Html.ValidationMessageFor(model => model.instructor_id, "", new { #class = "text-danger" })
</div>
</div>
Make sure field names are correct.
Taken form John Peters answers, kindly take a look
Create a data layer that retrieves a list of what you want. Then use EF to get all the states.
//assuming you have a table of states..
var states = db.States();
The states table should be a Unique list of states.
var selectList = new List<SelectListItem>();
foreach(var thing in states){
//if you got everything, thus the ID field for the value...
selectList.Add(new SelectListItem {Text =thing.State, Selected = false, Value = thing.ID);
}
Make sure in your Viewmodel class that selectlist is a public property.....and set to what you did above. You also need to provided a string for the view selection post back.
StatesSelectList = selectList;
public IEnumerable<SelectListItem> StatesSelectList {get;set;}
public string SelectedState {get;set;}
In your view, do this:
#Html.DropDownListFor(p => Model.SelectedState, Model.StatesSelectList)

Submit CheckBoxList From a single column in MVC .net

I'm working in a HR project in ASP.NET MVC using Entity Framework with a database-first approach.
My view markup is:
<div class="form-group">
#Html.LabelFor(model => model.emp_Disease, htmlAttributes: new { #class = "control-label col-md-2 " })
<div class="col-md-10" style="padding-top:6px;margin:0px;">
#Html.CheckBoxListFor(m => m.emp_Disease,m=> m.Disease,s => s.Name, s => s.Name,s => s.emp_Disease,MvcCheckBoxList.Model.Position.Horizontal)
</div>
</div>
Controller action is:
[HttpPost]
public ActionResult Home(tbl_employee emp)
{
using (hrm_DB db = new hrm_DB())
{
if (emp.emp_Id == 0)
{
db.tbl_employee.Add(emp);
db.SaveChanges();
TempData["SaveMessage"] = "Save Record Succesfully! ";
}
else
{
db.Entry(emp).State = EntityState.Modified;
db.SaveChanges();
TempData["EditMessage"] = "Change Succesfully! ";
}
}
return RedirectToAction("ViewAll");
}
My model classes are:
public partial class tbl_employee
{
[DisplayName("AnyDisease")]
public string[] emp_Disease { get; set; }
public List<Checkdisease> Disease { get; set; }
}
public class Checkdisease
{
public int id { get; set; }
public string Name { get; set; }
public bool emp_Disease { get; set; }
}
Only store the one value. My problem is not submit the array store in database. How to store the string[] in database model ?

ASP.NET MVC, C#, Entity Framework

I am trying to update supplier in database when users on webform insert data into textboxes.
First users in textbox for supplierID insert value, and on screen shows particular supplier from database. Then user can change supplier and when he is done he have to click on submit button.
I use EntityState.Modifier, but supplier doesn't change in database, and also I have no errors in the view. I think that's not working because my Supplier have foreign key from Adress table.
Does somebody know how to update using Entity state modified if a have a foreign key to another table?
I appreciate any help!
public partial class Supplier
{
public int SupplierID{ get; set; }
public string Name{ get; set; }
public string Phone{ get; set; }
public string Email { get; set; }
public Nullable<int> TownID{ get; set; }
public Nullable<int> StreetID{ get; set; }
public Nullable<int> AdressNumber{ get; set; }
public virtual Adress Adress { get; set; }
}
public partial class Town
{
public int TownID{ get; set; }
public string Name{ get; set; }
}
public partial class Street
{
public int TownID{ get; set; }
public int StreetID{ get; set; }
public string Name{ get; set; }
}
public partial class Adress
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Adress()
{
this.Supplier= new HashSet<Supplier>();
}
public int TownID{ get; set; }
public int StreetID{ get; set; }
public int AdressNumber{ get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Supplier> Suppliercs{ get; set; }
}
This is my View:
#model FpisNada.Models.Supplier
#{
ViewBag.Title = "Index";
Layout = null;
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.TextBoxFor(model => model.SupplierID, new { #placeholder = "pib dobavljaca", style = " float:left" })
<div class="col-md-9">
#if (ViewBag.ListTown!= null)
{
#Html.DropDownListFor(m => m.TownID, ViewBag.ListTown as SelectList, "--select town--", new { #class = "form-control", style = " float:left" })
}
#Html.DropDownListFor(m => m.StreetID, new SelectList(""), "--select street--", new { #class = "form-control", style = " float:left" })
<div class="container">
#Html.TextBoxFor(model => model.AdressNumber, new { #class = "form-control"})
#Html.TextBoxFor(model => model.Email, new { #class = "form-control" })
#Html.TextBoxFor(model => model.Name, new { #class = "form-control" })
#Html.TextBoxFor(model => model.Phone, new { #class = "form-control"})
</div>
</div>
<input type="submit" value="Edit" />
}
My controller method:
[HttpGet]
public ActionResult Edit(int id)
{
Supplier supplier= db.Supplier.Find(id);
return View(supplier);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit( Supplier supplier)
{
try
{
if (ModelState.IsValid)
{
db.Entry(supplier).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("ChangeSupplier");
}
}
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name after DataException and add a line here to write a log.)
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
return View(supplier);
}

Proper way to populate a ViewModel?

I'm working on a webapp for work, and I'm using standard CRUD style interactions. However, there are certain fields that I do not want the users updating, so I removed them from the view. However, if I don't explicitly set these fields, they're cleared when the model is updated in the database.
I'm concerned with what the proper method of populating the fields for my ViewModels is.
The rough idea I came up with was something like this:
My view model:
public class EditSoftwareTrackingViewModel
{
public EditSoftwareTrackingViewModel(SoftwareTracking model)
{
Id = model.Id;
SoftwareId = model.SoftwareId;
ComputerId = model.ComputerId;
SoftwareActionId = model.SoftwareActionId;
LastModified = model.LastModified;
Computer = model.Computer;
Software = model.Software;
SoftwareAction = model.SoftwareAction;
}
public int Id { get; set; }
[DisplayName("Software")]
public int SoftwareId { get; set; }
[DisplayName("Computer")]
public int ComputerId { get; set; }
[DisplayName("Software Action")]
public int SoftwareActionId { get; set; }
[DisplayName("Last Modified")]
public DateTime? LastModified { get; set; }
public virtual Computer Computer { get; set; }
public virtual Software Software { get; set; }
public virtual SoftwareAction SoftwareAction { get; set; }
}
My main model
[Table("asset.SoftwareTracking")]
public partial class SoftwareTracking
{
public int Id { get; set; }
[DisplayName("Software")]
public int SoftwareId { get; set; }
[DisplayName("Computer")]
public int ComputerId { get; set; }
[DisplayName("Date Entered")]
public DateTime? EnteredDate { get; set; }
[DisplayName("Software Action")]
public int SoftwareActionId { get; set; }
[DisplayName("Last Modified")]
public DateTime? LastModified { get; set; }
public virtual Computer Computer { get; set; }
public virtual Software Software { get; set; }
public virtual SoftwareAction SoftwareAction { get; set; }
}
And my controller using the view model
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
EditSoftwareTrackingViewModel softwaretracking = new EditSoftwareTrackingViewModel(db.SoftwareTrackings.Find(id));
if (softwaretracking == null)
{
return HttpNotFound();
}
GeneratePageData(softwaretracking.Software.Id);
return View(softwaretracking);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(EditSoftwareTrackingViewModel softwaretracking)
{
if (ModelState.IsValid)
{
softwaretracking.LastModified = DateTime.Now;
var softwareTrack = db.SoftwareTrackings.Find(softwaretracking.Id);
softwareTrack = new SoftwareTracking
{
Computer = softwaretracking.Computer,
ComputerId = softwaretracking.ComputerId,
LastModified = softwaretracking.LastModified,
Software = softwaretracking.Software,
SoftwareAction = softwaretracking.SoftwareAction,
SoftwareActionId = softwaretracking.SoftwareActionId,
SoftwareId = softwaretracking.SoftwareId,
EnteredDate = softwareTrack.EnteredDate
};
db.Entry(softwareTrack).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
GeneratePageData(softwaretracking.Software.Id);
return View(softwaretracking);
}
Is there a better alternative? Or should I continue to create my view models in this manner?
EDIT
My business logic and view
private void GeneratePageData(int? id = null)
{
ViewBag.Computers = new SelectList(db.Computers, "Id", "ComputerName");
ViewBag.SoftwareActions = new SelectList(db.SoftwareActions, "Id", "ActionPerformed");
var usedSoft = (from softTrack in db.SoftwareTrackings
where (softTrack.SoftwareActionId != 3)
select softTrack.Software);
var softwareList = (from soft in db.Softwares
where (
((from softTrack in db.SoftwareTrackings
where (softTrack.SoftwareActionId != 3 && softTrack.SoftwareId == soft.Id)
select softTrack.Software).Count() < soft.KeyQuantity)
&& !(soft.AssetStatusId == 4 || soft.AssetStatusId == 5)
|| soft.Id == id)
select soft).ToList();
ViewBag.SoftwareList = softwareList.Select(t => new SelectListItem
{
Text = t.SoftwareIdNameFull,
Value = t.Id.ToString()
});
}
And my view
#model Lighthouse_Asset_Manager.Models.EditSoftwareTrackingViewModel
#{
ViewBag.Title = "Edit Software Install";
Layout = "";
}
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
×
</button>
<h4 class="modal-title" id="myModalLabel">Edit Software Install</h4>
</div>
<div class="modal-body">
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "computerForm" }))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.Id)
<div class="form-horizontal">
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.SoftwareId, "Software", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("SoftwareId", (IEnumerable<SelectListItem>)ViewBag.SoftwareList, "-- Select --", new
{
#style = "width:100%",
#class = "select2"
})
#Html.ValidationMessageFor(model => model.SoftwareId)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ComputerId, "Computer", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("ComputerId", (IEnumerable<SelectListItem>)ViewBag.Computers, "-- Select --", new
{
#style = "width:100%",
#class = "select2"
})
#Html.ValidationMessageFor(model => model.ComputerId)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.SoftwareActionId, "Action", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("SoftwareActionId", (IEnumerable<SelectListItem>)ViewBag.SoftwareActions, "-- Select --", new
{
#style = "width:100%",
#class = "form-control"
})
#Html.ValidationMessageFor(model => model.SoftwareActionId)
</div>
</div>
<div class="form-actions no-color">
<button type="submit" class="btn btn-primary btn-sm"><i class="fa fa-floppy-o"></i> Edit Install Record</button>
<button type="button" class="btn btn-default" data-dismiss="modal">
Cancel
</button>
</div>
</div>
}
</div>
You approach of using a view model is a good one. The answers to this question explains some of the benefits including preventing over-posting attacks, using view specific display and validation attributes and including view specific properties such as SelectLists. Tools such as automapper can make it easy to map between you data and view models and reduce the code in the controller. A few changes I would suggest to your view model. The LastModified, Computer, Software and SoftwareAction properties are not required (you not binding to these), and I would include the SelectList properties in the model rather than ViewBag
View model
public class EditSoftwareTrackingViewModel
{
public int Id { get; set; }
[Display(Name="Software")]
public int SoftwareId { get; set; }
[Display(Name="Computer")]
public int ComputerId { get; set; }
[Display(Name="Software Action")]
public int SoftwareActionId { get; set; }
public SelectList Computers { get; set; }
public SelectList SoftwareActions{ get; set; }
public SelectList SoftwareList{ get; set; }
}
Then change the GeneratePageData() method to accept the view model
private void GeneratePageData(EditSoftwareTrackingViewModel model)
{
model.Computers = new SelectList(db.Computers, "Id", "ComputerName");
....
and in the view (always preferable to use the strongly typed helpers)
#Html.DropDownListFor(m => m.SoftwareId, Model.SoftwareList, "-- Select --", new { #class = "select2" })
A few other things to note.
You should use the [Display(Name="..")] attribute (not
[DisplayName(..)])
When you set the LastModified property, you should consider using
UCT time.
The hidden input for the Id property is not required in the view
(assuming your using the default {controller}/{action}/{id} route
mapping) - its added to the route values and will be bound anyway
Unless you specifically want an id attribute for the form, you can
just use #using(Html.BeginForm()) {
You do not need the second parameter in LabelFor() - it can be just
Html.LabelFor(m => m.SoftwareId, new { #class = "control-label
col-md-2" }) since you have specified it in the [Display]
attribute
Finally, if you want to simplify your view further, you could consider custom EditorTemplates or html helpers as indicated in this answer which would allow you to replace
<div class="form-group">
#Html.LabelFor(model => model.SoftwareId, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(m => m.SoftwareId, Model.SoftwareList, "-- Select --", new { #class = "select2" })
#Html.ValidationMessageFor(model => model.SoftwareId)
</div>
</div>
with (custom EditorTemplate)
#Html.EditorFor(m => m.SoftwareId, "BootstrapSelect", Model.SoftwareList)
or (custom HtmlHelper)
#Html.BootstrapDropDownFor(m => m.SoftwareId, Model.SoftwareList)
You should use the AutoMapper to make the mapping between Model and ViewModel cleaner. Use this code to create the mapper first.
Mapper.CreateMap<SoftwareTracking, EditSoftwareTrackingViewModel>();
Mapper.CreateMap<EditSoftwareTrackingViewModel, SoftwareTracking>();
When you want to create a viewmodel from model, do this:
public ActionResult Edit(int? id)
{
SoftwareTracking tracking = db.SoftwareTrackings.Find(id);
EditSoftwareTrackingViewModel viewmodel =
Mapper.Map<SoftwareTracking, EditSoftwareTrackingViewModel>(tracking);
return View(viewmodel);
}
When you want to populate the info from the viewmodel back to the model, do this
public ActionResult Edit(EditSoftwareTrackingViewModel vm)
{
if (ModelState.IsValid)
{
vm.LastModified = DateTime.Now;
var softwareTrack = db.SoftwareTrackings.Find(softwaretracking.Id);
softwareTrack =
Mapper.Map<EditSoftwareTrackingViewModel, SoftwareTracking>(vm, softwareTrack);
db.Entry(softwareTrack).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
To patch update your model without loading the object from Db. Try Attach:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(EditSoftwareTrackingViewModel softwaretracking)
{
if (ModelState.IsValid)
{
var softwareTrack = new SoftwareTracking
{
Computer = softwaretracking.Computer,
ComputerId = softwaretracking.ComputerId,
LastModified = softwaretracking.LastModified,
Software = softwaretracking.Software,
SoftwareAction = softwaretracking.SoftwareAction,
SoftwareActionId = softwaretracking.SoftwareActionId,
SoftwareId = softwaretracking.SoftwareId,
EnteredDate = softwareTrack.EnteredDate
};
db.SoftwareTrackings.Attach(softwareTrack);
db.Entry(softwareTrack).Property(a => a.Computer).IsModified = true;
db.Entry(softwareTrack).Property(a => a.ComputerId).IsModified = true;
db.Entry(softwareTrack).Property(a => a.LastModified).IsModified = true;
db.Entry(softwareTrack).Property(a => a.Computer).IsModified = true;
db.Entry(softwareTrack).Property(a => a.Software).IsModified = true;
db.Entry(softwareTrack).Property(a => a.SoftwareAction).IsModified = true;
db.Entry(softwareTrack).Property(a => a.SoftwareActionId).IsModified = true;
db.Entry(softwareTrack).Property(a => a.SoftwareId).IsModified = true;
db.SaveChanges();
return RedirectToAction("Index");
}
GeneratePageData(softwaretracking.Software.Id);
return View(softwaretracking);
}
Regarding the second question about whether to use ViewModel or just use the Model directly. This is really a matter of opinion, each approach has its pros and cons. I don't have strong opinion about this, i just want to point out these pros and cons for your consideration:
Using the model directly saves us from creating the viewModel, resulting in smaller source code and avoiding mapping logic but it would mix concerns. Because you use the same Model for your domain logic and for communcating with the client, any changes to the model may propagate up to the client if we don't take that into account.
Using the viewModel is a good way for separation of concerns but it would require more effort and mapping logic (maybe slow down the performance a bit). To apply ViewModel efficiently, I suggest using a mapper: https://github.com/AutoMapper/AutoMapper/wiki/Getting-started
This is the Model Class
[Table("CURRENCY")]
public class CurrencyClass : ICurrency
{
private Int32 mCURRENCY_ID = default(Int32);
[Key]
public virtual Int32 CURRENCY_ID
{
get { return mCURRENCY_ID; }
set { mCURRENCY_ID = value; }
}
private string mCURRENCY_NAME = default(string);
public virtual string CURRENCY_NAME
{
get { return mCURRENCY_NAME;}
set { mCURRENCY_NAME = value;}
}
private string mCURRENCY_DESC = default(string);
public virtual string CURRENCY_DESC
{
get { return mCURRENCY_DESC; }
set { mCURRENCY_DESC = value; }
}
private string mCURRENCY_SYMBOLE = default(string);
public virtual string CURRENCY_SYMBOLE
{
get { return mCURRENCY_SYMBOLE; }
set { mCURRENCY_SYMBOLE = value; }
}
private Int32 mcreated_by = default(Int32);
public virtual Int32 created_by
{
get { return mcreated_by; }
set { mcreated_by = value; }
}
private DateTime mcreated_date = default(DateTime);
public virtual DateTime created_date
{
get { return mcreated_date; }
set { mcreated_date = value; }
}
private Int32 mmodified_by = default(Int32);
public virtual Int32 modified_by
{
get { return mmodified_by; }
set { mmodified_by = value; }
}
private DateTime mmodified_date = default(DateTime);
public virtual DateTime modified_date
{
get { return mmodified_date; }
set { mmodified_date = value; }
}
}
This is the ViewModel
public class CurrencyViewModel
{
[Key]
public Int32 CURRENCY_Id { get; set; }
[Required(ErrorMessage="Currency Name is required")]
public string CURRENCY_NAME { get; set; }
[Required(ErrorMessage="Currency Description is required")]
public string CURRENCY_DESC { get; set; }
[Required(ErrorMessage = "Currency Symbole is Required")]
public string CURRENCY_SYMBOLE { get; set; }
}
This is the Action
[HttpPost]
[ActionName("Create")]
public ActionResult Create(CurrencyViewModel vm)
{
if (!ModelState.IsValid)
{
return View("Create");
}
obj.CURRENCY_NAME = vm.CURRENCY_NAME;
obj.CURRENCY_DESC = vm.CURRENCY_DESC;
obj.CURRENCY_SYMBOLE = vm.CURRENCY_SYMBOLE;
obj.created_by = 1;
obj.created_date = DateTime.Now;
obj.modified_by = 1;
obj.modified_date = DateTime.Now;
db.Currencies.Add(obj);
db.SaveChanges();
return RedirectToAction("Index");
}

Categories

Resources