Introduction
I am working with the mvc project, implementation approach is code-first.
Domain Model i am using, have more than 70 fields.So i made ViewModels.
Need for making view model is due to creating form wizard which store information in server side(Session Variable).
Domain Model
public class RegisterationDM
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int RegisterationId { get; set; }
//other fields
}
Main View Model
public class RegistrationViewModel
{
public PersonalViewModel PersonalViewModel {get;set; }
public DetailViewmodel DetailedViewmodel { get; set; }
public PhysicalDetailViewModel PhysicalDetailViewModel { get; set; }
public RequirementViewModel RequirementViewModel { get; set; }
public CreationInfoViewModel CreationInfoViewModel { get; set; }
}
Separate Classes
public class PersonalViewModel()
{
//fields
}
public class DetailViewmodel()
{
//fields
}
public class PhysicalDetailViewModel()
{
//fields
}
public class RequirementViewModel()
{
fields
}
public class CreationInfoViewModel()
{
//fields
}
Record Insertion Code
public ActionResult SaveInformation()
{
RegisterationDM regdm = new RegisterationDM();
RegistrationViewModel regvm = new RegistrationViewModel();
PersonalViewModel personalvm = (PersonalViewModel)Session["Personal"];
DetailViewmodel detailvm = (DetailViewmodel)Session["Detail"];
PhysicalDetailViewModel physicalvm = (PhysicalDetailViewModel)Session["Physical"];
RequirementViewModel requirementvm = (RequirementViewModel)Session["Requirement"];
CreationInfoViewModel createdinforvm = new CreationInfoViewModel();
createdinforvm.CreatedBy = "";
createdinforvm.CreatedDate = DateTime.Now;
regvm.PersonalViewModel = personalvm;
regvm.DetailedViewmodel = detailvm;
regvm.PhysicalDetailViewModel = physicalvm;
regvm.RequirementViewModel = requirementvm;
regvm.CreationInfoViewModel = createdinforvm;
//here assigning of view model to domain model
db.Reg.Add(regdm);
db.SaveChanges();
return View();
}
All Actions(Updated)
public ActionResult Step1()
{
RegistrationViewModel regvm = new RegistrationViewModel();
return View(regvm.PersonalViewModel);
}
[HttpPost]
public ActionResult Step1(PersonalViewModel personalvm)
{
if (ModelState.IsValid)
{
//Store the wizard in session
Session["Personal"] = personalvm;
return RedirectToAction("Step2");
}
else
{
return View(personalvm);
}
}
public ActionResult Step2()
{
if (Session["Personal"] != null)
{
RegistrationViewModel regvm = new RegistrationViewModel();
return View(regvm.DetailedViewmodel);
}
else
{
return RedirectToAction("Step1");
}
}
[HttpPost]
public ActionResult Step2(DetailViewmodel detailvm)
{
if (ModelState.IsValid)
{
//Store the wizard in session
Session["Detail"] = detailvm;
return RedirectToAction("Step3");
}
return View(detailvm);
}
public ActionResult Step3()
{
if (Session["Detail"] != null && Session["Personal"] != null)
{
RegistrationViewModel regvm = new RegistrationViewModel();
return View(regvm.PhysicalDetailViewModel);
}
else
{
return RedirectToAction("Step1");
}
}
[HttpPost]
public ActionResult Step3(PhysicalDetailViewModel physicalsvm)
{
if (ModelState.IsValid)
{
//Store the wizard in session
Session["Physical"] = physicalsvm;
return RedirectToAction("Step4");
}
return View(physicalsvm);
}
public ActionResult Step4()
{
if (Session["Detail"] != null && Session["Personal"] != null && Session["Physical"] != null)
{
RegistrationViewModel regvm = new RegistrationViewModel();
return View(regvm.RequirementViewModel);
}
else
{
return RedirectToAction("Step1");
}
}
[HttpPost]
public ActionResult Step4(RequirementViewModel requirementvm)
{
if (ModelState.IsValid)
{
Session["Requirement"] = requirementvm;
return RedirectToAction("SaveInformation");
}
return View(requirementvm);
}
Question
How can i add(record) using main view model.Should i map first?
I understand its not possible like that.So i ask, is there a proper way of doing that.What it might be?The best way, the right way or the wrong way?
I will prefer standard implementation even if it is hard to implement.
If someone have ideas about this problem, please do help.Any kind of help or reference will be appreciated.Thanks for your time.
(Due to lack of knowledge, may be i made some mistakes.Down Voters are welcome but please leave comment so i can improve question.)
By Defining your own Custom model binder would be suitable for such scenarios. I would recommend you to find some useful resources to get knowledge on how to implement this, Also this is one simple straight forward article CodeProject Custom Model Binder . Let me know if this was useful
We used Automapper to map fields. It is very helpful. Keeps code clean. It has customizable pre and post mapping functions too.
Related
I have a problem with Post a complex model object from client to Web Api controller.
My model structure is:
public class PaymentModel
{
public Credit Crediter { get; set; }
}
public class Credit : ICredit
{
public int BankInformationId { get; set; }
}
public interface ICredit
{
int BankInformationId { get; set; }
}
public sealed class CrediterEmployee:Credit
{
public int EnployeeId { get; set; }
}
I tried to create a model to post to API controller:
var param = new PaymentModel
{
Crediter = new CrediterEmployee
{
BankInformationId = 4928,
EnployeeId = 7013
},
}
In API controller I received a model object, but for Crediter I cannot cast to CreditEmployee. It is null when I tried to cast.
How can I cast Crediter to the CreditEmployee?
Have you tried something like this? ... And have you tried to do the POST maybe with tool like POSTMAN? (to be sure you're not sending an empty body)
[HttpPost]
[ResponseType(typeof(Credit))]
public async Task<IHttpActionResult> Post([FromBody] PaymentModel payment)
{
try
{
if (payment == null || payment.Crediter == null) return BadRequest();
var response = new Credit
{
BankInformationId = payment.Crediter.BankInformationId
};
return Ok(response);
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
I have a mvc project with a home controller
public class HomeController : BaseController
{
[MyActionFilter]
public ActionResult Index(string id)
{
var model = HttpContext.Items["model"] ;
var viewname = HttpContext.Items["viewname"] as string;
if (model != null)
return View(viewname, model);
model = GetDefaultModel(id);
return View(model);
}
}
Then i have a action filter
public class MyActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var id = filterContext.ActionParameters["id"];
List<IWillDoTheThing> myDoTheThings = LoadAllTheImplementationsOf<IWillDoTheThing>();
var myObject = myDoTheThings.Where(i => i.IdISupport == id).First().ExecuteTheThing();
filterContext.HttpContext.Items["viewname"] = myObject.ViewName;
filterContext.HttpContext.Items["model"] = myObject.Model;
}
}
Then an interface and a class
public interface IWillDoTheThing
{
string IdISupport { get; set; }
MyObject ExecuteTheThing();
}
public class MyObject
{
public string ViewName { get; set; }
public object Model { get; set; }
}
Depending on the id, i load viewname and model (dynamically from other assemblies). So using this method, i can change the home page without any big problem. My only question is, is it a good approach or if there is any better way to do it.
Help will be appreciated.
Regards
I'm creating a static class with static methods for helping the controllers to do their job. When build the application I get the following error:
Error 40 'System.Web.Mvc.Controller.Content(string)' is inaccessible due to its protection level"
Any idea how to solve this problem?
Notes:
It's a c# mvc aplication
public static ActionResult GetAlbumJSON(AlbumVO album)
{
return Controller.Content(
JsonConvert.SerializeObject(new
{
max_car = #ABookClient.maxCharsProjecName,
trans_img = #ABookClient.Transparent_Image,
show_description = #ABookClient.Show_Product_Description,
product_type = "Album",
obj = CreateObjAlbumVO(album),
})
);
}
Content method is protected internal, so you can't use it outside of controller.
Controller.Content Method. Most probably your static class violates SRP principle. Let him do his job (initializing, serializing,...) and controller - controller's job - return result to the client.
protected internal ContentResult Content(string content)
It would look smth like:
public static class MyHelper
{
public static object GetAlbum(AlbumVO album)
{
return new
{
max_car = #ABookClient.maxCharsProjecName,
trans_img = #ABookClient.Transparent_Image,
show_description = #ABookClient.Show_Product_Description,
product_type = "Album",
obj = CreateObjAlbumVO(album),
};
}
}
public class AlbumController : Controller
{
public ActionResult GetAlbums(int id)
{
var album = Context.GetAlbum(id);
var convertedResult = MyHelper.GetAlbum(album);
return Json(convertedResult);
}
}
Also I'd advice to take a look at AutoMapper for creating client response objects
I think this is valid case for a view-model for a JSON result since you do want a separation between the Domain model and the data sent back to the client. Using a view model also gives you a proper place to put this mapping between the domain model and the view (the JSON) so you don't need to delegate to a helper class.
public class AlbumModel
{
[JsonProperty(PropertyName = "max_car")]
public int MaxChars { get; private set; }
[JsonProperty(PropertyName = "trans_img")]
public string TransparentImage { get; private set; }
[JsonProperty(PropertyName = "product_type")]
public string ProductType { get; private set; }
[JsonProperty(PropertyName = "obj")]
public AlbumInfo Object { get; private set; }
[JsonProperty(PropertyName = "show_description")]
public bool ShowProductDescription { get; private set; }
public AlbumModel(AlbumVO album)
{
MaxChars = album.maxCharsProjecName;
TransparentImage = album.Transparent_Image;
ShowProductDescription = album.Show_Product_Description;
ProductType = "Album";
Object = new AlbumInfo(album);
}
}
The AlbumInfo class provides additional mappings for your JSON result, which becomes the "obj" property sent back to the client.
public class AlbumInfo
{
// ... define properties here
public AlbumInfo(AlbumVO album)
{
// ... map properties here
}
}
And your controller becomes nice and clean:
public class AlbumController : Conrtoller
{
public ActionResult GetAlbums(int id)
{
var album = Context.GetAlbum(id);
var model = new AlbumModel(album);
return Json(model);
}
}
I have two models with a one to many relationship
public class Team
{
public int TeamID { get; set; }
public string Name { get; set; }
public virtual ICollection<Player> Players { get; set; }
}
public class Player
{
public int PlayerID { get; set; }
public string Name { get; set; }
public virtual Team Team { get; set; }
}
I would like to change the Team's details view to enable adding a new Player to the team
// GET: /Team/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Player player = db.Players.Find(id);
if (player == null)
{
return HttpNotFound();
}
return View(player);
}
It has been a little hard to find any info on this but from what I can gather the best way to go about this would be to add a viewmodel
public class TeamViewModel
{
public Team team { get; set; }
public Player player { get; set; }
}
and send that to my details view, I have not had much success with this and am not too sure if this is even the right approach.
Any links or guidance would be greatly appreciated.
Update
I have managed to get the details view working but now need to sort out the post method
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Details(TeamViewModel tvm)
{
if (ModelState.IsValid)
{
tvm.player.team = tvm.team;
db.Players.Add(tvm.player);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(tvm);
}
This will create the new player but will not store the relationship so the column TeamID in the player table is empty.
Update 2
I have done some debugging and it seems that my teamViewModel is not storing the team data during the post.
// GET: /Team/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Team team = db.Teams.Find(id);
if (team == null)
{
return HttpNotFound();
}
var teamViewModel = new TeamViewModel()
{
team = team,
player = new Player() { Team = db.Team.Find(id) }
};
return View(teamViewModel);
}
// POST: /Team/Details/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Details(TeamViewModel teamViewModel)
{
if (ModelState.IsValid)
{
db.Players.Add(teamViewModel.player);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(teamViewModel);
}
So what you want to do is the following:
Display a view with the Team's data.
On the same view have the possibility to add a new Player.
Your view model:
public class TeamViewModel
{
public Team team { get; set; }
public Player player { get; set; }
}
Can handle this job.
Team data should be fetched before generating the view.
Player can be empty - it will just be used for the input fields generation purposes of your view.
You should change your action more less like this:
// GET: /Team/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Team team= db.Team.Find(id); //assuming that this is the id of the team
if (team== null)
{
return HttpNotFound();
}
var teamViewModel = new TeamViewModel() { Team = team, Player = new Player() };
return View(teamViewModel);
}
It is a common practice to use ViewModels. Thanks to this you separate your business models from the models that are used when presenting the data to a user. What is more you can then aggregate all the data you need and you are not limited to passing just one object to the view.
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;}
}