Mvc rules and concepts - asp.net-mvc-4

A view can have only one #model Folder.ModelName but what if I want to show data from two different tables in one view? How should I do this and what is the right way? For example, if I want to select all profits of the current user and to select all costs of the current user in the one view, then obviously this is 2 tables with 2 different models. Basically my question is, where can I find some rules and concepts for the asp.net mvc pattern?

You would create something known as ViewModel. ViewModel can contain one or more entities, methods, additional fields, etc. You then pass this ViewModel to your View. For example,
namespace Sample
{
public class ProfitCostViewModel
{
public Profit Profit { get; set; }
public Cost Cost { get; set; }
public decimal DoSomeCalculations()
{
// Do something
}
}
}
In your Action, create an instance of this ViewModel class and initialize properties Profit and Cost. Then pass this object to the View. Inside of your view your can declare model like:
#model Sample.ProfitCostViewModel
and use it as
<p>Current profit or something: #Model.Profit.SomeProfitProperty</p>
<p>Sample cost: #Model.Profit.SomeCostProperty</p>
And that's how you would pass two or more entities as a model to your view.
UPDATE: You action could be something like:
public ActionResult YourAction()
{
var profitCostVm = new ProfitCostViewModel();
profitCostVm.Profit = LoadProfitFromSomewhere();
profitCostCm.Cost = LoadCostFromSomewhere();
return View(profitCostCm);
}

Related

Saving user information in MVC (Show same data on different views)

I could use some help in order to find the best way of saving data a user selects during a mini booking site.
Right now my booking flow contains of 4 steps (4 views). Each of these 4 views has it's own Model linked.
Lets say that on the first page, I am saving information like departure / return date and so on. That information is stored in a model. How can I the best way pass this information to the other models? I don't think creating a viewmodel containing of all 4 models in the end is a good solution.
What I have tried to do, is to create a class which can hold the data. Before going to the next step, I am saving the data to this class and storing the needed data in the next Model.
The scenario looks like this:
Storing data to choices class using step 1 Model (BookingModel)
Sending necessary data from choices class to new Model (RoutesModel) before showing the view.
Storing data to choices class using step 2 Model (RoutesModel)
Sending necessary data from choices class to new Model (TpaModel) before showing the view.
and so on...
Right now I can save the data to the choices class in step 1, but when I try to send the data to the other Models, it fails because the fields inside choices are empty.
public class Choices
{
public DateTime Departuredate { get; set; }
public DateTime Returndate { get; set; }
}
How I load the data and creating a model for the controller on step 2:
RoutesController (step 2):
// GET: Routes
public ActionResult Index()
{
var model = _routeModelService.CreateRouteModel();
return View(model);
}
Helper class that creates RoutesModel
public class RouteModelService
{
private Choices _Choices;
public RouteModelService()
{
_Choices = new Choices();
}
public RouteModel CreateRouteModel(RouteModel model = null)
{
if (model == null)
{
model = new RouteModel()
{
};
model.DepartureDate = _Choices.Departuredate;
model.ReturnDate = _Choices.Returndate;
}
return model;
}
}
Is it because I create a new instants of choices that its empty?

One controller/view for entities with same properties? (DRY code)

We have quite a few lookup entities that all have the same structure - just ID and Name. For example, Gender, Ethnicity are just dropdown lists on a Patient view. Each lookup entity has views for viewing/adding/editing the values. Each entity has its own controller with nearly identical actions - Index (view list), Create, and Edit. The only thing different is the type.
Is there a way to create one controller and one set of views to manage all of these lookup entities using generics, a base entity, some other technique or a combination of these?
One simple thing you can do is create an Interface for ID and Name. Then inherit it in the models that you need to.
Example
public interface IEntity
{
int Id { get; set; }
string Name { get; set; }
}
Inherit this in your models
public class Gender : IEntity
{
}

Pattern for passing common data to _layout.cshtml in MVC4.5

