I have a use case where I used different models for the GET and POST actions in my controller. This works great for my view, because most of the data goes into labels. The model for the GET method contains 10 properties, but the model for the POST method only needs 3.
This GET view renders a form, which only needs 3 of these properties, not all 10. Thus, the model for the POST method accepts a model class which contains only these 3 properties. Therefore, the ASP.Net MVC model binder populates the model class parameter on my POST method with only these 3 necessary properties, and all is well.
Here's the question: When I encounter some business rule violation in the POST method, and want to use ModelState.AddModelError, and re-display the original view, I no longer have the 7 properties that were not POSTed, as they were not part of the form, and are not part of the model class which this method takes as its parameter.
Currently, I'm calling into a builder to return an instance of the model class for the POST method, and have the GET method itself delegating to the same builder. So, in these cases, when there is some business rule violation in the POST method, I return a View("OriginalGetView", originalGetModel). How can I use ModelState.AddModelError in this case, in the POST method, if I want to send custom messages back to the view, using a completely different model class?
It seemed way too lazy to use the same model class for both the GET and POST methods, given that their needs were so different. What is the best practice here? I see a lot of people recommending to use the same model for both methods, and to POST all of the fields back from hidden form fields, but that just seems like a waste of bandwidth in the majority of cases, and it feels ugly to be sending things like "VendorName" back to the server, when I already have "VendorId".
I may be misunderstanding what you are trying to do, but make sure you aren't being penny-wise and pound foolish. I see you may only want to post the identifiers and not necessarily the descriptors. But it sounds like you have to re-display the view after posting...if so you can just access the model properties if you post the same model that is in the get. If you only post the identifiers, you have to spend time re-accessing the database to get the description values(i.e. vendorname as you describe) using the vendor id no? Wouldn't that also be extra processing? Like I said, I could be misunderstanding your post, but for consistency using the same view model for your view to get and post makes the most sense to me.
Hidden Inputs maybe the best solution here still I think, even on 2g you shouldn't create any lag unless unless the values of your Model properties are long strings or something encrypted or xml.
So your .cshtml would have this in it for the 4 properties not currently included in the form:
<form>
#Html.HiddenFor(m => m.Property1)
#Html.HiddenFor(m => m.Property2)
#Html.HiddenFor(m => m.Property3)
#Html.HiddenFor(m => m.Property4)
But you could also get the model state errors from the original posted model and recreate the ModelError state in your response model to get around using hidden inputs.
I just found this guide (not the answer with Green Checkmark but the highest upped Answer: ASP.NET MVC - How to Preserve ModelState Errors Across RedirectToAction?
Note: if you need to copy model properties from Model to another Model (of the same type or different type), in a cleaner way, check out AutoMapper.
Perhaps this could help with what you were trying to achieve - 'Model' level errors - which wouldn't need to attach to a specific field/property - but can be displayed in a Global area.
https://stackoverflow.com/a/53716648/10257093
Related
I have a quick question on scope of ModelAttributes.
Dev. Env: Spring MVC 3.1/Java 6/JSP w/JSTL for Views
In my controller, I add an attribute to the model via
model.addAttribute(“appForResubmission”, appForResubmission);
In the JSP(served out in response to a GET request) I read it’s contents as:
${appForResubmission.appId}
— works fine and the data is shown on JSP as expected.
Upon submission of the JSP, in the same controller in a different method(in response to a PUT request), I try to read the attributes from the Model for any changes and I am doing this as
#ModelAttribute(“appForResubmission”) Application app
in the method signature.
However, all I get is a new Application object when I try to interrogate the object for data. Spring’s documentation says this kind of instantiation of a new object happens when the requested attribute does not exist in the Model.
What would cause the attribute to be lost? Any ideas? I am suspecting it is a scope issue someplace but I am not sure where the problem could be.
Any pointers you could provide is greatly appreciated?
Thank you,
M. Reddy
The scope of a modelattribute is the request, internally it is just equivalent to HttpSerletRequest.setAttribute("model", model).
If you want the model to be available in a different controller you probably have two options, one is to reconstruct it, based on what you submit to the controller or using your persistent source. The second option is for specific model attributes to be added to the session using #SessionAttribute({'modelname'}), but just be careful that you have to call SessionStatus.complete to remove the model added to the session later.
I am using ExtJS 4, and have a model with an association hasMany defined.
ModelA -> hasMany -> ModelB
I use GridA to show the data from ModelA. On clicking a record in GridA, I use a rowSelect event to create GridB which use ModelA.ModelB() as a store.
It all goes well until I change a record in ModelB. The record does get updated in the hierarchy, but when I execute StoreA.save(), no change is sent back to the server. It does not notice the changes in associations. How do I save this data, without hacking the architecture?
I am expecting the Model to be able to save, exactly the way it loads.
Also when I change a record in ModelA, it gets sent back as only ModelA and not as ModelA->ModelB, even when that record has ModelB data.
Thanks
Yeah I don't think this whole model hierarchy is fully functional yet to the level that you expected. You can assume that only the things that this Doc mentioned work: http://docs.sencha.com/ext-js/4-0/#!/api/Ext.data.Model
Model relations are not well implemented yet, and ExtJS 4.1 is not making any improvements on the implementation either, other than a few important bug fixes. To learn more check my blog at http://blog.neolocus.com/2011/12/extjs-4-and-model-associations/
There seem to be multiple means of passing model data from controllers in asp.net mvc to views. Its not clear to me if there's a recommended approach in the mvc v1 and v2 releases or if like most things in life, it depends. I've seen several approaches:
Option 1 - Populate the controller's ViewData dixtionary in either an icky string-based indexing way with casting in the view, or the in a strongly typed way by creating a strongly typed custom model class and passing that via ViewData.
Option 2 - Use ViewData.Model, which I'm not sure I even understand.
Option 3 - Use ViewPage.Model, in which case I'm not sure how you pass the model data from the controller.
I've seen a number of posts poo-pooing options 1 and 2 but I don't understand why. These posts seem to highly recommend 3 in most cases.
How do you approach this? Is there a standard way?
Every view 'should' have a specific model. This is sometimes more work so people use short cuts like ViewData, which works but is just not as clean and type safe in my opinion, so I prefer to have everything in the view's model.
You can then make all your views stongly typed. This is a very clean way to do so. Then in your controller you just call the view like:
YourViewModel model = new YourViewModel()
{
// initialize the data here
};
View(model);
Then in your views you can access all the data via ViewPage Model and it is all type safe and enforced from the controller as well.
EDIT from comments:
You don't need to use ViewData at all if you don't want. You can encapsulate all the data your view needs in a model. Just like the example you quoted with ProductsListViewData. It's just a model that contains all the items that were going to be stored in the ViewData. Both ways work but when you encapsulate it in a class (preferred method where everything is in the model) then all the bits and pieces are strongly typed.
ViewData is a generic container so even though you can just put anything you want into it, it is not type safe and therefore not as 'clean'. It comes down to preference and maintainability. There is only option 1 and 3. Your option 2 is misunderstood and is just option 3 in reality. There is no ViewData.Model just ViewPage.Model.
One approach you may wish to consider as your views become more complex, is to reserve the use of Models for input fields, and use ViewData to support anything else the View needs to render.
There are at least a couple of arguments to support this:
You have a master-page that requires some data to be present (e.g. something like the StackOverflow user information in the header). Applying a site-wide ActionFilter makes it easy to populate this information in ViewData after every action. To put it in model would require that every other Model in the site then inherit from a base Model (this may not seem bad initially, but it can become complicated quickly).
When you are validating a posted form, if there are validation errors you are probably going to want to rebind the model (with the invalid fields) back to the view and display validation messages. This is fine, as data in input fields is posted back and will be bound to the model, but what about any other data your view requires to be re-populated? (e.g. drop-down list values, information messages, etc) These will not be posted back, and it can become messy re-populating these onto the model "around" the posted-back input values. It is often simpler to have a method which populates the ViewData with the..view data.
In my experience I have found this approach works well.
And, in MVC3, the dynamic ViewModels means no more string-indexing!
This might be of some help:
When is it right to use ViewData instead of ViewModels?
(I know the following answer is highly arguable, but it's just the way I like to do it)
I would say use ViewData for simple data-tasks, and use the Model for the main purpose of the View. Check out Rob Conerys ViewData helper-classes here to give them a bit more Strongly typed feel:
http://blog.wekeroad.com/2010/01/20/my-favorite-helpers-for-aspnet-mvc
Using Models for absolubtely everything will bloat your project with hundreds of models just to achieve the smallest thing. I mean if you have a User-setting page, you would normally pass a User-model into the view, but if you decide to show some related data like Customers related to this User. You're stuck with the following solutions
You might have to add a List property to the User-model in order to expose the customers to the view. This leads to the Customer-property always being applied to the User-model everywhere else in the project - or make a new simpler User-model.
Make a new action that returns a partial of customers, which you can use with Html.RenderAction.
OR
you could do ViewData["Customers"] = myRepo.GetCustomersRelatedTo(user); // or something like that.
and (if using Robs helpers) in your view:
<%= Html.RenderPartial("CustomerList", Html.ViewData("Customers")) %>
add a PartialView called CustomerList that takes IEnumerable
In my humble opinion this is a cleaner solution and sure - you end up with a magic string here and there, but I'll stick to this approach until someone shows me a project with not a single magic string.
Use the tools we have in the Framework to get the job done. and Keep it simple s... ;)
I am pretty new to this stuff but I am running into a concept-wall and I keep going back and forth with the best way to handle the problem.
I have a multi-view process to filling out a "New User Form". Each view has a small part of the entire form. In each view I have a model and the model has properties set to an instance of a LINQ to SQL class (for pre-populating) along with dropdown data (state, country). I also thought I should have a model (value object) that represents the entire form. This value object has properties for each LINQ class as well. So I made the view take the value object as a dependency injection. Then what? Just set a property to ViewData to send in multiple models? Seems like a bad idea since I would have to do that to every view. Should all view models come from a base class with the value object?
I might be way off already. Hopefully someone can help me get back on track. The ultimate goal is to have an object that represents the state/data of a form that spans multiple views and the form fields should populate if data is present.
Thanks for your patience!
Okay, so I am going to try to answer my own question but I am still not sure about things. I am going to use the info I got here: http://www.asp.net/Learn/mvc/tutorial-13-cs.aspx to create an instance of the value object that will be available to every view. Then I am sending the instance (or a property of) into the view model through it's constructor.
I am still working on how to keep the instance of the value object through all pages but I am assuming it will have to be done through a session variable of sorts.
How would you tackle this problem:
I have data in my data store. Each item has information about:
URL = an arbitrary number of first route segments that will be used with requests
some item type = display will be related to this type (read on)
title = used for example in navigation around my application
etc.
Since each item can have an arbitrary number of segments, I created a custom route, that allows me to handle these kind of requests without using the default route and having a single greedy route parameter.
Item type will actually define in what way should content of a particular item be displayed to the client. I was thinking of creating just as many controllers to not have too much code in a single controller action.
So how would you do this in ASP.NET MVC or what would you suggest would be the most feasible way of doing this?
Edit: A few more details
My items are stored in a database. Since they can have very different types (not inheritable) I thought of creating just as many controllers. But questions arise:
How should I create these controllers on each request since they are related to some dynamic data? I could create my own Controller factory or Route handler or possibly some other extension points as well, but which one would be best?
I want to use MVC basic functionality of using things like Html.ActionLink(action, controller, linkText) or make my own extension like Html.ActionLink(itemType, linkText) to make it even more flexible, so Action link should create correct routes based on Route data (because that's what's going on in the background - it goes through routes top down and see which one returns a resulting URL).
I was thinking of having a configuration of relation between itemType and route values (controller, action, defaults). Defaults setting may be tricky since defaults should be deserialized from a configuration string into an object (that may as well be complex). So I thought of maybe even having a configurable relation between itemType and class type that implements a certain interface like written in the example below.
My routes can be changed (or some new ones added) in the data store. But new types should not be added. Configuration would provide these scenarios, because they would link types with route defaults.
Example:
Interface definition:
public interface IRouteDefaults
{
object GetRouteDefaults();
}
Interface implementation example:
public class DefaultType : IRouteDefaults
{
public object GetRouteDefaults()
{
return new {
controller = "Default",
action = "Show",
itemComplex = new Person {
Name = "John Doe",
IsAdmin = true
}
}
}
Configuration example:
<customRoutes>
<route name="Cars" type="TypeEnum.Car" defaults="MyApp.Routing.Defaults.Car, MyApp.Routing" />
<route name="Fruits" type="TypeEnum.Fruit" defaults="MyApp.Routing.Defaults.Fruit, MyApp.Routing" />
<route name="Shoes" type="TypeEnum.Shoe" defaults="MyApp.Routing.Defaults.Shoe, MyApp.Routing" />
...
<route name="Others" type="TypeEnum.Other" defaults="MyApp.Routing.Defaults.DefaultType, MyApp.Routing" />
</customRoutes>
To address performance hit I can cache my items and work with in-memory data and avoid accessing the database on each request. These items tend to not change too often. I could cache them for like 60 minutes without degrading application experience.
There is no significant performance issue if you define a complex routing dictionary, or just have one generic routing entry and handle all the cases yourself. Code is code
Even if your data types are not inheritable, most likely you have common display patterns. e.g.
List of titles and summary text
item display, with title, image, description
etc
If you can breakdown your site into a finite number of display patterns, then you only need to make those finite controllers and views
You them provide a services layer which is selected by the routing parameter than uses a data transfer object (DTO) pattern to take the case data and move it into the standard data structure for the view
The general concept you mention is not at all uncommon and there are a few things to consider:
The moment I hear about URL routing taking a dependency on data coming from a database, the first thing I think about is performance. One way to alleviate potentialy performance concerns is to use the built in Route class and have a very generic pattern, such as "somethingStatic/{*everythingElse}". This way if the URL doesn't start with "somethingStatic" it will immediately fail to match and routing will continue to the next route. Then you'll get all the interesting data as the catch-all "everythingElse" parameter.
You can then associate this route with a custom route handler that derives from MvcRouteHandler and overrides GetHttpHandler to go to the database, make sense of the "everythingElse" value, and perhaps dynamically determine which controller and action should be used to handle this request. You can get/set the routing values by accessing requestContext.RouteData.Values.
Whether to use one controller and one action or many of one or many of each is a discussion unto itself. The question boils down to how many different types of data do you have? Are they mostly similar (they're all books, but some are hardcover and some are softcover)? Completely different (some are cars, some are books, and some are houses)? The answer to this should be the same answer you'd have if this were a computer programming class and you had to decide from an OOP perspective whether they all have a base class and their own derives types, or whether they could be easily represented by one common type. If they're all different types then I'd recommend different controllers - especially if each requires a distinct set of actions. For example, for a house you might want to see an inspection report. But for a book you might want to preview the first five pages and read a book review. These items have nothing in common: The actions for one would never be used for the other.
The problem described in #3 can also occur in reverse, though: What if you have 1,000 different object types? Do you want 1,000 different controllers? Without having any more information, I'd say for this scenario 1,000 controllers is a bit too much.
Hopefully these thoughts help guide you to the right solution. If you can provide more information about some of the specific scenarios you have (such as what kind of objects these are and what actions can apply to them) then the answer can be refined as well.