use properties and metadata on base class in different namespace - c#

I have a separate Visual Studio project where I keep my data model (EF6). So, my entities are in namespace Name1 (created by EF6 database first, but simplified below, for this example):
namespace Name1
{
public class Person
{
public string FName {get; set;}
public string LName {get; set;}
}
}
Now, I have created a new MVC 5 project, which references the data Visual Studio project so that I can access the entities. In my MVC project, I want to add some metadata to the entity like this:
namespace NameMvc
{
[MetadataType(typeof(PersonMetaData))]
public class Person : Name1.Person
{
}
public class PersonMetaData
{
[Display(Name = "Firstname")]
public string FName;
}
}
In my controller I want to get all the persons, so I have an Action like this:
using Name1;
using NameMvc;
-- controller class code
public ActionResult Index()
{
var persons = db.Person.ToList();
return View(persons);
}
-- controller class code
And in my view I try to access that via:
#model IEnumerable<NameMvc.Person>
Now, when I run the code I get an error:
The model item passed into the dictionary is of type 'System.Data.Entity.Infrastructure.DbQuery`1[Name1.Person]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[Name.MvcPerson]'.
I probably did something wrong in the action, because db.Person comes from the Name1 namespace.
I want to use the metadata in my View so that when I do something like this #Html.DisplayNameFor(model => model.FName), it displays "Firstname".

You are getting an error rendering your view because you are returning IEnumerable<Name1.Person> from your action, but your view is expecting IEnumerable<NameMvc.Person>. When you are using a strongly-typed model in your view, it must match the model returned from the action.
There are two possible solutions for this error:
Change your view to use IEnumerable<Name1.Person> as its model, or
Change your action to return an IEnumerable<NameMvc.Person> as the model.
When you use MetadataType to add metadata to your model, the most common way is to use the fact that the original model class is generated as a partial class. You create another "part" to the partial class and add the MetadataType attribute to it.
However, partial classes cannot be used across assemblies. So this means that if you move your model to its own assembly, then you cannot add a partial class to your MVC project to add the metadata.
To solve this, you can do one of the following:
Add your metadata in your model's assembly. To do this, you use the partial class solution in your model's assembly. The problem with this is that you're now mixing view logic with data logic.
Create a new class in your MVC project which is a pseudo-copy of your data model to act as a view model. You add your metadata to that. Your action will return this view model and your view will use that. In your action, you copy the data from your data model to your view model.
I prefer option #2 for a couple of reasons:
It solves the problem you're facing, and
How data is presented to the user is often different than how I want it represented in my databases. This mapping allows me to handle that nicely.
The drawback to option #2 is the repetitive copying of data. However you could use tools like AutoMapper to simplify the data copying.

Related

Why Model is having only declaration in MVC?

I am new to MVC. According to MVC tutorial, Model are the classes which contains business logic. But in all the example which i referred, Model contains only the declaration (using interface). Why the Model cannot contain definition of business logic. Since i compared with MVVM model, where Model contains definition.
Why model look like this?
public interface IDBModel
{
void addRecord();
void deleteRecord();
}
Instead of like below.,
public Class DBModel
{
void addRecord()
{
// Insert logic
}
void deleteRecord()
{
// Delete logic
}
}
Kindly help me to understand the "Model" purpose in MVC and MVVM with some real time examples.
A model is meant to encapsulate data, making it easier to transfer from different logical areas of your application. The first example you give is incorrect, in that you're defining an interface with methods. You're more likely to see a model that looks like this:
public class Person {
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName() {
return string.Format("{0} {1}", FirstName, LastName);
}
}
Notice that I'm using properties as a way to transfer data, but have a method that performs lightweight logic (this could have also been done as a read only property). 90% of the time this is what your models will look like.
I would treat the M in MVC more like a view model. It contains all properties and formatting logic needed for the view to display itself. No need to have interfaces for it.
The controller is responsible for building that view model based on the models it receives from the services.
I think you misunderstood about the Model.
Worng: Model are the classes which contains business logic (not the business logic).
Models: Model objects are the parts of the application that implement the logic for the application's data domain. Often, model objects retrieve and store model state in a database. For example, a Product object might retrieve information from a database, operate on it, and then write updated information back to a Products table in SQL Server.
Take a look at official ASP.NET MVC Site.
Why model look like this?
Your application may follow certain different patters. other than MVVM.
Real Time Examples / Basic Understandings : Click Here

How should I be binding my business models to my views?