I am trying to come up with the best pattern for passing data to my _layout.cshtml page.
I am toying with creating a common base class from which all view specific models derive. This base class would be recognized by my _layout.cshtml and used to fill in details about the user and load proper images in the header, etc. For example, here is a snippet of it.
public abstract class ViewModelBase
{
public string Username { get; set; }
public string Version { get; set; }
}
At the top of my _layout.cshtml I have...
#model MyProject.Web.Controllers.ViewModelBase
I need a common area to hydrate the information required by the model, and am planning to use the following pattern...
Each action method creates and hydrates a model derived from
ViewModelBase.
The action completes.
I create a ActionFilterAttribute and override OnActionExecuted to cast the
current Result to ViewModelBase.
If the conversion is successful, then I populate the ViewModelBase details with the relevant data.
Here are my questions...
Is the use of a ActionFilterAttribute (OnActionExecuted) a good pattern for what I am trying to do?
I am not able to see how to get the Result created in the action from the HttpActionExecutedContext. How is this done?
I follow the same approach and use a base ViewModel class which all my other viewModels inherit from.
Then, I have a base controller that all controller inherit from. In there, I have one method that takes care of initializing the view model:
protected T CreateViewModel<T>() where T : ViewModel.BaseViewModel, new()
{
var viewModelT = new T {
HeaderTitle = "Welcome to my domain",
VisitorUsername = this.VisitorUsername,
IsCurrentVisitorAuthenticated = this.IsCurrentVisitorAuthenticated,
//...
};
return viewModelT;
}
Then on each controller, when I want to create the view model, I simply call the base controller's method:
var vm = base.CreateViewModel<MyPageCustomViewModel>();

Same view for both create and edit in MVC4

