I have 2 models like this.
public partial class Question
{
public int QuestionId { get; set; }
public string QuestionText { get; set; }
public string Ans1 { get; set; }
public string Ans2 { get; set; }
public string Ans3 { get; set; }
public string Ans4 { get; set; }
}
public partial class UserAnswer
{
public int UserAnsId { get; set; }
public Nullable<int> QuestionId { get; set; }
public Nullable<int> UserId { get; set; }
public Nullable<int> AnsVal { get; set; }
}
As you can see QuestionId is in both the models. How can I render it in view. There are multiple questions. Question Moldel has data in initial run but UserAnswer doesn't.
How can I combine these 2 models so that I can use it as IEnumerable in view. Ans1,Ans2,Ans3,Ans4 has text and AnsVal in UserAnswer will get its value from Raiobutton.
make a combine class like below..i am not sure this is perfect or not..any suggestions are acceptable.
public class QuestionAnswerViewModel
{
public Question Question {get;set;}
public ICollection<UserAnswer> Answers {get;set;}
}
You want to create a ViewModel that represents the combined model objects. This keeps things clean, your model is just that, the model, what gets passed to the view can be the model but in many cases the concept of a ViewModel will make things easier to design while keeping your code loosely coupled and clean. This also keeps things that are not important to the View out of the equation aka particular properties in your model such as maybe a CreatedDate should not be passed to the View, especially since View requests will pass back the value as null since it is not being used in the view and thus not populated on postback. This could lead to you updating the database with a null value for CreatedDate simply because it was not used in the View.
Maybe you have a Model class library in your solution? If you do, create another class library called MyNamespace.Web.ViewModels or something like that. Also you should look into using a tool like AutoMapper that will populate the ViewModel on View request to the Controller and populate the model on View postback to the controller.
Related
I am working on an ASP.Net MVC Website.
I have a table called animal, which is created based of this class using Entity Framework code first:
public class Animal
{
public int AnimalId { get; set; }
public string AnimalName { get; set; }
public int NoOfLegs { get; set; }
public int FlyingSpeed { get; set; }
public int RunningSpeed { get; set; }
}
Now I have my AnimalRepository that reads this info from the DB and pass it to my ViewModel (My Domain Classes are different from my ViewModel and I am using ModelReader, ModelWriter and AutoMapper to Map my domain model into my view model as explained here). I have this generic interface in my ViewModel:
public interface IAnimalModel
{
int AnimalId { get; set; }
string AnimalName { get; set; }
int NoOfLegs { get; set; }
}
And I have these two classes in my ViewModel:
public class DogModel: IAnimalModel
{
public int AnimalId { get; set; }
public string AnimalName { get; set; }
public NoOfLegs { get; set; }
public int RunningSpeed { get; set; }
}
public class EagleModel: IAnimalModel
{
public int AnimalId { get; set; }
public string AnimalName { get; set; }
public NoOfLegs { get; set; }
public int FlyingSpeed { get; set; }
}
Now I have one Animal Controller that uses AnimalRepositoy to get the data from DB, and maps it to the correct ViewModel.
I want to bind my View to IAnimalModel interface so that I can pass different animals to the same View.
In my View, I want to use Razor Code to display certain properties based on Animal Model type, something like this:
#if (typeof(Model) == typeof(EagleModel)) {
Html.EditorFor(model => model.FlyingSpeed)
}
#if (typeof(Model) == typeof(DogModel)) {
Html.EditorFor(model => model.RunningSpeed)
}
I have been thinking about this for a long time and I am not sure if this is a good solution? I have quite a few different Animal types with a lot of common properties, so don't really like the idea of creating one Table for each different animal.
I thought it's better to map them to correct type in my ViewModel...
I am not really sure if binding my ViewModel to IAnimalModel interface is a good idea? As I need to check the model type before displaying certain properties.
Another disadvantage is that in my DB, I don't know which type of animal each row contains... I am thinking maybe of I have to add a ViewModelType column to my Animal table, but again I am not sure if this is a good solution?
I think the problem is much simpler to be honest.
You're talking about a lot of animals but you could think in terms of types and then everything simplifies.
So based on some very quick research, you have 6 animal types:
Invertebrates
Fish
Amphibians
Reptiles
Birds
Mammals
You can probably get away with less. So if you look at your problem from this point of view, now you don't have that many different types to look at.
So, have an animal type enum and use that to differentiate the fields you are displaying.
In terms of database, I would not go with one table and null fields as that complicates everything. Either create a base one with whatever is common and a separate one for each type, or go with a Nosql db and save one type data per row.
Finally I don't really think that using a base interface gives you anything. You still have to repeat the fields in every class that implements it and you're not achieving anything in terms of simplification. If you instead go with a base abstract class then at least you don't have to repeat the same properties everywhere and your classes are now smaller and reflect the differences properly.
I'm trying to do something i that feels like a small task, but i cannot figure out a simple way to do it. All my approaches for doing this gets really complex for a simple task.
I have these models:
public class Blog
{
public int Id { get; set; }
public String Name { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
public class Post
{
public int Id { get; set; }
public String Name { get; set; }
public int BlogId { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
public class Comment
{
public int Id { get; set; }
public String CommentText { get; set; }
public int PostId { get; set; }
public int UserProfileUserId { get; set; }
}
public class UserProfile
{
public int UserId { get; set; }
public string UserName { get; set; }
public String FirstName { get; set; }
public String LastName { get; set; }
}
In the Added Comments partial view, i want to show the full user name of the user that made a comment. If i just use my base classes in my views and partial views, i get everything i need except full user name on added comments. So far, i've thought of the following ways:
ViewModels - This will result in creating a ViewModel for each of my Classes and then populate / map them manually in my controller.
Code in Views - I have the UserProfileUserId so i can just ask the repository from the view but this Kills the MVC in MVC so i don't want to do it.
Actually Adding UserProfileFirstName and UserProfileLastName to the Comment Class as foreign keys - This feels like filling the database with view specific data. It doesn't belong in a relational database.
Using regular SQL and Query the database - Just because i know SQL, this -could- be a way to do it. but then again i'm killing the MVC in MVC.
How should i do this? Where is my silly overlooked option? I've searched a lot but could not find an answer, but this could be related to me not knowing all the technical terms yet. Sorry if this is answered 1000 times before.
Ideally i would change my domain model to include a Author property of type UserProfile and load that data as well using a JOIN (Comment table and User table)
public class Comment
{
public int Id { get; set; }
public String CommentText { get; set; }
public int PostId { get; set; }
public UserProfile Author { get; set; }
}
EDIT : As per the questions in the comment
This is how i will do this.
My Repositary method will have these methods
List<Comment> GetCommentsForPost(int postId);
BlogPost GetPost(int postId);
I would have ViewModel for representing a single blog post like this
public class PostViewModel
{
public int PostID { set;get;}
public string PostText { set;get;}
public string AuthorDisplayName { set;get;}
public List<CommentViewModel> Comments { set;get;}
public PostViewModel()
{
Comments=new List<CommentViewModel>();
}
}
public class CommentViewModel
{
public int CommentID {set;get;}
public string Text { set;get;}
public string AuthorDisplayName { set;get;}
}
Now in your GET Action, Get the data from your Repositary and Map that to ViewModel and send it to view
public ActionResult ViewPost(int id)
{
var post=repositary.GetPost(id);
if(post!=null)
{
PostViewModel vm=new PostViewModel { PostID=id };
vm.PostText=post.Name;
var comments=repo.GetCommentsForPost(id);
foreach(var item in comments)
{
vm.Comments.Add(new CommentViewModel { CommentID=item.Id,
AuthorDisplayName=item.Author.FirstName});
}
return View(vm);
}
return View("NotFound");
}
Now your view will be strongly typed to The PostViewModel
#model PostViewModel
<h2>#Model.PostText</h2>
#Html.Partial("Comments",Model.Comments)
And your partial view(Comments.cshtml) will be strongly typed to a collection of CommentViewModel
#model List<CommentViewModel>
#foreach(var item in Model)
{
<div>
#item.Text
<p>Written by #item.AuthorDisplayName</p>
</div>
}
Now our views are not depending directly to Domain models. This allows us to bring data from another source tomorrow if we need (Ex :Get comments from a web service) and simply map to our view model.
Some notes
Do not add too much of code to Views. Let's keep it pure HTML as much as possible. No data access calls directly from Views!
I manually mapped the domain model to viewmodel for your understanding. You may use a mapping library like Automapper to do so. Also you may move part of the code we have in the GET action method to another servier layer so that it can be reused in multiple places.
I have a model like the followings:
public class MyModel {
[Required]
public string Name { get; set; }
public string Family { get; set; }
[Required]
public int Number { get; set; }
}
So for example in Edit View I have 3 Editorfor() objects and I am interesting to filter the post data of this page, actually I want to ignore Number field and just want to post Name and Family Also I need the validations of Number be active, One way is I remove Number property from MyModel and define in view by hand and write all validation script by own, but I am interesting to know is there any simpler way in MVC. Does anyone have any idea?
Controlling all that validation and model binding manually is way too complicated and error-prone. You should be using ViewModels
public class SomeSpecificViewModel
{
[Required]
public string Name { get; set; }
public string Family { get; set; }
}
public ActionResult SomeSpecificAction(SomeSpecificViewModel model)
{
//...
}
Now MVC wil validate only Name and Family
Any value not filled in the view will not be posted to the controller. However, if a field which is [Required] is not filled, then ViewModel.isValid will be false.
I have an order model (shown below)
public class Order
{
//[Key]
[ScaffoldColumn(false)]
public int OrderId { get; set; }
[DisplayName("Order Date")]
public DateTime OrderDate { get; set; }
public virtual ProductSelection ProductSelection { get; set; }
public virtual ShippingDetails ShippingDetails { get; set; }
public virtual BillingDetails BillingDetails { get; set; }
public virtual CardDetails CardDetails { get; set; }
public virtual AccountUser AccountUsers { get; set; }
}
As you can see is made up of a set of other models for example ProductSelection (shown below).
public class ProductSelection
{
public int SimulatorId { get; set; }
public string VersionNumber { get; set; }
[DisplayName("Quantity")]
public int Quantity { get; set; }
[DisplayName("Total Price")]
[ScaffoldColumn(false)]
public decimal TotalPrice { get; set; }
}
The issue I am having is when I post to the Controller which has a parameter of Order, I am unable to obtain any of the values from the sub-models (for example Order.ProductSelection.SimulatorId.)
Any ideas why this isn't working as I having to currently use FormCollection which isn't ideal and better messy.
Looking forward to replies
Steve
1) Silly question but just to make sure....Do you preserve values of your sub model on the view(In the form as hidden or any other input type,make sure name of your hidden are same as your properties name in the model) or in the query string.
Before giving you fully loaded model, model binder looks at different places to load your model like your form collection,rout data and query string
If you are not preserving them in any of these places then model binder has no way to find those values and give you loaded values on controller action.
Basics.. http://dotnetslackers.com/articles/aspnet/Understanding-ASP-NET-MVC-Model-Binding.aspx
2)Your example model seems fine but make sure all properties of your sub model have public access modifier and they must have set in their property declaration.
--->I had same issue before because I had private access modifier for set on those properties and I wasted whole day to figure that out.
3)If nothing works(hope that's not the case) then at last you can write your own model binder.
Here is the good post if you decide to head in that direction
http://buildstarted.com/2010/09/12/custom-model-binders-in-mvc-3-with-imodelbinder/
This is my first post (under my account) and it feels really good to participate..!!
You should apply ForeignAttribute on the ProductSelection property which points the primary key of the ProductSelection class:
[ForeignKey("SimulatorId")]
public virtual ProductSelection ProductSelection { get; set; }
Hope that helps.
I have a Person model and a student model. The student model has 2 FKs of PersonIDs; one for student and the other for parent.
My view looks like this:
#Html.EditorFor(m => m.student.Person.FirstName)
#Html.EditorFor(m => m.student.Person.DOB)
#Html.EditorFor(m => m.student.Father.FirstName)
The models would look like this:
public partial class Person
{
public int PersonID { get; set; }
[Required]
[PlaceHolder("First Name")]
public string FirstName { get; set; }
[PlaceHolder("Birth Date")]
public Nullable<System.DateTime> DOB { get; set; }
}
public partial class Student
{
public int Student_PersonID { get; set; }
public int Parent_PersonID { get; set; }
}
I want the DOB to be required field for the student but not for the parent. If I add [Required] attribute to the DOB element, then it requires it for both. Is there a way I can set a require a field on the view? or is there a way in the model or using validation attribute to do this?
fyi... i am using EF database first approach
thanks
I would suggest having the view model match the fields that are displayed in the view. If later a field is to be removed from the view, then it will also be removed from the domain model.
In this case, if your view is to display the following fields:
StudentFirstName
StudentDOB
ParentFirstName
ParentDOB
Then I would suggest having the following view:
public class PersonViewModel
{
public int StudentPersonID { get; set; }
[Required]
public string StudentFirstName { get; set; }
[Required]
public DateTime StudentDOB { get; set; }
public int ParentPersonID { get; set; }
[Required]
public string ParentFirstName { get; set; }
public DateTime ParentDOB { get; set; }
}
Or if instead you have 2 seperate views displaying:
StudentFirstName
StudentDOB
AND displaying:
ParentFirstName
ParentDOB
Then I would suggest having 2 seperate view models:
public class StudentViewModel
{
public int StudentPersonID { get; set; }
[Required]
public string StudentFirstName { get; set; }
[Required]
public DateTime StudentDOB { get; set; }
}
public class ParentViewModel
{
public int ParentPersonID { get; set; }
[Required]
public string ParentFirstName { get; set; }
public DateTime ParentDOB { get; set; }
}
Using the view models in this way will allow you to use the [Required] data annotations for the fields that require them rather than trying to create a workaround. Note that the view models are not to be confused with the domain models and therefore this data would then need to be mapped to the domain model.
Hope this helps.
If your application is a simple application you may not need to create a seperate business logic layer and most books only present MVC with simple models which may be fine. However, if you search around you will find other examples where developers recommend having a view model seperate from a business model such as this
I would also recommend reading Wrox Professional Enterprise .Net 2009 where chapters 7 & 8 give great examples of the business layer with discussions of the Transaction Script pattern, Active Record pattern and Domain Model pattern.
One way is to make a PersonRequired class that inherits from Person. Add a metadata class to PersonRequired so you have PersonRequiredMetaData and in that specific that the inherited DOB field is required. You would need to manually copy the values between the Person and PersonRequired classes or use AutoMapper. I hope there is a better answer than this!
Another option is to use FluentValidation that would let you do the validation separate from the model (doesn't use data annotations). I wonder if some people are using data annotations for database requirements and fluent validation for programmatic requirements.