I have a Helper method that I need to use across multiple views. In an attempt to accomplish this, I tried to implement the approach shown by Scott Guthrie here: http://weblogs.asp.net/scottgu/archive/2011/05/12/asp-net-mvc-3-and-the-helper-syntax-within-razor.aspx. For the sake of reference, my helper method looks like the following:
#helper MyMethod(string parameter)
{
MyNamespace.MyClass.HelperMethod(this.Request, parameter)
}
As you can see, I need to get access to the HttpRequestBase object associated with the view. The code works fine if I define the method at the top of _Layout.cshtml. However, I need to use it in other views. So, as mentioned, I used the approach highlighted by scottgu. Unfortunately, I get a runtime error now that says:
"CS0026: Keyword 'this' is not valid in a static property, static method, or static field initializer"
How can I get over this hump?
If it's the HttpRequestBase object you need, try passing in HttpContext.Current.Request instead of this.Request.
Related
I'm trying out a generic approach when calling an MVC controller via ajax. So, if we have something like this:
url: '/ChartReporting/GetChartReportingResults'
data: JSON.stringify({Type: 'Pie', some more properties...})
Is there a way to not have to specify the concrete class in the controller method as in:
public JsonResult GetChartReportingResults(ChartingData chartingData)...
I'd love to be able to say ChartingBase instead of ChartingData. ChartingBase would be an abstract class. So the params I pass within the JSON.stringify would be a class of type ChartingBase. I can easily do this type of thing once inside the controller but to get this generic info over in the first place is the tricky part. Basically, Im trying to have one method to use for all charting needs based on this ChartingBase class.
Hope this makes sense.
David
I thought about this and decided to just simply pass in an array of key/value pairs with paramname, param value. I can easily build a paramlist for the proc with that info.
Thanks for letting me vent and think this through myself. Of course ideas from others would have been cool too :-)
David
Suppose I have a route like the following:
/api/v1/{organisationId}/resource/{resourceId}
Since there are likely to be a number of different types of resources hanging off an organisation, I'd like a common way of dealing with the organisationId.
What I thought of doing is creating a base controller (inheriting from ApiController, obviously) which would have an OrganisationId property.
Is there any way to get WebApi to pass the organisationId route parameter to a property on the controller, rather than to a parameter on the action method?
How about this?
public int OrganisationId
{
get
{
return Convert.ToInt32(Request.GetRouteData().Values["organisationId"]);
}
}
This is not using the model binding layer and is assuming that organisationId would be present in the route data. It is simple and a bit less flexible than what web API model binding offers.
The downside to reading the route from within the property getter is unit testing could be a challenge. Another possible approach is to explore implementing your own IHttpControllerActivator that sets the OrganizationId property. Yes, for this to work, you will need a base controller or the property defined in the individual controllers, when not derived from the base. If you go with base, you can cast IHttpController type to the base and set the property. Or you can resort to reflection.
I'm working on an ASP.NET MVC3 app, and I'd like to make a call from the view to the controller and store that information as a Dictionary.
Controller code:
public Dictionary<string,int> foo()
{
Dictionary<string,int> bar = new Dictionary<string,int>();
bar.Add("test",100);
return bar;
}
View code:
#{ Dictionary<string,int> foobar = Html.Action("foo"); }
...
<div>#foobar["test"]</div>
I can get the view to work if I use var foobar = Html.Action("foo");, but then it just says that foobar is of type System.Web.Mvc.MvcHtmlString, so I can't do much with it.
Is there any built-in functionality that I'm missing, or should I just use something like a JSON result for this?
EDIT: Additional Info
It should be noted also, that in the VS2010 debugger, it recognizes foobar properly as {System.Collections.Generic.Dictionary'2[System.String,System.Int32]}, so the issue is that foobar isn't resolving correctly. The error thrown is:
Compiler Error Message: CS0021: Cannot apply indexing with []
to an expression of type 'System.Web.Mvc.MvcHtmlString'
EDIT 2
These are the errors that came with casting the result to a dictionary:
Url.Action("foo").ToDictionary<string,int>(); returned CS1501: No overload for method 'ToDictionary' takes 0 arguments for obvious reasons, but the VS2010 debugger recognizes that the result is a Dictionary:
But when I add parameters (Url.Action("foo").ToDictionary<string,int>(x=>x);), it stops recognizing that it is a dictionary, although I'm not sure those are the proper params (it's based off what I found here.
You should try as much as possible to keep views dumb. The controller has to push the data to the view. For some reason if you want to perform call a function that performs complex calculations then a better idea would be create a separate static class and move the function from controller to that class. The Html.Action is for entirely a different purpose. Your controllers should always return action results not dictionaries or other types.
A call to Html.Action("foo") is supposed to return a string. What this is doing is calling an action method that renders the child view as a string. See http://msdn.microsoft.com/en-us/library/ee721266
You could call a static utility method , see Is this an Extension or Helper Method in MVC3?
I've been looking at this Stackoverflow question and have the answer implemented. It works all fine and dandy until I get to call HtmlHelper.Partial in my helper method, which is listed below. I know it might not be the best code, but this is until I can refactor more of the app. The error it throws is
Previous method 'ViewContext.get_TempData();' requires a return value or an exception to throw.
Am I missing mocking something, or is there a better way to render a usercontrol?
Edit Ok I did miss something, I didn't call mocks.Replay(). Now have another error which it wants something named controller in routeData...progress.
Edit #2 Clarifying I'm trying to mock the call to HtmlHelper.Partial(partialPath, model), I just want that to return whatever partialPath I send in I suppose, or at least not blowup. I did find this page http://andrevianna.com/blog/?p=8 which was very helpful and I almost got things working. This was helpful as well http://farm-fresh-code.blogspot.com/2009/10/mocking-htmlhelper-class-with.html
public static string RenderRateDetails(this HtmlHelper html, string partialPath, RatesViewData model, RateDetailType type)
{
switch (type)
{
case RateDetailType.AR:
if (model.ExistingRateDetailAR != null)
return html.Partial(partialPath, model).ToString();
break;
case RateDetailType.AP:
if (model.ExistingRateDetail != null)
return html.Partial(partialPath, model).ToString();
break;
}
return string.Empty;
}
I think the example given at 'farm fresh code' is the right way to go, you can't directly mock the HtmlHelper, but you can build an instance where all of it's dependencies are mocked.
When you're code calls html.Partial(partialPath, model).ToString(), the HtmlHelper calls properties and methods on the dependencies that you mocked, and you get errors if these don't return reasonable default values.
In this case it looks like the TemplateData property of the mocked ViewContext object was called, and I imagine it returned null, hence:
Previous method 'ViewContext.get_TempData();' requires a return value or an exception to throw.
Once you mock this property, you should be able to get past this error, but you might need to mock a few more things before you get it all working.
It might save you some time to take a look at the MVC source code to see what gets called in the Partial method. You can get that here http://aspnet.codeplex.com/releases/view/58781.
EDIT
BTW. The TempData property returns a System.Web.Mvc.TempDataDictionary. Mocking the property to return an empty instance of one of those should solve the immediate problem.
Have you considered using Display and Editor templates for your user controls rather than extending HtmlHelper?
I used to do the same thing quite a lot in the early MVC versions, but I have switched almost completely to using templates now.
I'm trying out the Razor ViewEngine from ASP.NET MVC 3 Preview 1 and I'm running into an issue trying to using the Any() extension method.
Here's the code I use to set the property in the controller:
ViewModel.Comparisons = DB.Comparisons.Where(c => c.UserID == this.UserID).ToArray();
Here's the code in the View where I try to use Any():
#if (!View.Comparisons.Any()) {
<tr>
<td>You haven't not started any comparisons yet. #Html.Action("Start a new comparison?", "create", "compare")</td>
</tr>
}
I get an exception that says:
'System.Array' does not contain a definition for 'Any'
I've tried adding the System.Linq namespace to both the pages\namespaces section of web.config, and adding a #using System.Linq line at the top of the view, neither of which made a difference. What do I need to do to have access to the LINQ extension methods?
Update: It looks like it has to do with the fact that it's a property of a dynamic object - it works if I manually cast it to IList<T>.
Unfortunately you cannot call extension methods on values that are declared as dynamic. In this case ViewModel returns dynamic values, so the compiler doesn't know the type, so calls to extension methods cannot be found.
I recommend one of the following:
Use a strongly-typed view. This way you will also get full Intellisense in Visual Studio, once that is supported.
Cast the values to IList explicitly and then call the extension method. This way the compiler can do the right mapping.
It appears to be a bug with the dynamic View property. If you change your #if statement to something like
#if(!(new List<string>().Any())) { }
You'll see that it works.