I've come across an interesting question during my development. Right now, I use a database-agnostic unit of work layer to abstract the access of data from the actual database dependencies in my ASP MVC 4 web application.
Each individual database project which implements the unit of work interfaces are aware of my business models (models that go directly to/from the database). I'm not too sure how I feel about this approach, but that's not the question I am going to ask.
Should I be using a solution like AutoMapper to convert my business models to/from domain models - models that are passed to the view and used for any work that shouldn't have access to database fields (i.e., IDs)?
For example, consider inside my BusinessModels project, I have the following classes
BusinessModels
/UserAccounts/
User.cs
- ID
- Username
- HashedPassword
- Salt
UserSettings.cs
- IsSubscribedToNewsletter
- AllowDirectEmails
Would it make any sense to bind these User, and UserSettings models into a single model using AutoMapper like so
MyProject
/DomainModels/
User.cs
- Username
- HashedPassword
- Salt
- IsSubscribedToNewsletter
- AllowDirectEmails
for the purpose of views?
This question also extends to non-MVC projects but I feel seeing as I am working on an MVC project it would make more sense to ask it in that tag.
TLDR is there any point in mapping business models/entities to view models or does that provide an unnecessary layer of abstraction? And if so, would the Repositories contain business models, or view models (which map automatically to business models under-the-hood)?
You can use view models for two different things:
rendering a new view (GET action), passing the view model object as the model for the view
receiving data back from the view, in a Post Action (POST action), using the view model as parameter
(I know, the second is arguable. But it's not strange to use the view models for this)
The model for the GET action needs all the properties neccessary to render the View:
the values of the entity you're showing/editing
extra values needed to render the view (for example SelectLists for drop down lists)
Suppose that you have a User which can belong to one UserGroup.
In this case, if you want to edit the user, the model needs:
the User data
a list of UserGroups
I'd use a model like this:
public class EditUserModel
{
public User User {get;set;}
public SelectList UserGroups {get;set;}
}
As you can see, I directly add the User as a property. But I don't add the list of categories as a property, because I don't need the whole list of categories, with all their properties in the view. Besides, if you unit test the controller you can verify that the SelectList is as expected (that couldn't be done if you created the User Groups list in the view)
But, what if you don't need all the properties of the user in the View? Is it worth removing the User property, and add individual properties for Name, Email, JoinedData, Active... ? I think the anser is NO. Imagine you add/remove or rename some of the User entity properties. If you had individual properties in the view model, you'd have to change them as well, before updating the View. And, if you rely on automatic mapping (auto mapper, value injecter) you would't even realized if you make some mistake.
I also said that the view model can be used for posting back data to the controller. So you could do this:
[HttpPost]
public ActionResult Edit(EditUserModel userModel)
If you do so, the model binder will populate the userModel with the values in the form controls. So you'lll get back a half empty model. In this case, the UserGroups list would be null, and, depending on how many of the User's properties you edit, the User could also have many null/non-initialized properties.
To avoid making errors, in some occasions is advisable to create a different model (and probably auxiliary classes) to make it clear what is expected to be posted to the model.
For example, if you have an action to show the whole user data, but which only allows to change its password, you could create a class with two properties: Password, and PasswordConfirmation.
In this case, the view model for the POST could only have the Password and PasswordConfirmation. And derive a model for the GET which has this inherited properties, and also the list of User Groups and the User.
Why inheriting and not using independent classes? Simply beacuse when you use something like Html.TextBoxFor(m => m.User.Name), the Model Binder will be able to set the Name property of the User property, only if the parameter for the post action has the same structure. I.e. if the view model for the get has this structure:
public ChangePasswordModel
{
public string Password {get;set;}
public string PasswordConfirmation {get;set;}
// extra properties, like the list of user groups, the user data...
}
And the model for the post has this structure:
public PostedChanegPasswordModel
{
public User User {get;set;}
}
The content of the input rendered by Html.TextBoxFor(m => m.EditedUser.Name) won't be bound to the User.Name of the PostedEditViewModel.
But if you make this:
public EditUserModel : PostedEditUserModel
{
// extra properties, like the list of user groups
}
the data will be bound without any problem.
In general you have to be careful with the models you use for posting and getting. And I recommend using as many different view models as neccesary.
When to use automatic property mapping to completely new view and different models?
You must have a very strong reason to have different view models. This could be a result of starting the application from outside in (i.e. design first) or because a team is developing the UI before or while the business logie is being implemented.
In this case you can find that the classes for the view models, and the views themselves are already defined, and are quite similart to your entities, but not exactly equal. This is one case when I think it can be good to use mappers.
The other reason to use different classes would be to decouple the interface from the logic. But this usually only happens in the previous case.
Regards viewmodels I treat them like a summary of the data you wish to work with.
So taking from your example, your viewmodel would contain data from both the User and UserSettings classes. Let's say you had a view named UserData.cshtml then I would code it up like so:
public class UserDataViewModel
{
public string Username { get; set; }
public bool AllowDirectEmails { get; set; }
// etc ...
}
public ActionResult UserData()
{
var viewModel = new UserDataViewModel();
viewModel.UserName = "Whatever";
viewModel.AllowDirectEmails = false;
// Or however you get the data for the user.....
return View(viewModel)
}
Hopefully you get the idea. So you are on the right track with merging information from externals classes into one viewmodel class. Bascially tie everything together in the viewmodel class.
I name the viewmodel class the same as the view that it's going to be used for. This can help documentation, as well as make it easier for devs new to the code to follow.

How could I make my View Model

I have a partial view that contains my form input(textboxes). I have 2 other partial views that use this same form. One for adding a product and one for editing a product.
This form uses a view model(Lets call it CoreViewModel). Now editing product has a couple more fields then adding a product.
I am wondering how can I add these extra fields without them showing up on an add product form?
I cannot add these extra fields to the edit product view they must be in the CoreViewModel otherwise I think styling it will be a nightmare.
I was thinking of having maybe a base class and then for the editing. I would send it a view model that inherits this base class.
Check in the view if the View Model is of this inherited class and not a base class and if it is not a base class render the code.
This way I am not sticking the edit specific code into my CoreViewModel that both the add view and the edit view have access.
I hope this sort of makes sense.
Thanks
Edit
Using Muhammad Adeel Zahid code as I base I think I got it to work
public class CreateViewModel
{
......
......
}
public class EditViewModel:CreateViewModel{
public string AdditionalProperty1{get;set;}
public string AdditionalProperty2{get;set;}
}
Controller
EditViewModel viewModel = new EditViewModel();
// add all properties need
// cast it to base
return PartialView("MyEditView", (CreateViewModel)viewModel);
View 1
#Model CreateViewModel
#using (Html.BeginForm())
{
#Html.Partial("Form", Model)
}
Form View
#Model CreateViewModel
// all properties from CreateView are in here
// try and do a safe case back to an EditViewModel
#{EditViewModel edit = Model as EditViewModel ;}
// if edit is null then must be using this form to create. If it is not null then it is an edit
#if (edit != null)
{ // pass in the view model and in this view all specific controls for the edit view will be generated. You will also have intellisense.
#Html.Partial("EditView",edit)
}
When you post it back to your Edit action result just take in the EditViewModel and cast it back to your base. Then you will have all the properties as it seems to work
I have often read people advising against such things. They often urge having viewmodel per view (even for edit and create view of same entity for that matter). Again, it all comes down to what you are doing. You may have different data annotations on edit and create view for different validation needs but if they are the same, we probably have a point to use same viewmodel for create and edit.
To solve your scenario I can't figure out couple of options. First, keep a boolean property in your view model telling you if it is and edit or create and conditionally render you properties on view
public class MyViewModel
{
public string P1{get;set;}
....
public boolean Editing{get;set;}
}
Set Editing property to false in Create ActionResult and to true in Edit ActionReult. This is the simplest method. The second one is little dirtier, but you will feel like using the technology. You can use dynamic behavior of c# 4.0. have your page inherit from dynamic in iherits directive of the page (I use aspx view engine). Then have a create ViewModel:
public class CreateViewModel
{
......
......
}
and one Edit ViewModel
public class EditViewModel:CreateViewModel{
public string AdditionalProperty1{get;set;}
public string AdditionalProperty2{get;set;}
}
and in your view you can do something like:
<%:if(Model.GetType().Name.ToString() == "EditViewModel"){%>
<%:Html.Textbox("AdditionalProperty1")%>
<%:Html.Textbox("AdditionalProperty1")%>
<%}>
There is a price to pay with dynamic. You lose intellisense and you can't use strongly typed helpers (at least in asp.net MVC 2).

ASP.Net MVC 2 Omitting Properties When Autogenerating View Content For Strongly-Typed View

This is a simple question but I couldn't find an answer for it online. I have a class that looks like this:
public class EquipmentItem : Item
{
public Status Status { get; set; }
public string Description { get; set; }
}
When I create a strongly-typed view and provide this class, asking ASP.Net MVC to automatically make a view populated with "create" content it leaves out all of the properties in the Status class. Is there a way for me to tell ASP.Net MVC to also provide HTML input fields for the properties in that class?
Thanks
you can also do
Html.EditorFor(m=>m.Status);
I always take it as a sign that you should create a partial view when you have such properties in your view model.
In any case, creating the partial view will allow you to scaffold the properties of your Status class as it would be strongly typed to status, instead of EquipmentItem.
The alternative is to use mapping (eg, AutoMapper), and map your domain model class to your view model class. The view model class would just have simple properties that MVC will scaffold for you.

ASP.NET MVC: Populating Derived Strongly Typed View with Base Strongly Typed View

In my application I have different pages: Contact Us, About Us, Home
They all have the same base elements that needs to populate the view:
Page Title
Meta Description
User Information
However on each page, they have some elements that are different:
Contact Us
Contact Information Model
Contact Form Model
About Us
Extended User Information Model
Home
Home Page Text Property
They are all routed to the same Controller Action Method because most of the functionality is similar other than populating the "extra" information dependent on page type.
So far I have done something where:
PageDetailViewData pageDetailViewData = new PageDetailViewData {Title = title, Desc = desc....}
and following this I have:
switch ((PageType)page.PageType)
{
case (PageType.Contact):
return View("ContactUsDetails", pageDetailViewData);
default:
return View(pageDetailViewData);
}
The question is how do I populate the "extra" information? I am not sure if I am going about doing this the right way. Any insight to better structure the logic flow would be appreciated.
The answer of using interfaces to imply some commonality between your view models is certainly going to help to answer some of the points in your questions.
I would however ask how wise it is to "refactor" your Action to support multiple views of differing data structures.
MVC controller actions typically represent the minimum amount of code required to gather the specific data required to generate the intended view. It's not completely uncommon for a single action to return different views of the same model data (Html view or Mobile view for example) but by varying both the structure of the data and view that will generated you introduce a few problems.
In particular you violate common best practices like the Single Responsibility Principle and make your code much more complicated to test - and Pain free testing and TDD are part of the big win with ASP.Net MVC after all.
Personally I would have a separate Action.
As far as your view models are concerned, how would you do it if this was a database?
You would have separate queries for separate data right?
A user's profile information would be queried separately from the page meta data information. This would be done for a number of reasons that could include the ability to cache certain parts of the data but not others for example.
So with the above suggestions your code might look like this (Warning: this code wasn't written in Visual Studio and is probably full of syntax issues):
public interface IMetaDataViewModel
{
PageMetaData MetaData{get; set;}
}
public class HomeViewModel : IMetaDataViewModel
{
public PageMetaData MetaData{get; set;}
public string HomePageText{get; set;}
}
//other view models go here....
public class CommonPagesController : Controller
{
private MetaDataProvider _metaProvider = new MetaDataProvider();
private PageDataProvider _pageDataProvider = new PageDataProvider();
private ContactDataProvider _contactDataProvider = new ContactDataProvider();
public ActionResult Home()
{
var viewModel = new HomeViewModel
{
MetaData = _metaProvider.GetPageMeta();
HomePageText = _pageDataProvider.GetPageData();
};
return View(viewModel);
}
public ActionResult Contact()
{
var viewModel = new ContactViewModel
{
MetaData = _metaProvider.GetPageMeta();
ContactFormData = _contactDataProvider.GetData();
};
return View(viewModel);
}
//you get the picture...
}
There are several ways you could also refactor out the generation of the view model code but thats one possible pattern.
I appreciate that this answer does have a certain amount of opinion in it but I would consider having separate actions to be best practice.
Hope that helps.
The title of your question almost gives you the answer. You can use some form of polymorphism to accomplish this. You could define a base class with the shared properties, or alternatively an interface like this:
public interface ICommonPage
{
string Title { get; }
string MetaDescription { get; }
string UserInformation { get; }
}
Then define three strongly typed ViewModel classes that all implement this interface (or derive from the base class):
ContactUsViewModel : ICommonPage
AboutUsViewModel : ICommonPage
HomeViewModel : ICommonPage
On each of those ViewModel classes, you add the extra properties that you need for those Views.
In your Controller Action, you will need to switch on PageType to select the correct ViewModel and populate it with data.
You will also need to creat three different Views (.aspx) that are strongly typed to each ViewModel class.
If you have shared rendering for the common data, you can extract that into a strongly typed UserControl (.ascx) that is typed to ICommonPage.

Categories

Resources