Use Html.HiddenFor while iterating - asp.net-mvc-3

The ViewModel looks like this:
public W { get; set; }
public WC WC { get; set; }
public List<TC> TCs { get; set; }
WC has a correlated group of TC. Their relation is mapped by TC containing the foreign key WCId.
In the view, I have a form. In the form, there are input fields for WC. Then, there is a group of TC depending on a count with a max of 4. Each TC has a related T in that TC has a foreign key TCId. I am trying to make sure that when the form is posted TC has the correlating TId. The TId is held in a list of T in W (i.e. #Model.W.T.ElementAt(someindex).TId).
How can I levarage a lambda expression to use the helpers to generate this relation in the view so it can be consumed in a httppost action by the correlating controller?
Here is what I am doing right now:
<input type="hidden" value="#(Model.W.T.ElementAt(i).TId)"
name="TCs[#(i)].TId"
id="TCs_#(i)__TId" data-val="true"/>
What I would like to do is use the #Html.HiddenFor helper but cannot seem to get it to work so I just used the slightly dynamic yet still hardcoded approach above. Note: this works, however, I would like it to be cleaner.

What you're doing right now is -as far as I know - the only way to do it. I don't know of a way for a simpler #Htm.HiddenFor(...) version. I've been dealing with similar issues too. Nevertheless, if you think that you could reuse the pattern above you can still create your own Display/Editor template or other more abstract ways. Of course they'll be more verbose and complex than your "ugly" but straight forward approach.

I made this into a helper, and have decided to share it in case anyone else comes across this issue.
Helper:
public static MvcHtmlString CustomHiddenFor(
this HtmlHelper html, object ListValue,
string ListName, int ListIndex, string ListItem)
{
return MvcHtmlString.Create(
string.Format("<input type=\"hidden\" value=\"{0}\" name=\"{1}[{2}].{3}\" id=\"{1}_{2}__{3}\" data-val=\"true\"/>",
ListValue.ToString(),
ListName,
ListIndex,
ListItem
));
}
Use (note that this is done from a loop where i is the iterator, any value will due in the first spot):
#Html.CustomHiddenFor(Model.W.T.ElementAt(i).TId, "TCs", i, "TId")

Related

How do I bypass the limitations of what MVC-CORE controllers can pass to the view?

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.

mvc3 composing page and form element dynamically

I'm developing an MVC3 application and I have a page (well, a view) that let the users edit document's metainfo (a classic #Html.BeginForm usage). For general documents users will see standard fields to fill up, but through a dropdownlist they will be able to specify the type of the document: this, through an ajax call, will load new fields on the edit-document-form.
Whem the user submit the completed form, at last, the controller should read all the standard fields, plus all the fields loaded as being specific to the type of document selected.
Question is, how can I handle all this extra fields in a controller?
Say that I have Document class and a bunch of other classes extendinf Document, like Contract : Document, Invoice : Document, Complaint : Document and so forth, each having specific property (and this fields loaded on the form), how do I write the action in the controller?
I thought to use something like (I'll omitt all the conversions, validations, etc, for brevity)
[HttpPost]
public ActionResult Save(dynamic doc)
{
int docType = doc.type;
switch (docType)
{
case 1:
var invoice = new Invoice(doc);
invoice.amount = Request.Form["amount_field"];
invoice.code = Request.Form["code_field"];
//and so forth for every specific property of Invoice
Repository.Save(invoice);
break;
case 2:
var contract = new Contract(doc);
contract.fromDate = Request.Form["fromDate_field"];
contract.toDate = Request.Form["toDate_field"];
//and so forth for every specific property of Contract
Repository.Save(contract);
break;
..... // and so forth for any document types
default:
break;
}
}
But it seems a very dirty approach to me. Do you have a better idea on how to achive this? Maybe there's a pattern that I don't know nothing about to approach this kind of scenario.
Update
A second idea comes to my mind. After commenting Rob Kent's answer, I thought I could take a different approach, having just one class Document with a property like
public IEnumerable<Field> Tipologie { get; set; }
where
public class Field
{
public int IdField { get; set; }
public String Label { get; set; }
public String Value { get; set; }
public FieldType ValueType { get; set; }
public List<String> PossibleValues { get; set; } // needed for ENUMERATION type
}
public enum FieldType
{
STRING, INT, DECIMAL, DATE, ENUMERATION
}
Is this a better approach? In this case I can have just an action method like
[HttpPost]
public ActionResult Save(Document doc)
But shoud I create the fields in the view in order to make the MVC engine do the binding back to the model?
Given that the class inheriting from Document in the first approach will probably be generated at run-time, would you prefer this second approach?
To keep it all hard-typed on the server, you could use an abstract base type with a custom binder. See my answer here to see how this works: MVC generic ViewModel
The idea is that every time they load a new set of fields, you change the BindingType form variable to the instantiated type of the handler. The custom binder is responsible for creating the correct type on submission and you can then evaluate that in your action, eg:
if (model is Contract) ...
I'm not sure if you will be able to set up different actions each with a different signature, eg,:
public ActionResult Save(Contract contract) ...
public ActionResult Save(Invoice invoice) ...
Pretty sure that won't work because Mvc will have already decided which method to call, or maybe it will firstly see what type it gets back and then decides.
In my linked example, I am checking for overridden base members but if that is not an issue for you, you just need to create the correct type.

How to iterate through all MVC Action Handlers to ensure they're in a SiteMap?

I'm working on writing a non-trivial "unit test" (perhaps better called a "UI test"?). In this case, I think I want a test that (reflectively?) finds all appropriate action handlers and then verifies that our SiteMap has a node for those Action Handlers. What I need is to identify when a developer adds a page to our system and forgets to add it to the SiteMap (this seems to be a common problem and I'd like to make it go away, which a test should easily be able to do for us). Ultimately, we want to make sure that any page that a user can land on will have a home in our SiteMap so that it builds the appropriate breadcrumb to tell the user where they are in our system (that breadcrumb part already works perfect for us, as long as the page is in the SiteMap). I would much rather try to do this with a test than try to force some policy/procedure update on us that is yet another thing we have to deal with.
Any tips on some existing code to start from in this endeavor? And if not, any thoughts on the best way to do this?
One possibility that I am considering is to reflectively identify any method that is decorated with the AcceptVerbs attribute which doesn't have a return type of JsonResult (and perhaps a couple others which would clearly not be "web pages" such as FileResult). And perhaps limit my search by first identifying classes which inherit System.Web.Mvc.Controller.
I'd love to hear a better way to do this, though. And would love even more if most of this was already written and shared to save me some time. :-)
I see this is mvc application=>
your solution would lie in routing(for me_)
you know every option which is not marked as [nonaction] is registered in Global.asax
which contains
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
and in this routes you will be able to find all accessible methods =>eg actions
this is created in basic mvc
Hope this will help
Here is the important part of my solution. This works well for us.
var mvcAssembly = typeof (AccountController).Assembly;
AllControllers = mvcAssembly.GetTypes().Where(type => type.IsSubclassOf(typeof (Controller))).ToList();
UnfilteredActionHandlers = new List<MethodInfo>();
foreach (var controller in AllControllers)
{
UnfilteredActionHandlers.AddRange(controller.GetMethods()
.Where(methodInfo =>
typeof (ActionResult).IsAssignableFrom(methodInfo.ReturnType)
&& !ControllerClassesToIgnore.Contains(methodInfo.ReflectedType)));
}
We have a few collections that we filter the UnfilteredActionHandlers collection by depending on how we want to use them:
internal List<MethodInfo> UnfilteredActionHandlers { get; set; }
internal List<MethodInfo> ActionHandlersExcludingFilteredReturnTypes { get; set; }
internal List<MethodInfo> ActionHandlersFilteredByAttributes { get; set; }
internal List<MethodInfo> ActionHandlersFilteredByBoth { get; set; }
internal static List<Type> ReturnTypesToIgnore { get; set; }
internal static List<Type> RequiredAttributes { get; set; }
internal static List<Type> ControllerClassesToIgnore { get; set; }

Linq dynamic queries for user search screens

I have a database that has a user search screen that is "dynamic" in that I can add additional search criteria on the fly based on what columns are available in the particular view the search is based on and it will allow the user to use them immediately. Previously I had been using nettiers for this database, but now I am programming a new application against it using RIA and EntFramework 4 and LINQ.
I currently have 2 tables that are used for this, one that fills the combobox with the available search string patterns:
LastName
LastName, FirstName
Phone
etc....
then I have an other table that splits those criteria out and is used in my nettiers algorithms. It works well, but I want to use LINQ..and it doesnt fit this model very well. Besides I think I can pare it down to just one table with linq...
using a format similar to this or something very close...
ID Criteria WhereClause
1 LastName 'Lastname Like '%{0}%'
now I know this wont fit specifically into a linq query..but I am trying to use a univeral syntax for clarity here...
the real where clause would look something like this: a=>a.LastName.Contains("{0}")
My first question is: Is that even possible to do? Feed a lambda in to a string and use it in a Linq Query?
My second question is: at one point when I was researching this before I found a linq syntax that had a prefix like it.LastName{0}
and I appear to have tried using it because vestiges of it are still in my test databases...but I dont know recall where I read about it.
Is anyone doing this? I have done some searches and found similar occurances but they mostly have static fields that are optional, not exactly the way I am doing it...
As for your first question, you can do this using Dynamic Linq as described by Scott Gu here
var query = Northwind.Products.Where("Lastname LIKE "test%");
I'm not sure how detailed your dynamic query needs to be, but when I need to do dynamic queries, I create a class to represent filter values. Then I pass that class to a search method on my repository. If the value for a field is null then the query ignores it. If it has a value it adds the appropriate filter.
public class CustomerSearchCriteria{
public string LastName { get; set; }
public string FirstName { get; set; }
public string PhoneName { get; set; }
}
public IEnumberable<Customer> Search(CustomerSearchCriteria criteria){
var q = db.Customers();
if(criteria.FirstName != null){
q = q.Where(c=>c.FirstName.Contains(criteria.FirstName));
}
if(criteria.LastName!= null){
q = q.Where(c=>c.LastName.Contains(criteria.LastName));
}
if(criteria.Phone!= null){
q = q.Where(c=>c.Phone.Contains(criteria.Phone));
}
return q.AsEnumerable();
}

Beginner EF4 / CodeFirst / MVC3 help

Although I love what I'm learning, I'm finding it a struggle and need some help
I've been using these two tutorials which I think are awesome:
http://weblogs.asp.net/scottgu/archive/2010/07/16/code-first-development-with-entity-framework-4.aspx
http://msdn.microsoft.com/en-us/data/gg685467
Currently my main problem/confusion is:
I have a CodeFirst table/entity I don't know how to correctly get data from other tables/entities to show in my views:
public class Car {
public int ID { get; set; }
public string Name { get; set; }
public int EngineID { get; set; }
public virtual Engine { get; set; }
}
public class Engine {
public int ID { get; set; }
public string Name { get; set; }
public string Manufacturer { get; set; }
// (plus a whole lot of other things)
}
Now when I create a View for Cars (using the List type/option) I get a nice autogenerated list
#foreach (var item in Model) {
<tr>
<td>#item.ID</td>
<td>#item.Name</td>
<td>#item.EngineID</td>
</tr>
Perfect... except EngineID is mostly worthless to the viewer, and I want to show Engine.Name instead
So I assumed I could use EF lazy loading:
<td>#item.Engine.Name</td>
Unfortunately when I tried that, it says my ObjectContext has been disposed so can't get any further data requiring a connection
Then I tried going to the controller and including the Engine.Name
var cars = (from c in db.Cars.Include("Engine.Name") select c;
Which tells me: Entities.Engine does not declare a navigation property with the name 'Name'
... ? Lies
Include("Engine") works fine, but all I want is the Name, and Include("Engine") is loading a large amount of things I don't want
Previously in a situation like this I have created a view in the DB for Car that includes EngineName as well. But with CodeFirst and my noobness I haven't found a way to do this
How should I be resolving this issue?
I thought perhaps I could create a Model pretty much identical to the Car entity, but add Engine.Name to it. This would be nice as I could then reuse it in multiple places, but I am at a loss on how to populate it etc
Wanting to learn TDD as well but the above is already frustrating me :p
Ps any other tutorial links or handy things to read will be greatly appreciated
It isn't lies as you are actually trying to include a property that's a 2nd level down withouth giving it a way to navigate. If you let EF generate your DB with this structure, it would likely have made a navigation table called something like Car_Engine and if you include the name without the object it HAS mapped, then it's not got a navigation property in your new object.
The simple way around this is to go:
(from c in db.Cars.Include("Engine") select new { c, EngineName = c.Engine.Name }
If you still get navigation property errors then you might need to make sure your are mapping to your schema correctly. This can be done with EntityTypeConfiguration classes using the fluent API - very powerful.
This of course won't help in strongly typing your car object to show in MVC.
If you'd like to get around this, your gut feeling is right. It's pretty common to use viewmodels that are read only (by design, not necessarily set to readonly) classes that provide simple views of your data.
Personally I keep my model quite clean and then have another project with viewmodels and a presentation project to populate. I'd avoid using overlapping entities in your core model as it might lead to unpredictable behaviour in the data context and at least a peristance nightmare when updating multiple entities (ie who's responsible for updating the engine name?).
Using you viewmodels, you can have a class called CarSummaryView or something with only the data you want on it. This also solves the issue of being vulnerable to overposting or underposting on your site. It can be populated by the above query quite easily.
PS There's a bunch of advantages to using viewmodels beyond just not loading full heirarchies. One of the biggest is the intrinsic benefit it gives you with avoiding over and underposting scenarios.
There's loads of ways to implement viewmodels, but as a simple CarView example:
public class CarView
{
public int ID { get; set; }
public string Name { get; set; }
public string EngineName { get; set; }
}
This should be clearly seperated from your entity model. In a big project, you'd have a single viewmodels project that the presenter can return, but in a smaller one you just need them in the same layer as the service code.
To populate it directly from the query, you can do the following
List<CarView> cars = (from c in db.Cars.Include("Engine.Name") select new CarView() { ID = c.ID, Name = c.Name, EngineName = c.Engine.Name }).ToList();

Resources