How do I bypass the limitations of what MVC-CORE controllers can pass to the view? - asp.net-core-mvc

From what I've read, I'm supposed to be using ViewModels to populate my views in MVC, rather than the model directly. This should allow me to pass not just the contents of the model, but also other information such as login state, etc. to the view instead of using ViewBag or ViewData. I've followed the tutorials and I've had both a model and a viewmodel successfully sent to the view. The original problem I had was that I needed a paginated view, which is simple to do when passing a model alone, but becomes difficult when passing a viewmodel.
With a model of
public class Instructor {
public string forename { get; set; }
public string surname { get; set; }
}
and a viewmodel of
public class InstructorVM {
public Instructor Instructors { get; set; }
public string LoggedIn { get; set; }
}
I can create a paginated list of the instructors using the pure model Instructor but I can't pass InstructorVM to the view and paginate it as there are other properties that aren't required in the pagination LoggedIn cause issues. If I pass InstructorVM.Instructors to the view, I get the pagination, but don't get the LoggedIn and as this is just the model, I may has well have passed that through directly.
An alternative that was suggested was to convert/expand the viewmodel into a list or somesuch which would produce an object like this that gets passed to the view
instructor.forename = "dave", instructor.surname = "smith", LoggedIn="Hello brian"
instructor.forename = "alan", instructor.surname = "jones", LoggedIn="Hello brian"
instructor.forename = "paul", instructor.surname = "barns", LoggedIn="Hello brian"
where the LoggedIn value is repeated in every row and then retrieved in the row using Model[0].LoggedIn
Obviously, this problem is caused because you can only pass one object back from a method, either Instructor, InstructorVM, List<InstructorVM>, etc.
I'm trying to find out the best option to give me pagination (on part of the returned object) from a viewmodel while not replicating everything else in the viewmodel.
One suggestion was to use a JavaScript framework like React/Angular to break up the page into a more MVVM way of doing things, the problem with that being that despite looking for suggestions and reading 1001 "Best JS framework" lists via Google, they all assume I have already learned all of the frameworks and can thus pick the most suitable one from the options available.
When all I want to do is show a string and a paginated list from a viewmodel on a view. At this point I don't care how, I don't care if I have to learn a JS framework or if I can do it just using MVC core, but can someone tell me how to do this thing I could do quite simply in ASP.NET? If it's "use a JS framework" which one?
Thanks

I'm not exactly sure what the difficulty is here, as pagination and using a view model aren't factors that play on one another. Pagination is all about selecting a subset of items from a data store, which happens entirely in your initial query. For example, whereas you might originally have done something like:
var widgets = db.Widgets.ToList();
Instead you would do something like:
var widgets = db.Widgets.Skip((pageNumber - 1) * itemsPerPage).Take(itemsPerPage).ToList();
Using a view model is just a layer on top of this, where you then just map the queried data, no matter what it is onto instances of your view model:
var widgetViewModels = widgets.Select(w => new WidgetViewModel
{
...
});
If you're using a library like PagedList or similar, this behavior may not be immediately obvious, since the default implementation depends on having access to the queryset (in order to do the skip/take logic for you). However, PagedList, for example has StaticPagedList which allows you to create an IPagedList instance with an existing dataset:
var pagedWidgets = new StaticPagedList<WidgetViewModel>(widgetViewModels, pageNumber, itemsPerPage, totalItems);
There, the only part you'd be missing is totalItems, which is going to require an additional count query on the unfiltered queryset.
If you're using a different library, there should be some sort of similar functionality available. You'll just need to confer with the documentation.

Related

Understanding relations between model view and controller

