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

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; }

Related

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.

Use Html.HiddenFor while iterating

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")

validation rules in mvc 3.0

I have one view model which is common for 3 to 4 views in this model I also define validation rules.Now problem is that in one of that view I want to overwrite that view model validation rules for two to three fields.so what I do? I don't want to make new view model for that view.
From an MVC architecture standpoint - this is exactly why you use view models.
You should create separate view models for each case. Use automapper (available for free on codeplex) to copy the values between your view model and your entity.
Don't even consider a different way, inheritance, etc - this is what ViewModels are for.
Three options I can think of:
Make a separate ViewModel using AutoMapper to handle some of the heavy lifting.
Make a subclass having different validation rules.
Make a custom ValidationAttribute which is context sensitive (Either by overriding the IsValid(Object, ValidationContext)method, or relying other context information from static methods/properties.
For instance, this Required validation attribute would be ignored if the request came from a certain URL:
public class CustomRequiredAttribute : RequiredAttribute
{
public override bool IsValid(object value)
{
if (HttpContext.Current.Request.Url != "urlwhennotrequired")
return base.IsValid(value);
return true;
}
}
If you do go ahead and use inheritance, then make sure that you inherit from abstract class. I think that as the system grows, you are likely to come across a scenario where your abstract class will have to be modified heavily,therefore If I were you, I'd create more view models, even if the code appears to be repetative. In the long term run you'll benefit because you'll be able to modify parts of your applications with as little side affects as possible.
My recommendation is basically what you don't want: create new model classes, but use inheritance to avoid repeating the properties you want. If you are adamantly opposed to creating separate models, you might look into implementing IValidatableObject and have it inspect other properties before validating the properties that you wish to vary.
EDIT:
I don't disagree with Tuliper's answer, but to flesh out my suggestions, consider a scenario in which you want to save a user's data. From one form, you are creating a user; from another, you are simply updating (this is a bit of a stretch but it's for purposes of illustration). The "create" form might require the name of a person referring the user, while the "update" form might not.
Using inheritance, you could do the following:
public class SaveUserModel
{
public int? UserId { get; set; }
...
}
public class CreateUserModel : SaveUserModel
{
[Required]
public string ReferredByName { get; set; }
}
Using IValidatableObject, you could do it this way:
public class SaveUserModel : IValidatableObject
{
public int? UserId { get; set; }
public string ReferredByName { get; set; }
...
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// if UserId is null, we are creating a user vs. updating
if (UserId != null && string.IsNullOrWhiteSpace(ReferredBySiteUrl))
yield return new ValidationResult("Please specify the name of the person who referred you.", new[] { "ReferredByName" });
}
}
To reiterate, I am not trying to push my answer. I would be inclined to reuse models if they are exactly the same across different views, but generally there are enough differences to warrant simply creating separate models. In the end, any perceived technical debt alleviated by adhering to DRY in this situation would a bit of a wash; models tend to be easy to maintain.

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();

MVC2 - How to obtain parent model (container) inside template

I'm writing an MVC2 app using DataAnnotations. I have a following Model:
public class FooModel
{
[ScaffoldColumn("false")]
public long FooId { get; set; }
[UIHint("BarTemplate")]
public DateTime? Bar { get; set;}
}
I want to create a custom display template for Bar. I have created following template:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<DateTime?>" %>
<div class="display-label">
<span><%: Html.LabelForModel() %></span>
</div>
<div class="display-field">
<span><%: Html.DisplayForModel()%></span>
<%: Html.ActionLink("Some link", "Action", new { id = ??FooId?? }) %>
</div>
Now, my problem is that inside template for Bar I want to access another property from my model. I don't want to create a separate template for FooModel because than I will have to hardcode all other FooModel properties.
After a brief investigation with a debugger I can see that:
this.ViewData.ModelMetadata.ContainerType
is FooModel (as expected)
this.ViewData.TemplateInfo has a
non-public property VisitedObjects
(of type
System.Collections.Generic.HashSet<object>)
which contains two elements:
FooModel and DateTime?.
How can I get access to my FooModel? I don't want to hack my way around using Reflection.
Update:
I've accepted mootinator's answer as it looks to me as the best solution that allows type-safety. I've also upvoted Tx3's answer, as mootinator's answer builds upon it. Nevertheless, I think that there should be a better support form MVC in those kind of scenarios, which I believe are quite common in real world but missing from sample apps.
Maybe you could create new class, let's say UserDateTime and it would contain nullable DateTime and rest of the information you need. Then you would use custom display template for UserDateTime and get access to information you require.
I realize that you might be looking for other kind of solution.
I think you may be better off extracting this functionality to an HtmlHelper call from the Parent View.
Something like RenderSpecialDateTime<TModel>(this HtmlHelper html, Expression<Func<TModel,DateTime?>> getPropertyExpression) would probably do the job.
Otherwise, you will have to do something like what Tx3 suggested. I upvoted his answer, but posted this as an alternative.
Couldn't you use the ViewData dictionary object in the controller and then grab that in the ViewUserControl? It wouldn't be strongly typed but...you could write a helper to do nothing if it's empty, and link to say the example login history page if it had a value.
It would appear that somewhere between MVC 5.0 and 5.2.2 a "Container" property was added on to the ModelMetadata class.
However, because all of the methods in a provider responsible for metadata creation (GetMetadataForProperty, Create etc) do not have container in their signature, the Container property is assigned only in certain cases (GetMetadataForProperties and GetMetadataFromProvider according to reflected code) and in my case was usually null.
So what I ended up doing is overriding the GetMetadataForProperty in a new metadata provider and setting it there:
public override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, string propertyName)
{
var propMetaData = base.GetMetadataForProperty(modelAccessor, containerType, propertyName);
Object container = modelAccessor.Target.GetType().GetField("container").GetValue(modelAccessor.Target);
propMetaData.Container = container;
return propMetaData;
}
I know this is reflection but it's fairly succinct. It would appear that MS is correcting this oversite so maybe it will be possible to replace the reflection code in the future.
Sorry if this suggestion seems daft, I haven't tried it, but couldn't you do what Tx3 suggested without having to create a bunch of new classes by defining a generic class to reference whatever type of parent you want?
public class FooModel
{
[ScaffoldColumn("false")]
public long FooId { get; set; }
[UIHint("BarTemplate")]
public ParentedDateTime<FooModel> Bar { get; set;}
public FooModel()
{
Bar = new ParentedDateTime<FooModel>(this);
}
}
public class ParentedDateTime<T>
{
public T Parent {get; set;}
public DateTime? Babar {get; set; }
public ParentedDateTime(T parent)
{
Parent = parent;
}
}
You could expand that to encapsulate any old type with a <Parent, Child> typed generic, even.
That would also give you the benefit that your strongly typed template would be for
Inherits="System.Web.Mvc.ViewUserControl<ParentedDateTime<FooType>> thus you would not have to explicity name which template to use anywhere. This is more how things are intended to work.

Resources