ASP.NET MVC multiple models in one view - c#

I have the following models
Patient Class:
public class Patient
{
public int PatientID { get; set; }
public virtual Salutation salutation { get; set; }
public int SalutationID { get; set; }
public string Surname { get; set; }
public string Firstname { get; set; }
[Display(Name = "Date of Birth")]
[DisplayFormat(DataFormatString="{0:d}", ApplyFormatInEditMode=true)]
public DateTime DOB { get; set; }
public string RegNo { get; set; }
public DateTime RegDate { get; set; }
public string Occupation { get; set; }
}
VatalSigns Class
public class VitalSign
{
public int VitalSignID { get; set; }
public string Sign { get; set; }
[Display(Name = "Lower Limit")]
public int? LowerHold { get; set; }
[Display(Name = "Upper Limit")]
public int? UpperHold { get; set; }
[Display(Name = "Unit Of Measurment")]
public string Units { get; set; }
}
PV class that stores VitalSigns for each patient
public class PVSign
{
public long PVSignId { get; set; }
[Display(Name = "Patient")]
public int PatientID { get; set; }
public VitalSign VitalSigns { get; set; }
//public IList<VitalSign> VitalSigns { get; set; }
public Patient patient { get; set; }
}
Now the problem I have is that I have not been able to get display on one from to enter the details. I want to select the Patient and the different Vitals signs will appear and I will save the ones I need to the PVSign table.
I have tired all sorts of samples from the web. You can see from the code below, this is the index stub:
public ActionResult Index()
{
var pVSigns = db.PVSigns.Include(p => p.patient).Include(p => p.PVSignId).Include(p => p.VitalSigns);
//var pVSigns = from o in db.Patients join o2 in db.PVSigns
//List<object> myModel = new List<object>();
//myModel.Add(db.Patients.ToList());
//myModel.Add(db.VitalSigns.ToList());
//return View(myModel);
return View(pVSigns.ToList());
}
How to I solve this issue. I am new to MVC, if it was Webforms, I would have been through with this project.
Thank you.

There is no single answer(solution) to this(your) problem. It depends on how you want to design/build/achieve your solution.
The simple, brute solution : Just have a view model as a wraper that has as his properties the classes(models) you made and work around that,
public class FullPatientDetailsViewModel
{
public Patient { get; set;}
public List<PVSign> PatientPvSigns { get; set;} // Patien PV Signs
}
Or use just a PatientViewModel and load his PVSigns async with Ajax.
There is no simple best solution, it all depends about what do you want to achieve as a bigger picture.

I've answered a similar question here. As Alexandru mentioned, the easiest and most straightforward way would be to wrap your models inside a ViewModel. You can read about other ways to implement this here.

Related

How unflattening commands to complex types

