I'm new to MVC and I have a relatively basic question. I've found some answers through searching, but nothing that conclusively makes me feel like I know the 'best practice' for this common functionality:
I have strongly typed views and I have a relationship between 'Members' and 'Pets'. I want a page on my Member edit view to 'Create a new Pet'.
At this point, I'd like to pass the memberId from the Member edit view to the Pet create page so it can start a new instance of the Pet model with the correct member associated.
I'm thinking the best practice is to pass the memberId from the Member edit view (but by what method?) and then store this value in a hidden form field that is the correct name for the strongly typed 'memberId' value on the Pet model. Then, the Pet create controller should automatically pull this value when it's creating the Pet.
I need help passing the memberId from the Member edit view to the Pet edit view, and also general thoughts if this is the best way to accomplish what I'm looking to do. Thank you for your time and assistance!
The straightforward way is to pass the memberId as a route value to the CreatePet Action.
So your Member View might have a Url like the following to navigate to the CreatePet Action:
#Url.Action("CreatePet", "PetController", new { memberId = Model.MemberId })
And the CreatePet action would have the memberId as a parameter
public ActionResult CreatePet(int memberId)
{
...
}
UPDATE
From comment to question:
I was also curious if I could just pass the entire Member model in the 'Add a Pet' button and use that in the Pet Controller? This might be the preferable method
This doesn't sound like a good idea for a number of reasons. Why should the Pet need to know so much about the Member? And what if the client spoofs the Member values - you shouldn't rely on them, so would be better off looking up the Member server side using only the id that is passed as a route value.
So the way that is really preferred that I have seen for handling this kind of problem is the use of the MVVM pattern where you create a ViewModel object specifically designed for the view. While this may seem like, I have so many Views! But thats probably when you need to switch to dynamic views. However if you can not this pattern is a good one to follow for it. For instance you need a member id and the pet information I believe? What you can do is pass down an object that contains the list of pets, or something like that. Then in the view model you also pass down a list of member ids or the member id. I am not quite clear on the point of the application. But bottom line is you create a model that is specific to that view that also makes use of the actual models in order to pass down the necessary information. This links is a good description of it.
http://msdn.microsoft.com/en-us/library/gg405484(v=pandp.40).aspx
Good luck.
Related
I'm using NerdDinner as a guide for my first MVC/LINQ to SQL project. It discusses the use of the ViewModel pattern when a View needs data from multiple sources - in their example: Dinners and Countries (serves as the drop down list).
In my application, the problem is a bit different. It's not so much different data, rather data linked via a key constraint. I have a Story table that links to aspnet_users via the UserId key. I would like to have easy access to the UserName for each story.
Since I'm using the repository pattern and returning IQueryable in some cases for deferred execution, I'm struggling with the correct way to shape the data. So I'm using this VideModel pattern right now to make it work but not sure if it's the right way.
Instead of returing IQueryable Story (which wouldn't work since I need the UserName), I'm returning a new custom class UserStory, which has a Story property and a string Username property.
What are your thoughts?
It seems like your question has less to do with MVC as it is simply a question about how to access the story data based on the username string.
Would it be possible to create a view in your database with all the UserStory data, the username, along with userid in it? That way, you could select from the view based on the username you have.
To create the view, you would simply have to do a join between the user table and the userstory table based on the userid.
After that, you could still use the repository pattern with the IQueryable being returned.
If you are wanting to do updates, it would be simple to do since you still have the userid, and would be able to link back to the actual table which would need the update.
If you look at Kigg, you will see that they mess about with the initial model to create custom ViewModels. That's the thing that NerdDinner doesn't cover in any detail. You could create a StoriesWithUserName class that inherits from Stories, but adds a new property - UserName. Then you return that to your View which would inherit from IEnumerable<StoriesWithUserName>
[EDIT]
Oops. Didn't spot that you already did this :o)
Using the repository pattern and returning an IQueryable of Stories is fine. The relationship allows you to access the the username value something like this >>
Assuming you are returning the IQueryable in your model object:
foreach(Story story in Model.Stories)
{
// do something with the value
response.write(story.aspnet_user.UserName);
};
Your Repository method would look like this:
public List<Stories> GetStories(Guid UserId)
{
return datacontext.Stories.Where(u => u.UserId = UserId).ToList();
}
The relationship will automatically provide you with access to the UserName value in the foreach loop i first mentioned. nothing more is required.
I'm not sure why your pagination control failed on Count() though??
Hope this helps
My domain model is this: we have a bunch of schools as the root of the "hierarchy". Each school has teachers and courses, and each course has one teacher. I am trying to model this with the logic of the mvc framework and I 'm quite confused. For example, the \school\details\x should give the first page of a school. That should contain a link to a list of its teachers, and a list to each courses.
A list of teachers means that the index action should be parametric to the school the user is looking at: \teacher\id where id is the school. The same with the course list. And then create teacher or course should also be parametric to what school we are looking at:\teacher\create\x where x=school.
How do I carry around the school id? Is there some neat way to do it, or do I need to pass it around all the time, into every view that needs it? It also makes the site URLs very cryptic. I was thinking of a way to make the url structure like {school-alias}\{controller}\{action}\{id}, still I have to find a way to pass around the school. If this is accomplished, then I need to implement some kind of filter that will not allow a user to perform certain actions if the schoolId he is requesting does not match the one in his profile.
I figure that if I 'm carrying the schoolid around the URL, the site is more REST-like, compared to, for example, getting the schoolId from the user's profile.
I would create acronym for every school. For example:
School no. 1 - ABC
School no. 2 - DEF
If i wanted to list teachers, I would write
http://site-address/ABC/teachers/list or just http://site-address/ABC/teachers
To show basic information about school
http://site-address/ABC
The code for routing would be:
routes.MapRoute(
"Default", // Route name
"{acronym}/{controller}/{action}/{id}", // URL with parameters
new {controller = "School", action = "Details", id = ""} // Parameter defaults
);
I would create authorization action filter on teachers,school and classes controller to check if user has access to school defined by acronym parameter in URL. You can check it by comparing filterContext.RouteData.Values["acronym"] with data stored in profile.
Write an extension method to overload rendering of links that extracts the school identifier ( acronym or whatever you choose to use ) from the routing data and adds it to the route values already passed in. This way your action can choose to use the identifier if it is present but is not required to add it to the view data and you do not have to remember to include it in any action links ( you just have to remember to use your action link overload ).
I would make the action link overload quite obviously different so anyone following behind you can see you are doing something unusual. This could be as simple as Html.SchoolActionLink( ...).
For example:
If your url is http://mydomain.com/abc/teachers/list and your route is defined as {school}/{controller}/{action} then the route value dictionary will have the value "abc" at the key "school". The route values can be accessed via HtmlHelper.ViewContext.RouteData.Values.
In the end I 'm answering my own question.
The real solution to this is :Restfull Routing. It implements the functionality in RoR, which is exactly what I need. Too bad this is not a requirement from more people so that it can go into mvc-trunk.
I have a View class (OrderView.aspx) which shows the details of an order (Account Name, Order Date) as well as a list of order lines via the Repeater control. Each order line is represented by a User Control View (OrderLineView.ascx) which shows the details of the order line (Item Name, Quantity, Price). I have a model object called Order which I use as the data source for all of this, which I pass as the model object for the view.
Each OrderLineView user control has a Save and a Delete button. I have the Save button posting the contents of a form within the OrderLine control to a Save method on the Controller and then RedirectToAction back to the same Order page (this refreshes the whole page, nothing AJAXy about it). I have the Delete button linking to a method on the Controller that tries to delete, and then RedirectToAction back to the same Order page. If the delete fails, however, I want a little error message to show up next to the delete button when the page renders again(remember, there is a delete button for every order line on the page, so I only want the message next to the one I clicked). My questions:
1 - How do I pass this data from my Controller method to the specific User Control? Should I somehow add it in to the model? Seems like a bad idea (since it really isn't part of the model).
2 - Should I have a OrderLineController for the OrderLine operations as well as a OrderController for Order operations? I just want to know if best practice is to have a separate Controller for every view.
3 - I have seen how some people might call RedirectToAction with an anonymous type value like this:
RedirectToAction("ViewOrder", new { Id = 1234, Message = "blabla"});
but this makes the Message value show up in the URL string. I am OK with that, but would prefer that it doesn't show if possible.
4 - Also, for accessing properties of the Model from within the view, I find myself doing this all of the time:
foo(((someModelType) this.ViewData.Model).SomeProperty);
I don't like this for a number of reasons, one of which is the fact that I don't want my view to be coupled with the type of my model (which is why I am using ViewPage instead of ViewPage). I would much prefer to be able to have a call like this:
foo(ModelEval("SomeProperty"));
Is there such a thing? I have written my own, but would like it if I didn't have to.
1
Check out ModelState.
ViewData.ModelState.AddModelError("something.Name", "Please enter a valid Name");
ModelState is actually a dictionary, so you could identify the errors on a per-control basis. I don't know if this is a best practice, but it would probably work.
Try something along the lines of
ViewData.ModelState.AddModelError("something#3.Name", "Please enter a valid Name");
and in your view, you could put
<%= Html.ValidationMessage(string.format({"something{0}.Name", YourUniqueId))%>
4
You can strongly type your view, so you don't need that cast, but if you're concerned about tightly coupling, this may put you off. But having the strong type there is no more tightly coupled than having a magic string point to that property of the model anyway. The former just gives you type safety and the glory that is intellisense.
Since your OrderLine has a unique ID you can use that to construct a key to be placed in the ModelState errors container.
public ActionResult Delete(int? Id)
{
ModelState.AddModelError("OrderLine" + Id.Value, "Error deleting OrderLine# " + Id.Value);
...
}
and then use the ValidatinoMessage helper. This will check the ModelState to see if an error exists and if it does it will display the message. Otherwise it's blank.
<%= Html.ValidationMessage ("OrderLine" + Id)%>
In the next release of MVC Model will become a top level property so the following
foo(((someModelType) this.ViewData.Model).SomeProperty);
can be written as
foo(Model.SomeProperty);
Model objects should already be typed unless you're using public object as a property?
The 'RenderPartial()' method in ASP.NET MVC offeres a very low level of functionality. It does not provide, nor attempt to provide a true 'sub-controller' model *.
I have an increasing number of controls being rendered via 'RenderPartial()'. They fall into 3 main categories :
1) Controls that are direct
descendants of a specific page that
use that page's model
2) Controls that are direct
descendants of a specific page that
use that page's model with an
additional key of some type.
Think implementation of
'DataRepeater'.
3) Controls that represent unrelated
functionality to the page they appear
on. This could be anything from a
banner rotator, to a feedback form,
store locator, mailing list signup.
The key point being it doesn't care
what page it is put on.
Because of the way the ViewData model works there only exists one model object per request - thats to say anything the subcontrols need must be present in the page model.
Ultimately the MVC team will hopefully come out with a true 'subcontroller' model, but until then I'm just adding anything to the main page model that the child controls also need.
In the case of (3) above this means my model for 'ProductModel' may have to contain a field for 'MailingListSignup' model. Obviously that is not ideal, but i've accepted this at the best compromise with the current framework - and least likely to 'close any doors' to a future subcontroller model.
The controller should be responsible for getting the data for a model because the model should really just be a dumb data structure that doesn't know where it gets its data from. But I don't want the controller to have to create the model in several different places.
What I have begun doing is creating a factory to create me the model. This factory is called by the controller (the model doesn't know about the factory).
public static class JoinMailingListModelFactory {
public static JoinMailingListModel CreateJoinMailingListModel() {
return new JoinMailingListModel()
{
MailingLists = MailingListCache.GetPartnerMailingLists();
};
}
}
So my actual question is how are other people with this same issue actually creating the models. What is going to be the best approach for future compatibility with new MVC features?
NB: There are issues with RenderAction() that I won't go into here - not least that its only in MVCContrib and not going to be in the RTM version of ASP.NET-MVC. Other issues caused sufficent problems that I elected not to use it. So lets pretend for now that only RenderPartial() exists - or at least that thats what I've decided to use.
Instead of adding things like MailingListSignup as a property of your ProductModel, encapsulate both at the same level in a class like ProductViewModel that looks like:
public class ProductViewModel() {
public ProductModel productModel;
public MailingListSignup signup;
}
Then get your View to be strongly-typed to the ProductViewModel class. You can access the ProductModel by calling Model.productModel, and you can access the signup class using Model.signup.
This is a loose interpretation of Fowler's 'Presentation Model' (http://martinfowler.com/eaaDev/PresentationModel.html), but I've seen it used by some Microsoft devs, such as Rob Conery and Stephen Walther.
One approach I've seen for this scenario is to use an action-filter to populate the data for the partial view - i.e. subclass ActionFilterAttribute. In the OnActionExecuting, add the data into the ViewData. Then you just have to decorate the different actions that use that partial view with the filter.
There's a RenderPartial overload I use that let's you specify a new ViewData and Model:
RenderPartial code
If you look at the previous link of the MVC source code, as well as the following (look for RenderPartialInternal method):
RenderPartialInternal code
you can see that if basically copies the viewdata you pass creating a new Dictionary and sets the Model to be used in the control. So the page can have a Model, but then pass a different Model to the sub-control.
If the sub-controls aren't referred directly from the main View Model, you could do the trick Marc Gravell mentions to add your custom logic.
One method I tried was to use a strongly typed partial view with an interface. In most situations an agregated ViewModel is the better way, but I still want to share this.
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IMailingListSignup>" %>
The Viewmodel implements the interface
public class ProductViewModel:IMailingListSignup
Thats not perfect at all but solves some issues: You can still easily map properties from your route to the model. I am not shure if you can have a route parameter map to the properties of MailingListSignup otherwise.
You still have the problem of filling the Model. If its not to late I prefer to do it in OnActionExecuted. I dont see how you can fill a Model in OnActionExecuting.
Imagine a blog engine in ASP.NET MVC where I want strongly-typed Views. I have a Model for BlogPost and a Model for Comments. A typical page would display a blog post and all of that post's comments. What object should I pass to the View()? Should I create a Presentation Model? Maybe I'm using that term incorrectly, but by that I mean should I create a composite model just for a View(), something like BlogAndComments? Should my controller create an instance of BlogAndComments and pas that?
Or, should I somehow pass both a BlogPost and Comments object to the View?
I think you're on the right track with your understanding of Presentation Models. As to when you should create a View Model, the answer is probably 'it depends'. In your example, you can probably get away with passing the BlogPost and Comments in the ViewData object. It's not gorgeous, but hey, it gets the job done.
When and if that starts to feel ugly or unwieldy, then I would start thinking about making a View Model. I usually end up with the notion of some sort of 'Page', which includes the page title, common data, and then specific stuff for a particular page. In your case, that might end up as a BlogViewPage, which includes Title, BlogPost and List comments.
The nice thing about that approach is that you can then test that controller by making a request and testing the BlogViewPage to ensure that it contains the expected data.
In my opinion comments belong to the view as much as the post itself.
Make a BL class for your comments like:
class CommentBO
{
Guid UserID;
string Text;
}
Then you have a BO for your post.
class PostBO
{
Guid UserID;
List<CommentBO> Comments;
}
Then your model can be really simple.
class BlogModel
{
string AuthorName;
string BlogTitle;
List<PostBO> Posts;
}
Pass it to the view and render it.
You may be tempted to omit all BO and just fill the model directly from the database. It is an option, but not exactly the right one. A model is just a package of things for a view to display. These things however should be prepared somewhere else, namely at the business logics level with just a nominal participation of the controller.
I always use strongly typed views and create a presentation model class for each view or view user control. The advantage is that by looking at the presentation model class alone I know exactly what are the values that the view uses. If I were passing domain models then that would not be obvious because domain models may contain many properties that the view does not use. I would have to look at the view's markup to figure out what values it needs. Plus the presentation model usually adds certain properties that are not available in the domain model. It seems a bit tedious but I think it saves time and makes the code better in the long run.
In my opinion if comments belong to a blog post why not create a collection of comments on the blog post model? That makes perfect sense from a domain modeling stand-point and chances are whatever ORM you are using would support lazy-loading that collection which simplifies your controller logic as well.