I'm trying to add a new item to an existent list in my MVC application, my problem that's I don't know the correct approach to do it.
I've done this so far:
My Customer code:
Controller
public class CustomerController : Controller
{
public CustomerBusiness customerBusiness { get; set; }
public CustomerController()
{
customerBusiness = new CustomerBusiness();
}
//Some code that makes CRUD and more these methods
[HttpGet]
public ActionResult ViewAllJobOfferts(int id)
{
var cust = customerBusiness.GetById(id);
return View(cust.JobOfferts);
}
public ActionResult CreateJobOffert(int id)
{
var cust = customerBusiness.GetById(id);
return View(cust);
}
/* [HttpPost]
public ActionResult CreateJobOffert(JobOffertModel jobOffert)
{
return View();
}*/
}
I have a relationship 1 to n between my entities Customer and JobOffert, and the method ViewAllJobOfferts works fine, but I got stuck when I try to add a new JobOffert.
I have a couple of questions, here we go:
I must to create a controller special to JobOfferts, or control
inside the CustomerController?
When I try to create the view that will submit the form to new
JobOffert I didn't know how to link the customer to this new
JobOffert, f I try create a page using customer model, I do not have the JobOffert attributes and if I create using the JobOffert model, I dont know how make the link between these two objects. how must I do this?
PS.: Here the code of both models:
JobOffert Model
Customer Model
I must to create a controller special to JobOfferts, or control inside
the CustomerController?
Not necessarily, controller like other class should follow SRP (Single Responsibility Principle). In this case as long as CustomerController facilitating information related to Customer, it's completely fine.
When I try to create the view that will submit the form to new JobOffert I didn't know how to link the customer to this new JobOffert, f I try create a page using customer model, I do not have the JobOffert attributes and if I create using the JobOffert model, I dont know how make the link between these two objects. how must I do this?
The link between the customer and JobOffer is as you defined One-to-Many and entities contains references to each other. E.g. You can find all JobOffer for a customer whose Id is 1024 by querying JobOffer table where customerID = 1024. Likewise each JobOffer is traceable by Customer reference in entity class.
Now about creating a new JobOffer for a customer this is how you can go about:
public class CustomerController : Controller
{
public CustomerBusiness customerBusiness { get; set; }
public CustomerController()
{
customerBusiness = new CustomerBusiness();
}
//Some code that makes CRUD and more these methods
[HttpGet]
public ActionResult ViewAllJobOffersForCustomer(int customerId)
{
ICollection<JobOfferModel> model = customerBusiness.GetAllJobOffersByCustomerId(customerId);
return View(model);
}
[HttpGet]
public ActionResult CreateJobOffer()
{
// Blank model object to accept values from user,
// you may like to create a view model based on UI needs.
JobOfferModel jobOfferModel = new JobOfferModel();
return View(jobOfferModel);
}
[HttpPost]
public ActionResult CreateJobOffer(JobOfferModel jobOffer)
{
// You get a filled object here that contains customer id and job offer details
customerBusiness.CreateJobOffer(jobOffer);
return RedirectToAction("ViewAllJobOffersForCustomer", new { customerId = jobOffer.CustomerId });
}
Sample business service class:
public class CustomerBusiness
{
public ICollection<JobOfferModel> GetAllJobOffersByCustomerId(int customerId)
{
// TODO: Fetch job offer details from persistent store
// E.g.
// dataContext.JobOffers.Where(x => x.CustomerId == customerId).ToList();
throw new NotImplementedException();
}
public void CreateJobOffer(JobOfferModel jobOffer)
{
// TODO: Add job offer details in persistent store
// E.g.
// dataContext.JobOffers.Add(jobOffer);
}
}
Modified entity classes:
public class JobOfferModel
{
[Key]
public int Id { get; set; }
public string Description { get; set; }
[Required]
[DefaultValue(false)]
public bool Acepted { get; set; }
[Required]
[DefaultValue(true)]
public bool Active { get; set; }
[Required]
[Column(TypeName = "DateTime2")]
public DateTime JobDate { get; set; }
[ForeignKey("Id")]
public int CustomerId { get; set; }
public virtual CustomerModel Customer { get; set; }
}
public class CustomerModel
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Column(TypeName = "DateTime2")]
public DateTime BirthDate { get; set; }
public int PhoneNumber { get; set; }
public ICollection<JobOfferModel> JobOffert { get; set; }
}
So basically you will have a method in CustomerController that returns empty ViewModel or Model object. On view you will make customerId hidden. So that when form is posted it is mapped to correct customer along with JobOffer details. Once you have model object in HttpPost method you just need to insert an entry in JobOffer table (any persistent store) with customerId associated with it.
There are other nitty gritty but above typical approach will give you a good start I hope. Cheers
Related
I've been searching all over the web to find an answer for such simple question, but I can't seem to find one.
Suppose we have a Product that has an edit page where we can set name, price and category.
Following the MVC(MVVM) pattern we have:
2 models ProductModel and ProductCategoryModel
ViewModel that holds the product and the categories list (for the dropdown)
View to display the edit page
Beside the above we have the ProductService which handles the CRUD operations
// Model
public class ProductModel
{
public int ProductId { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public int CategoryId { get; set; }
}
// Model
public class ProductCategoryModel
{
public int CategoryId { get; set; }
public string Name { get; set; }
}
// ViewModel
public class EditProductViewModel
{
public ProductModel Product { get; set; }
public List<ProductCategoryModel> Categories { get; set; }
}
The question is who is responsible for populating the ViewModel? I though it could be done in the constructor of the view model, however people say it is bad practice. Populating it in the controller also doesn't seems right.
You will map your EditProductViewModel from your ProductModel/ProductCategoryModel in the page controller.
You can then call your action to render your html page and pass your viewModel object.
Something like this :
public class HomeController : Controller
{
private EditProductViewModel viewModel;
public HomeController()
{
this.viewModel = new EditProductViewModel();
InitialiseViewModel();
}
public ActionResult Index()
{
return View("Index", viewModel);
}
private void InitialiseViewModel()
{
ProductCategoryModel productCategoryModel = new ProductCategoryModel();
ProductModel productModel = new ProductModel();
//do your mapping
productModel.Name = "Test mapping";
this.viewModel.Product = productModel;
}
}
Your .cshtml will look like this
#model WebApplication5.ViewModels.EditProductViewModel
<div class="jumbotron">
<h1>ASP.NET</h1>
<p>#Model.Product.Name</p>
<p class="lead">ASP.NET is a free web framework for building great Web sites and Web applications using HTML, CSS and JavaScript.</p>
<p>Learn more ยป</p>
</div>
I'm trying to add an object using a PartialView inside a popup. It's a simple Rental application for which the data model was generated Model First through Entity Framework. The Controllers and Views have mostly been Scaffolded by EF. The relationship between RentalApplication and RentalObject is 1 to many, meaning a RentalObject always has to have 1 RentalApplication.
My controller looks like this:
// GET: /Calendar/Add/1
// Create a PartialView using a RentalObject as the model.
// Use the provided ID to lock in the RentalApplication.
[HttpGet]
public PartialViewResult Add(int id)
{
return PartialView(
new RentalObject(db.RentalApplicationSet.FirstOrDefault(x => x.Id == id)));
}
// POST: /Calendar/Add
// Save the submitted RentalObject to the db
[HttpPost]
public ActionResult Add(RentalObject rentalobject)
{
if (ModelState.IsValid)
{
try
{
db.RentalObjectSet.Add(rentalobject);
db.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
return View();
}
My object looks like this:
public partial class RentalObject
{
public RentalObject()
{
this.Lease = new HashSet<Lease>();
}
public RentalObject(RentalApplication rentapp)
{
this.Lease = new HashSet<Lease>();
RentalApplication = rentapp;
PricePerHour = RentalApplication.DefaultPricePerHour;
Currency = RentalApplication.DefaultCurrency;
}
public int Id { get; set; }
public string Name { get; set; }
public bool IsAvailable { get; set; }
public string Illustration { get; set; }
public string Description { get; set; }
public decimal PricePerHour { get; set; }
public string Currency { get; set; }
public virtual ICollection<Lease> Lease { get; set; }
public virtual RentalApplication RentalApplication { get; set; }
}
So when I'm opening the popup (using #Ajax.ActionLink to GET the first controller Add action) I'm creating a RentalObject WITH a RentalApplication (2nd constructor) to use as the model. This works so far, the popup dialog shows the values PricePerHour and Currency from the RentalApplication.
However, when I submit the form in my PartialView popup everything gets copied over BUT the RentalApplication object. It somehow ends up creating a new RentalObject object using the PricePerHour and Currency from the original RentalApplication, but doesn't include the object itself under the RentalApplication property. My debugger even goes to the first constructor for RentalObject.
So I guess it's having trouble keeping a complex object inside another object when submitted from controller to view (GET) and back to controller (POST). Is this just poor practice on my part? Should I be using a ViewModel?
In the past I've had to use #Html.HiddenFor(m=>m.yourObjectHere) on objects that were not changed in the form to keep them from getting new-ed up again. I did this for every object I didn't use in the form (about 2 or 3).
Hope this helps.
I am getting stuck using a ViewModel. Suppose I want to give a logged-in person an Edit Form with only a few of the fields from my Person domain model (maybe I want to custom annotate validations in the ViewModel).
I am getting stuck in two separate places in the controller (I have marked them with "<<< >>>").
When I pass the whole Person object as a property to my ViewModel, I know what to do. I can get my code to only update the name fields, but then I have lost my ability to validate the individual properties in my ViewModel. On the hand if I limit the properties in my ViewModel to only to a few properties, then my code in the GET section where I cann vm.Person doesn't work, since I am not passing the Person.
I scanned many examples on SO, but they were all using AutoMapper. Can I accomplish this without a mapper, and/or how do I write my own? And thanks in advance!
Model:
public class Person()
{
public int PersonId { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string Email { get; set; }
}
ViewModel:
public class LoggedInPersonViewModel()
{
public int PersonId { get; set; }
[Required(ErrorMessage = "Last Name is required")]
public string LastName { get; set; }
public string FirstName { get; set; }
}
Repository:
public Person GetLoggedInPerson()
{
var user = HttpContext.Current.User.Identity;
var userid = user.GetUserId();
return db.People.SingleOrDefault(i => i.UserId == userid);
}
Controller:
public class RegistrationController : Controller
{
//Get Logged in User, Edit Form
public ActionResult UpdateDetails()
{
LoggedInPersonViewModel vm = new LoggedInPersonViewModel();
<<<Do I also need a Person property in my ViewModel>>>
vm.Person = repository.GetLoggedInPerson();
return View(vm);
}
//POST
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult UpdateDetails(LoggedInPersonViewModel loggedinpersonviewmodel)
{
if (ModelState.IsValid)
{
<<<what do i do here? is this correct? Again I cannot use Person if not in my VM.>>>
//Person person = db.People.Find(loggedinpersonviewmodel.PersonId);
//Person.FirstName = loggedinpersonviewmodel.FirstName;
//Person.LastName = loggedinpersonviewmodel.LastName;
//db.Entry(person).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index", "Person");
}
return View(loggedinpersonviewmodel);
}
}
}
Is there a way...or do I have to use AutoMapper for this?
I think you need to Map LoggedInPersonViewModel and Person. Example
public ActionResult UpdateDetails()
{
var person = repository.GetLoggedInPerson();
LoggedInPersonViewModel vm = new LoggedInPersonViewModel();
vm.PersonId = person.PersonId;
//Rest of properties
...
//return view model
return View(vm);
}
I would recommend AutoMapper this type of work. i.e. AutoMapper is a simple little library built to solve a deceptively complex problem - getting rid of code that mapped one object to another.
I have a class that requires another class to be specified, but I don't want the MVC ModelState validator to check whether the secondary model is valid. Is this possible?
Here's a brief overview:
My entities look something like this:
public class WidgetType
{
public long Id { get; private set; }
[Required]
public string Name { get; set; }
...
}
public class Widget
{
public long Id { get; private set; }
[Required]
public string Name { get; set; }
[Required]
public WidgetType WidgetType { get; set; }
...
}
I have them encapsulated in a WidgetViewModel class that I'm passing to/from the View like this:
public class WidgetViewModel
{
public Widget Widget { get; set; }
public ICollection<WidgetType> WidgetTypes
{
get
{
return _repository.GetWidgets();
}
}
...
}
My view looks something like this:
...
#Html.DropDownListFor( m => m.Widget.WidgetType.Id, new SelectList( new EquipmentViewModel().EquipmentTypes, "Id", "Name" ) )
...
All of this works except for validation. ModelState.IsValid is always false because "Widget.WidgetType.Name" is required. I need the user to select a WidgetType, but I don't want ModelState to be validated deeper than "Widget.WidgetType.Id" (which should be all that Widget needs for its foreign key?).
Is there a better way to do this? I feel like there should be some way to validate without recursively inspecting deeper into the properties, but I can't find it. What am I missing...?
public class WidgetViewModel
{
[Required]
public string Name { get; set; }
[Required]
public WidgetType WidgetTypeId { get; set; }
public SelectList WidgetTypes
{
get
{
//This should be popuplated in your controller or factory not in the view model
retun new SelectList{ _repository.GetWidgets(),"Id","Name");
}
}
}
In your view
#Html.DropDownListFor( m => m.WidgetTypeId, Model.WidgetTypes)
And in your controller
public ActionResult Create(WidgetViewModel model)
{
Widget widget = new Widget{
Name = model.Name,
WidgetType = yourManager.GetWidgetTypeByID(model.WigetTypeId);
};
yourManager.Create(widget);
//...
}
If all you need in your view is the WidgetID then you don't need to include the entire Widget in the WidgetViewModel. Just have property called WidgetID. View model classes should have only the data the is necessary for the view.
In the controller action method that is called when you submit the form, you can use the WidgetID to fetch the Widget object from the database if it is needed.
http://blog.stevensanderson.com/2010/02/19/partial-validation-in-aspnet-mvc-2/ gives an example of partial validation
I am coverting my app from webforms to mvc, at the moment i am at a design issue (well i just dont know how to do it in mvc).
Basically my model would be something like this:
public class DamagedItem
{
public Int32 LoanId {get;set;}
public String IdentityCode {get;set;}
public virtual ICollection<DamagedItems> DamagedItems {get;set;}
}
In my controller i would like to do:
public ActionResult Add(DamagedItem damagedItem)
{
//Do update logic here
}
Then in my view i can add to the ICollection as needed.
But, i can't do this because if i try and access the ICollection from my controller it is null.
Here is an image of when i want to do:
I just dont know how to lay it out in my view, how to i add such items to my ICollection, update the view then when i need to save i have access to what i have added from my controller?
Thanks,
Nick
Edit:
I was thinking of using a partial in the view and doing all the logic for the bottom half using ajax and storing it in a session variable, but i would prefer NOT to make it reliant on ajax.
It is better to separate: you shoud have 2 actions, which produce 2 view.
You should have LoadInformationModel classe:
public class LoadInformationModel
{
public string StudentCode { get; set; }
public string FirstName { get; set; }
// etc..
public ICollection<Damage> Type { get; set; }
}
corresponding action
[HttpGet]
public ActionResult LoanInformation(int id)
{
var loanInfo = // get data by given id..
var model = new LoadInformationModel {
StudentCode = loanInfo.StudentCode,
// etc
Type = new List<Damage> { new Damage { Value = "Damaged"}, new Damage { Value = "Damaged Again" }
}
return View(model);
}
As well as RepairDataModel class
public class RepairDataModel
{
public bool CoveredByWarranty { get; set; }
public ICollection Status { get; set; }
}
And corresponding action
[HttpGet]
public ActionResult Repair(int id)
{
// logic
return View(model);
}
Your task is to create Post handler, that would save data to DB then form submitted
[HttpPost]
public ActionResult(RepairDataModel model)
{
// save to db
return View();
}
The view returned by Index() method, could be created like
#Html.RenderAction("LoanInformation")
#Html.RenderAction("Repair")
The rest depends on your desing and imagination. I hope that would give you direction.
What I can see is only the DamagedItem lacks a contructor with values for Collection;
public class DamagedItem
{
public DamagedItem()
{
DamagedItems = new List<DamagedItems>();
DamagedItems.Add(new DamagedItem { Description = "Damaged" } );
}
public Int32 LoanId {get;set;}
public String IdentityCode {get;set;}
public virtual ICollection<DamagedItems> DamagedItems {get;set;}
}