I'm not yet dependent to either Mapster or AutoMapper. For now I'm using handwritten mappings because I couldn't find a mapper who could do this with smaller code.
The problem is how do we map flatten structures to complex objects? I think a lot of people could benefit from a good mapping example for such a complex object. I've got even a mapping condition based on CopyOfficeAddressAsInvoiceAddress whether or not the office address needs to be copied as invoice address. I've looked all over the place but couldn't get it to work.
Maybe I should also use a different naming to make it more clear for the mapping algorithm?!
The biggest question could such a map being resolved by a mapper or is this to complex? Al the demo's I've seen were using dto and model objects that are quite similar to each other. I didn't get the point of mapping an object to another object that 99% similar to each other.
I have a Command (I'm using Mediatr) that looks like as follows:
public class Command : IRequest<IActionResult>
{
public string AccountName { get; set; }
public string ContactFirstName { get; set; }
public string ContactLastName { get; set; }
public string ContactEMail { get; set; }
public string ContactPhoneNumber { get; set; }
public string BankAccount { get; set; }
public string Bank { get; set; }
public string OfficeName { get; set; }
public string OfficeAddressStreet { get; set; }
public int OfficeAddressStreetNumber { get; set; }
public string? OfficeAddressStreetNumberAddition { get; set; }
public string OfficeAddressPostalcode { get; set; }
public string OfficeAddressCity { get; set; }
public string OfficeAddressCountry { get; set; }
public string? OfficeInvoiceAddressStreet { get; set; } = null;
public int? OfficeInvoiceAddressStreetNumber { get; set; } = null;
public string? OfficeInvoiceAddressStreetNumberAddition { get; set; } = null;
public string? OfficeInvoiceAddressPostalcode { get; set; } = null;
public string? OfficeInvoiceAddressCity { get; set; } = null;
public string? OfficeInvoiceAddressCountry { get; set; } = null;
//[Ignore]
public bool? CopyOfficeAddressAsInvoiceAddress { get; set; } = false;
public string? AssociationIdentifier { get; set; } = null;
}
And I want it to be mapped to the following models:
public class Account
{
public int Id { get; set; }
public string AccountName { get; set; }
public IList<Contact> Users { get; set; }
public IList<Office> Offices { get; set; }
public string Bank { get; set; }
public string BankAccount { get; set; }
public string? AssociationIdentifier { get; set; }
}
public class Office
{
public int Id { get; set; }
public string Name { get; set; }
public Address ContactAddress { get; set; }
public Address InvoiceAddress { get; set; }
public bool HeadQuarter { get; set; }
}
public class Address
{
public int Id { get; set; }
public string Street { get; set; }
public string Postalcode { get; set; }
public int StreetNumber { get; set; }
public string StreetNumberAddition { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
public class Contact
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EMail { get; set; }
public string PhoneNumber { get; set; }
}
First of all, my experience is mainly using Automapper, and it is definitely possible to map complex types like this.
But your command does not need to be completely flat. There is nothing inherently wrong with DTOs being similar to your domain models. Using Automapper this is fairly easy as properties with the same name are mapped 1:1.
It could be that you are submitting a form with all the properties flattened in one object. In that case you could define either a seperate map for this object and each domain object.
CreateMap<AccountDto, Account>(); // mapping logic omitted
CreateMap<AccountDto, Office>();
...
Or you could map the one object to a range of objects using Tuples.
CreateMap<AccountDto, (Account, Office, ...)>(); // mapping logic omitted
But if you define seperate DTOs and make mapping profiles for them, it will probably ease your whole mapping experience. For copying the address, you can simply do something like this, in that case.
if (copyAddress)
{
office.InvoiceAddress = _mapper.Map<Address>(addressDto);
}

Automapper Mapping Exception

I am trying to build a simple application where I can store and retrieve some details about some devices and the users of them, like an inventory. But when I try to display the list of devices with their owners, Automapper throws this error:
AutoMapperMappingException: Missing type map configuration or unsupported mapping.
I don't understand what I am doing wrong here. How can I deal with this?
Startup.cs
builder.Services.AddAutoMapper(typeof(MapConfig));
builder.Services.AddControllersWithViews();
var app = builder.Build();
MapConfig.cs
public class MapConfig : Profile
{
public MapConfig()
{
CreateMap<Asset, AssetVM>().ReverseMap();
CreateMap<AppUser, AppUsersVM>().ReverseMap();
}
}
Asset.Cs
public class Asset
{
public int Id { get; set; }
public string Brand { get; set; }
public string Model { get; set; }
public string? ProductNumber { get; set; }
public string? SerialNumber { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateModified { get; set; }
public bool IsAssigned { get; set; }
public string? ISN { get; set; }
public string Status { get; set; }
public bool IsInsured { get; set; }
public string Condition { get; set; }
[ForeignKey("UserId")]
public AppUser AppUser { get; set; }
public string? UserId { get; set; }
}
AssetVM
public class AssetVM
{
public int Id { get; set; }
public string Brand { get; set; }
public string Model { get; set; }
[Display(Name ="Product Number")]
public string? ProductNumber { get; set; }
[Display(Name ="Serial Number")]
public string? SerialNumber { get; set; }
[Display(Name ="Date Created")]
[DataType(DataType.Date)]
public DateTime DateCreated { get; set; }
[Display(Name = "Date Modified")]
[DataType(DataType.Date)]
public DateTime DateModified { get; set; }
[Display(Name ="Assigned")]
public bool IsAssigned { get; set; }
public string? ISN { get; set; }
[Required]
public string Status { get; set; }
[Display(Name ="Has Insurance")]
public bool IsInsured { get; set; }
[Required]
public string Condition { get; set; }
public string? UserId { get; set; }
public SelectList? AppUsersList { get; set; }
public AppUsersVM AppUsers { get; set; }
}
This is how I get and map the data that is to be displayed on the page:
public async Task<AssetVM> GetAssets()
{
var asset = await context.Assets.Include(q => q.AppUser).ToListAsync();
var model = mapper.Map<AssetVM>(asset);
return model;
}
And finally I return the result of GetAssets method to the view in my controller:
var model = await assetRepository.GetAssets();
return View(model);
Well, I found what I'm doing wrong. This is what I've done:
Since I was getting a list after querying the database in my GetAssets method, I had to change my mapping to:
var model = mapper.Map<List<AssetVM>>(asset);
And to be able to return this model, I also had to change my method declaration to:
public async Task<List<AssetVM>> GetAssets()
This changes made it work, except I didn't get the details of the user that is using that asset. And this was due to a typo in my AssetVM viewmodel.
public AppUsersVM AppUser { get; set; }
Those were all the changed I had to do. Still have a looong way to be a competent programmer so I'd be glad if you let me know if I have any flaws in my logic or any recommendations at all.

Model collection inside a Model

I am a complete newbie in asp.net mvc. And i'm stuck with some problem and don't yet know how to solve it. I googled, found some useful things, but they haven't helped me with solving my problem. So let's begin with what i have now:
public partial class Dish
{
public Dish()
{
PortionFood = new HashSet<PortionFood>();
}
[Key]
public int Id { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
[JsonIgnore]
public virtual DishCategory DishCategory { get; set; }
[JsonIgnore]
public virtual ICollection<PortionFood> PortionFood { get; set; }
}
}
// Food class in a separate file
public partial class Food
{
public Food()
{
PortionFood = new HashSet<PortionFood>();
}
[Key]
public int Id { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
public float Proteins { get; set; }
public float Fat { get; set; }
public float Carbs { get; set; }
public float Ccal { get; set; }
public float? Sugar { get; set; }
public float? AmountOfWater { get; set; }
[ForeignKey("FoodCategory")]
public int IdFoodCategory { get; set; }
[ForeignKey("FoodConsistencyType")]
public int IdFoodConsistencyType { get; set; }
[JsonIgnore]
public virtual FoodCategory FoodCategory { get; set; }
[JsonIgnore]
public virtual ICollection<PortionFood> PortionFood { get; set; }
}
}
// Portion Food Class in a separate file
public partial class PortionFood
{
[Key]
[Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[ForeignKey("Food")]
public int IdFood { get; set; }
[Key]
[Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[ForeignKey("Dish")]
public int IdDish { get; set; }
public float Amount { get; set; }
public bool IsDeleted { get; set; }
public virtual Dish Dish { get; set; }
public virtual Food Food { get; set; }
}
}
PortionFood just specifies what amount of what Food is inside which Dish.
Now i have views for classes Dish and Food, and i am able to add new object of each of these two classes(by filling in the view fields and pressing the button) to DB, i am able to edit an existing Dish && an existing Food. See list of all existing Food && Dish objects.
What i want:
When adding/editing a Dish there must be a possibility to add a PortionFood object releated to current Dish(which we are editing or adding). Just by selecting a FoodId in a dropdown list, setting an amount and clicking the button. The PortionFood object must be inserted to PotionFood collection of related Dish and related Food
How can i implement it?
If there're any uncretainties write in a comment, and i'll try to make it clear.
Thank's for help!
I think I see your problem now, you need to populate from ICollection not to it. In that case I suggest using List<SelectListItem> because you can then use DropDownListFor html helper.
For example:
public class ViewModel {
private readonly List<PortionFood> _foods;
[Display(Name = "Pick a portion")]
public int SelectedFoodItem { get; set; }
public IEnumerable<SelectListItem> FoodPortionItems
{
get { return new SelectList(_foods, "Id", "Name"); }
}
}
public class PortionFood {
public int Id { get; set; }
public string Name { get; set; }
}
//In your Razor View:
#Html.LabelFor(m => m.SelectedFoodItem)
#Html.DropDownListFor(m => m.SelectedFoodItem, Model.FoodPortionItems)
That should help you populate the dropdown.
PS: I usually like to use ViewModels instead of Models here, that way you only have what you need in the View

ASP.NET MVC 5, how to enable validation annotation on viewmodel that composes other view models?

Well I have a very complex User Profile system in a social network application I am building. The profile page has tabs that distinguishes each category of user profile information: Basic, Education, Job. There is a UserProfileViewModel sitting on top of everything, which composes of inner view models such as BasicViewModel, EducationViewModel and JobViewModel. Consider the structure as below:
public class ProfileViewModel
{
public string id { get; set; }
public BasicViewModel basic { get; set; }
public EducationViewModel education { get; set; }
public JobViewModel job { get; set; }
}
public class BasicViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime? DateOfRegistration { get; set; }
public DateTime? DateOfBirth { get; set; }
public string Gender { get; set; }
public string PhoneNumber { get; set; }
[Display(Name = "Biography")]
public string Biography { get; set; }
public string NickName { get; set; }
public string FavoriteQuotes { get; set; }
}
public class EducationViewModel{
public string EducationStatus { get; set; }
public List<University> Universities { get; set; }
public string CourseStatus { get; set; }
public string CourseSpecialization { get; set; }
public List<string> EducationEvents { get; set; }
}
public class JobViewModel
{
public string WorkStatus { get; set; }
public List<Company> Companies { get; set; }
}
public abstract class Organization
{
public string Name { get; set; }
public DateTime? Year { get; set; }
public int TimePeiod { get; set; }
}
public class University: Organization
{
public string Degree { get; set; }
public string Profession { get; set; }
}
public class Company: Organization
{
public string Website { get; set; }
public string Position { get; set; }
}
So the question is, does data annotation for model validation(both server and client side) work for a model that has composite structure like this? If so, do I just place annotation like I usually do with simple view models? If not, how can I achieve this in alternative ways? Please help.
Any single view model may contain other viewmodels like this:
This model is server side:
[Serializable]
public class MyBigViewModel : IValidatableObject
{
public MyBigViewModel(){
MyOtherViewModel = new MyOtherViewModel();
MyThirdViewModel = new MyThirdViewModel();
}
public MyOtherViewModel {get;set;}
public MyThiddViewModel {get;set;}
public void Post(){
//you can do something here based on post back
//like maybe this where the post method here processes new data
MyOtherViewModel.Post();
}
}
The controller could look like this:
public ActionResult UserList (MyBigViewModel uvm){
if(ModelState.IsValid){
uvm.Post();
return View(uvm);
}
return View(uvm);
}
You can implement the IValidateableObject to do "server side" validation. In the example above however, we want each viewmodel to "contain" it's own model for validation.
Each viewmodel property can use data annotations "contained" in only that viewmodel. It's a very nice way to "Contain" what you want where you want.
I very often use multiple Viewmodels in main VM and pass them in with partial views as needed.

MVC Model doesnt support ToPagedList

I have a model like this
public class SearchVM
{
[DisplayName("Type")]
public string Type { get; set; }
[DisplayName("Beds")]
public string NumberOfBeds { get; set; }
[DisplayName("Baths")]
public string NumberOfBaths { get; set; }
[DisplayName("Prices From")]
public string PriceFrom { get; set; }
[DisplayName("Prices To")]
public string PriceTo { get; set; }
public IEnumerable<Rental> Rentals { get; set; }
public IEnumerable<Sale> Sales { get; set; }
public IEnumerable<Rental> FeatureRentals { get; set; }
public IEnumerable<Sale> FeatureSales { get; set; }
public Rental NewRentals { get; set; }
public Sale NewSales { get; set; }
}
In my controller when trying to use this model SearchVM i cannot use the ToPagedList method
public ActionResult Search(SearchVM searchvm, int page = 1)
{
var query = from c in context.Rentals
select c;
searchvm.Rentals = query;
return View("RentProperty", query.ToPagedList(page,9));
}
I noticed that searchvm.ToPagedList doesnt work. Can some one please help
You are either missing a library reference in your code or if you already have that, you need to add a relevant using statement. For example, you may need this at the top of your code:
using PagedList;
Without this line, the compiler doesn't know where to get the ToPagedList() extension method.

Categories