Can we have a single razor view for both Create and Edit operations?
If yes, how do we achieve this?
I don't recommend it.
This should be a rather long answer, because there's a lot of things involved in the process, request and workflow of a normal MVC GET/POST workflow. I will try to answer your question with the minimum information required and why I do not recommend the use of the same view.
First, why?
You don't have control over the views, which may have over-posting;
No flexibility;
Not reusable views or parts;
Hard to maintain the views (one change in the view must be tested on both actions).
My suggested approach would be to have different actions/views but share common code:
Create both views as normal.
You will have duplicated code, but not all code is the same, for example, you may not want to send an ID on the create action, this is not directly related to your question, but using the same view implies you are also sending the same data, and this is not recommended, especially for over-posting or mass assignment. More info about mass assignment here (an Architectural Approach is what I'm using here).
So let's start from what are you going to receive in your controllers.
In this case I used inheritance but it's not the only strategy.
Binding models
public class UpdateBindingModel : CreateBindingModel {
// since we are not using the same binding model,
// we can have a "real" validation rules on our update binding and view.
[Required]
public int? Id {get;set;}
}
public class CreateBindingModel {
// no id here prevent overposting.
[Required]
public string Name {get;set;}
[Required]
public int? CountryId {get;set;}
}
That will make sure the data you send to your Create and Edit is the minimum needed and nothing else.
Let's then see the View Models that will be sent to the View, for this example I will include a List that will be used to select some value but should not be posted (the list) to the controller, only the selected value.
View models
public class CreateViewModel : CreateBindingModel {
public IEnumerable<SelectListItem> CountryList {get;set;}
}
public class UpdateViewModel : UpdateBindingModel {
public IEnumerable<SelectListItem> CountryList {get;set;}
}
As you can see, this gives you lot of flexibility but still have some duplicated code (the extra information needed on view model for both views) which can be mitigated in several ways (depending the needs/context):
Have an action to retrieve the common data and using #Html.Action("GetCountryList");
Use the same View Model aka CreateUpdateViewModel and discarding extra UpdateBindingModel properties in the view but still posting the corresponding model on POST.
Having your binding models as properties and select one or the other in the specific view. (better use #Html.EditorFor instead of partials so Model Binder will work with no additional change on code)
The controller actions will look like:
Controller
[HttpGet]
public ActionResult Create(){
ViewData.Model = new CreateViewModel();
return View();
}
[HttpPost]
public RedirectToRouteResult Create(CreateBindingModel binding) {
// check valid model state and create data
return RedirectToAction("Index");
}
[HttpGet]
public ActionResult Update(int id) {
var objectToEdit = service.GetObjectToEdit(id);
ViewData.Model = new UpdateViewModel(objectToEdit);
return View();
}
[HttpPost]
public RedirectToRouteResult Update(UpdateBindingModel binding) {
// check valid model state and update data
return RedirectToAction("Index");
}
And your views:
Views
Update.cshtml
<form action="Update">
#Html.HiddenFor(Model.Id);
#Html.Partial("EditFieldsPartial")
<button>delete</button> // no delete button on create.
<button>create new</button> // you can have a create new instead of update.
</form>
Create.cshtml
<form action="Create">
#Html.Partial("EditFieldsPartial")
</form>
Note: code is incomplete and didn't use helpers in most cases for brevity and clarity. Do NOT copy paste :D
Sure you can.
On post, check in your controller whether the primary key has value 0 then Insert, otherwise Update.
View should be the same for Create and Edit.
Just remember to include:
#Html.HiddenFor(model=>model.ID)
In your view
For example:
Model:
public class DescriptionModel
{
[Key]
public int ID { get; set; }
public string Description { get; set; }
}
CreateEdit.cshtml:
#model DescriptionModel
#using (Html.BeginForm("CreateEdit"))
{
#Html.HiddenFor(model=> model.ID)
#Html.EditorFor(model=> model.Description)
<input type="submit" value='Submit' />
}
DescriptionModel controller:
public ActionResult Create()
{
return View("CreateEdit", new DescriptionModel());
}
public ActionResult Edit(int id)
{
return View("CreateEdit", db.DescriptionModels.Find(id));
}
// Submit and add or update database
[HttpPost]
public ActionResult CreateEdit(DescriptionModel model)
{
if (ModelState.IsValid)
{
// No id so we add it to database
if (model.ID <= 0)
{
db.DescriptionModels.Add(model);
}
// Has Id, therefore it's in database so we update
else
{
db.Entry(model).State = EntityState.Modified;
}
db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
A View can definitely be shared for create and edit operations, using the same model. However, i would strongly recommend to think about it twice. In many cases, you will want to have a different view for edit operations(eg. hide some inputs that should not be editible) as well as the model could be slightly different, altought it might share some (or most) values. These difference will lead to some conditions in the view, checking whether you are creating or editing - which could make the code chaotic.
Conclusion: before deciding whether to have a shared view, try to think of how much is the edit screen gonna differ from create screen, then you may decide.
You certainly can, but usually that's something I will try to avoid. If the create and edit actions are virtually the same then you end up duplicating a lot of code in the controller. Usually in this situation I will have only a few fields on my 'Add' controller, and then once the item has been added I redirect the user to the edit page where they can fill in the rest of the information.
I wouldn't recommend that approach but you could have the main form be loaded into both views from a partial
[HttpGet]
public ActionResult myFun(int id = 0)
{
MyClass cls = new MyClass();
if (id == 0)
{
//Insert mode ... no data will be shown to textboxes , when primary key ie. id=0
//Display whole data
}
else
{
//Update mode... if id is not 0 ,data will be shown to textboxes
}
return View(cls);
}

Data Annotation for a model consisting of multiple tables in database

I have a form displaying fields from 2 tables in database.
I want to validate this form using Data Annotation model validation technique in MVC4.
I am confused whether Data Annotation validation attributes should be applied to individual models(tables) or should I create separate model consisting of fields from both tables.
You should create a new model containing the information you want to display on your view. This specific model has a name: it's called view model. You should always pass view models to your views from the controller actions and your controller actions should always take view models as parameters. Then you could decorate the properties on this view model with validation attributes.
You can do both things. You can decorate your domain model classes and view models. Both things are correct. The thing is that for your view, it's better to use a view model instead of model classes. No need to expose the whole domain model in you view
Just add using System.ComponentModel.DataAnnotations;namespace and add the attributes you need on each property.
For example:
public class BankAccount
{
[Required]
public Person Person { get; set; }
[Required]
public AccountType AccountType { get; set; }
[Required, StringLength(256)]
public string BankName { get; set; }
}