Add new related model to existing model - c#

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.

Related

The best way to check given value on unique in database with .NET Core

I have the following controller's action:
public async Task<IActionResult> Create([FromForm]LanguageViewModel viewModel)
{
if (!ModelState.IsValid)
{
return View(viewModel);
}
var newLanguage = new Language()
{
Sort = viewModel.Sort,
LangCode = viewModel.Code,
LangName = viewModel.Name
};
await _languageRepository.SaveAsync(newLanguage);
return RedirectToAction("Index");
}
And the following viewModel:
public class LanguageViewModel
{
public int Sort { get; set; }
[Required(ErrorMessage = "Language code is required")]
public string Code { get; set; }
[Required(ErrorMessage = "Language name is required")]
public string Name { get; set; }
}
Note, that LanguageViewModel is not my domain (Entity framework) object. For map on my database I have another one:
public class Language
{
public int LanguageId { get; set; }
public int Sort { get; set; }
public string LangName { get; set; }
public string LangCode { get; set; }
}
So, I'm working with ASP.NET MVC Core 3.1 and I need to check if Language with given code already exists or not. If it exists, I would like to show validation error about it. You can say that I can solve it like:
public async Task<IActionResult> Create([FromForm] LanguageViewModel viewModel)
{
if (!ModelState.IsValid)
{
return View(viewModel);
}
var lang = await _languageRepository.All.FirstOrDefaultAsync(x =>
x.LangCode.ToUpper() == viewModel.Code.ToUpper());
if (lang != null)
{
ModelState.TryAddModelError("Code", $"The language with code {viewModel.Code} already exists");
}
if (!ModelState.IsValid)
{
return View(viewModel);
}
var newLanguage = new Language()
{
Sort = viewModel.Sort,
LangCode = viewModel.Code,
LangName = viewModel.Name
};
await _languageRepository.SaveAsync(newLanguage);
return RedirectToAction("Index");
}
Ok. it works. But it's ugly :( Maybe there is better solution?
Maybe that would be a little bit cleaner solution.
if (_languageRepository.Any(o => o.LangCode.Equals(txnId, StringComparison.InvariantCultureIgnoreCase)))
{
ModelState.TryAddModelError("Code", $"The language with code {viewModel.Code} already exists");
}
Better to try this introduced after EF Core 5:
https://learn.microsoft.com/en-us/ef/core/modeling/indexes?tabs=data-annotations
[Index(nameof(Url), IsUnique = true)]
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}

How to save/upload image into mongodb using c# asp.net mvc?

I am trying to create asp.net mvc app with users for biliard amateur league (this is my practice project), and store players data into MongoDb, but i don't know how to insert picture for player. For now i can put field like Name, Surname etc. Here's my code for:
Create function:
public void Create(Player player)
{
playerCollection.InsertOne(player);
}
Create controller:
public ActionResult Create(Player player)
{
playerModel.Create(player);
return RedirectToAction("Index");
}
Player object:
public class Player
{
[BsonId]
public ObjectId Id { get; set; }
[BsonElement("Name")]
public string Name{ get; set; }
[BsonElement("Surname")]
public string Surname{ get; set; }
//[BsonElement("Picture")]
//public byte[] Picture{ get; set; } <- I commented on that until I found a solution
}
I guess the HttpPostedFileBase must be used, but ... I do not know how to implement logic. (and if possible without GridFS).
Cure my headaches! :)
Edit
Something like this:
public ActionResult Create(Player player, HttpPostedFileBase choosenPicture)
{
if (ModelState.IsValid)
{
if (choosenPicture != null)
{
player.Picture = new byte[choosenPicture.ContentLength];
Stream str = choosenPicture.InputStream;
str.Read(player.Picture, 0, choosenPicture.ContentLength);
}
try
{
db.Players.Add(player);
db.SaveChanges();
return RedirectToAction("Index");
}
catch (Exception)
{
ViewBag.Error = "Error :(";
}
}
return View(player);
}

How to insert record using viewmodels : Asp.Net Mvc

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.

Trouble Saving object and Child Objects to the database?

I am having trouble to see what i need to correct or add to my controller in order to have my data saved to the database. See below what i have so far.
Create method
[HttpPost]
public ActionResult Create(Team model)
{
if (ModelState.IsValid)
{
new Team
{
Name = model.Name,
Division = model.Division,
Description = model.Description,
TeamContact = new TeamContact
{
EmailAddress = model.TeamContact.EmailAddress,
PhoneNumber = model.TeamContact.PhoneNumber,
TeamAddress = new TeamAddress
{
Box = model.TeamContact.TeamAddress.Box,
StreetName = model.TeamContact.TeamAddress.StreetName,
StreetNumber = model.TeamContact.TeamAddress.StreetNumber,
City = model.TeamContact.TeamAddress.City,
PostalCode = model.TeamContact.TeamAddress.PostalCode,
Province = model.TeamContact.TeamAddress.Province
}
}
};
_dataSource.Save();
}
return View(model);
Table Relationships
-Team one-to-one TeamContact
-TeamContact one-to-on TeamAddress
IDataSource Interface
IQueryable<Team> Teams { get; }
IQueryable<TeamAddress> TeamAddresses { get; }
IQueryable<TeamContact> TeamContacts { get; }
void Save();
Context class
public DbSet<Team> Teams { get; set; }
IQueryable<Team> IDataSource.Teams
{
get { return Teams; }
}
public DbSet<TeamAddress> TeamAddresses { get; set; }
IQueryable<TeamAddress> IDataSource.TeamAddresses
{
get { return TeamAddresses; }
}
public DbSet<TeamContact> TeamContacts { get; set; }
IQueryable<TeamContact> IDataSource.TeamContacts
{
get { return TeamContacts; }
}
public void Save()
{
SaveChanges();
}
What am i missing to have my data saved to the database?
You don't have any code in your controller that is actually adding the Team to your database. Right now you are simply creating a new Team object in memory and saving changes to your database. Nothing is actually being added to the database to save.
Team team = new Team
{
.. the rest of your model building code here ..
};
_dataSource.Teams.Add(team); // This adds the Team entity to the database
_dataSource.Save();

View and ICollection Question

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;}
}

Categories

Resources