I went through a lot of readings about MVC and what each of these does is more or less clear. What I haven't understood yet is how they relate. I mean, I know about these relationships
but how are they implemented? What happens in an MVC framework?
I also have a few questions:
I read that a view can't be coupled with the controller, in other words it can't have a controller object inside, but then how does it use the proper controller if a view is supposed to trigger something in it?
How can the model update the view if its unique job is to represent data?
Is the business logic inside the controller or the model? I have read conflicting points of view
The most basic explination of MVC would be that you have each of the 3 layers.
Model
This contains your data. i.e database or set of classes.
View
This displays data to the user i.e your HTML page.
Contains controls for user interaction.
Controller
All access to data should go through this layer. i.e load data from your data source(model) and save data to your data source.
Carries out any data manipulation before saving or loading.
This create a separation of concerns theoretically allowing you to change anything in either layer without the other layer knowing or caring making for far more maintainable and readable code.
In practice this can become more complicated depending on how you wish to access data and display it although the basic principles still apply, occasionally meaning that each part of MVC pattern could be made up of smaller parts.
In terms of implementing it a good example would be ASP.Net MVC http://www.asp.net/mvc. the following could be a simple implementation of displaying some data via MVC using C#.
Model (C# class)
public class Person{
public string FirstName { get; set; }
public string LastName { get; set; }
}
Controller
public ActionResult Index(){
return View(new Person() { FirstName = "Person", LastName = "1" });
}
View (Index.cshtml)
#model Person
Full name: #Html.Raw(Model.FirstName + " " + Model.LastName)
This would output onto the web page
Full name : Person 1
Please forgive me for any syntax errors, not tested.
More detailed post: http://www.tutorialspoint.com/design_pattern/mvc_pattern.htm

Razor partial view prefix field names

When using Razor to render a form for a complex Model that has sub-models, we'd usually use Partial Views to render the sub-models.
Simple example Model:
public class BlogPost
{
public string Content { get; set; }
public List<Comment> Comments { get; set; }
}
public class Comment
{
public string Content { get; set; }
}
BlogPost.cshtml:
#model BlogPost
#Html.TextAreaFor(x => x.Content)
#for (int i = 0; i < Model.Comments.Count; i++)
{
#Html.Partial('Comment', Model.Comments[i])
}
Comment.cshtml:
#model Comment
#Html.TextAreaFor(x => x.Content)
Now for the issue:
Say we want to send the values of all fields to a controller action that takes BlogPost as a parameter. The fields are going to be posted back to the controller like so:
Content=The+content+of+the+BlogPost&Content=The+first+Comment&Content=The+second+Comment
But what we need to have MVC map them correctly to the BlogPost view model, we need this naming convention:
Content=The+content+of+the+BlogPost&Comments[0].Content=The+first+Comment&Comments[1].Content=The+second+Comment
How can this be achieved in a clean way? We can only think of two ways which both seem to compromise the design:
Either we pass the BlogPost as a model to the partial view such that we can define the text area like so: #Html.TextAreaFor(x => x.Comments[i].Content). But this means we couple the partial view for comments to the parent view model - you could think of a scenario where the same partial view should be used in a different context, which is not possible if the partial view depends on the parent view model. Futhermore, the i would have to be passed to the partial view somehow.
Or we fall back to explicitely defining the name of every single field with strings: #Html.TextArea(ViewBag.Prefix + ".Content").
Is there any way to tell the partial view to apply a certain prefix to all field names?
chiccodoro,
if you create and EditorFor template of type Comment, mvc will handle all of this beautifully for you. However, that will only work well in a scenario where the rows are already present in the DB. Exampel from SO:
Submiting Parent & Children in razor
If you need to create new rows on the fly, then you'll have to use a little bit of trickery to allow the fields to operate as required. I used an article from steven sandersons website which allows you to add collection items at runtime and still retains unobtrusive validation etc. see this SO question and related article ref:
Editing a Variable Length List, ASP.NET MVC 3 Style with Table

Model binding in controller when form is posted - why to use view model instead of class from domain model?

I'm still reasonably new to ASP.NET MVC 3. I have come across view models and their use for passing data from a controller to the view. In my recent question on model binding two experts suggested that I should use view models for model binding as well.
This is something I haven't come across before. But both guys have assured me that it is best practise. Could someone maybe shed some light on the reasons why view models are more suitable for model binding?
Here is an example situation: I have a simple class in my domain model.
public class TestParent
{
public int TestParentID { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
}
And this is my controller:
public class TestController : Controller
{
private EFDbTestParentRepository testParentRepository = new EFDbTestParentRepository();
private EFDbTestChildRepository testChildRepository = new EFDbTestChildRepository();
public ActionResult ListParents()
{
return View(testParentRepository.TestParents);
}
public ViewResult EditParent(int testParentID)
{
return View(testParentRepository.TestParents.First(tp => tp.TestParentID == testParentID));
}
[HttpPost]
public ActionResult EditParent(TestParent testParent)
{
if (ModelState.IsValid)
{
testParentRepository.SaveTestParent(testParent);
TempData["message"] = string.Format("Changes to test parents have been saved: {0} (ID = {1})",
testParent.Name,
testParent.TestParentID);
return RedirectToAction("ListParents");
}
// something wrong with the data values
return View(testParent);
}
}
So in the third action method which gets invoked when an HTTP POST arrives I used TestParent for model binding. This felt quite convenient because the browser page that generates the HTTP POST request contains input fields for all properties of TestParent. And I actually thought that's the way the templates that Visual Studio provides for CRUD operations work as well.
However the recommendation that I got was that the signature of the third action method should read public ActionResult EditParent(TestParentViewModel viewModel).
It sounds appealing at first, but as your models and view actions get increasingly complex, you start to see the value of using ViewModels for (most) everything, especially input scenarios.
Case 1 - Most web frameworks are susceptible to over-posting. If you are binding straight to your domain model, it is very possible to over-post data and maliciously change something not belonging to the user. I find it cleaner to bind to an input view model than have long string lists of white lists or black lists, although there are some other interesting ways with binding to an interface.
Case 2 - As your input grows in complexity, you'll run into times when you need to submit and validate fields not directly in the domain model ('I Agree' checkboxes, etc)
Case 3 - More of a personal thing, but I find model binding to relational domain objects to be a giant pain at times. Easier to link them up in AutoMapper than deal with MVC's modelbinder for complicated object graphs. MVC's html helpers also work more smoothly against primitive types than deep relational models.
The negatives of using ViewModels is that it isn't very DRY.
So the moral of the story is, binding to domain models can be a viable solution for simple things, but as the complexity increases, it becomes easier to have a separate view model and then map between the two.

Few questions... ModelState.IsValid and Grouped CheckBox Values

Using ASP.NET MVC when I create my model, then a controller based on the model with CRUD operations, the CRUD views are generated. I added some code using Fluent API to require certain fields but for some reason the ModelState.IsValid passes even when these fields are not completed. What determines whether this passes or not? I thought it was based on your model property data types and other things like being required or maxlength, etc....
Also, I have manually added code to grab a list of Categories from the database and generate a checkbox for each one in the View. This is a navigation property for the Project model where there is a many-many relationship. To get the group of checked values in the Create(Project project) method in the controller I use:
var selected = Request["categories"].Split(',');
This however, throws the classic Object reference not set to an instance of an object error if no values are checked. So what I want to know is, how can I determine that this does not have any values so I can do something else once detected?
I added some code using Fluent API to require certain fields but for
some reason the ModelState.IsValid passes even when these fields are
not completed.
ASP.NET MVC doesn't know anything about the Fluent API of Entity Framework and doesn't evaluate this configuration. You only can use the data annotations which MVC will recognize:
[Required]
public string SomeProperty { get; set; }
...how can I determine that this does not have any values so I can do
something else once detected?
Not sure if I understand it correctly but I'd say:
var categories = Request["categories"];
if (categories != null)
{
var selected = categories.Split(',');
// ...
}
else
{
// do something else
}

How to update tables with a many-to-many join with a bridging table that has payload

I am building a personal Movie Catalogue and have the following structure:
Movie table/entity
MovieID (PK identifier) +
Other movie related properties
Person table/entity
PersonID (PK identifier) +
Other person related properties.
PersonMovie table/entity
MovieID (FK)
PersonID (FK)
Other columns containing information about what the person did on the movie (I.e. charactor name or job).
I want to have a view that allows a user to create/update a movie, or a person, and have a checkbox to then allow them to select existing or create new cast members (persons), or movies.
I am struggling on two fronts:
1) how to present this type of multi-page data collection. A movie has many cast members & a person can be involved in many movies.
2) how to update 2 or 3 of the tables above depending on what the user whats to enter. A user may want to add a movie but doesnt know the cast members yet or vice versa. A user may want to add a movie and add people who already exist as cast members of the movie.
Also I do not want cascading deletes and have struggled switching it off for the relationships between the above entities.
I can do this easily with webforms but am learning MVC 3 & Entity Framework 4 and am still getting my head around it all. I have looked around and haven't come across solutions/tutorials on what I would like to achieve.
Any help would be much appreciated.
Tony
I had a similar issue when I switched from another MVC framework (Rails as in ROR). For the starters, check out Leniency's reply on the similar question, that is; relationship-with-payload or non-PJT (pure-join-table) which unfortunately ASP.NET MVC3 doesn't support explicitly.
You can create a ModelView (a virtual entity) to wrap the collections of other entity types and pass it to the View. The aforementioned post has the detailed example with the code for Model, ViewModel, View, Partial and the Controller. (read both the answers on that post, my answer is continuation of Leniency's answer there)
Hope it helps!
Vulcan's on the right track, and my response that she linked too will help you get the model setup where the linking table contains extra data.
For building the views, you'll mostly likely find that ViewModels are the way to go for more complicated setup like you're describing, then your controller and service layer will deal with processing the view model data and translating it into EF entities. Viewmodels are built specifically to the view that you need, rather than trying to hammer a domain model into a view that may not fit it.
Here's a very rough start for one of the workflows for creating a movie, with an optional list of people.
Domain - your Movie and Person class, plus a linking table similar to what I described here.
View Models - Create a movie and attach people to it
public class MovieCreatePage
{
public MovieInput Input { get; set; } // Form field data...
public IEnumerable<People> People { get; set; } // list of people for drop downs
// ... other view data needed ...
}
public class MovieInput
{
[Required, StringLength(100)]
public string Name { get; set; }
// Easiest to just submit a list of Ids rather than domain objects.
// During the View Model -> Domain Model mapping, there you inflate them.
public int[] PeopleIds { get; set; }
// ... other input fields ...
}
Create.cshtml - just make a form for your view model.
Controller:
// NOTE! The parameter name here matches the property name from the view model!
// Post values will come across as 'Input.Name', 'Input.Year', etc...
// Naming the parameter the same as the view model property name will allow
// model binding. Otherwise, you'll need an attribute: [Bind(Prefix=".....")]
[HttpPost]
public ActionResult Create(MovieInput input)
{
if (ModelState.IsValid)
{
//
// Now do your mapping - I'd suggest Automapper to help automate it,
// but for simplicity, lets just do it manually for now.
var movie = new Movie
{
Name = input.Name,
Actors = input.PeopleIds != null
? input.PeopleIds.Select(id => new Person { Id = id })
: null
};
//
// Now save to database. Usually in a service layer, but again,
// here for simplicity
// First, attach the actors as stubbed entities
if (movie.Actors != null)
{
foreach (var actor in movie.Actors)
_db.People.Attach(actor); // Attach as unmodified entities
}
_db.Movies.Add(movie);
_db.SaveChanges();
TempData["Message"] = "Success!"; // Save a notice for a successful action
return RedirectToAction("Index");
}
// Validation failed, display form again.
return View(new MovieCreatePage
{
Input = input,
// ... etc ...
});
}
Hopefully this helps you some and points you in a good direction. It does, of course, bring up a lot of other questions that will just take time (ie, automapper, service layers, all the various EF gotcha's, etc...).

Resources