Overload Index() Method in MVC Controller - asp.net-mvc

I want to access both /Blog and /Blog/1 where "1" is the ID of the Blog. Here is my code:
'
' GET: /Blog/
Function Index() As ViewResult
Return (View(db.Blogs.ToList()))
End Function
'
' GET: /Blog/(Integer)
Function Index(id As Integer) As ViewResult
Dim blog As Blog = db.Blogs.Find(id)
Return View("Details", "_MyLayout", blog)
End Function
It gives the error:
Server Error in '/' Application.
The current request for action 'Index' on controller type
'BlogController' is ambiguous between the following action methods:
System.Web.Mvc.ViewResult Index() on type
GemcoBlog.GemcoBlog.BlogController System.Web.Mvc.ViewResult
Index(Int32) on type GemcoBlog.GemcoBlog.BlogController
Description: An unhandled exception occurred during the execution of
the current web request. Please review the stack trace for more
information about the error and where it originated in the code.
Exception Details: System.Reflection.AmbiguousMatchException: The
current request for action 'Index' on controller type 'BlogController'
is ambiguous between the following action methods:
System.Web.Mvc.ViewResult Index() on type
GemcoBlog.GemcoBlog.BlogController System.Web.Mvc.ViewResult
Index(Int32) on type GemcoBlog.GemcoBlog.BlogController
Source Error:
An unhandled exception was generated during the execution of the
current web request. Information regarding the origin and location of
the exception can be identified using the exception stack trace below.
How can I overload the Index() method?
Edit:
I am also trying to combine them like so:
'
' GET: /Blog/
Function Index(id As Integer) As ViewResult
If (id) Then
Dim blog As Blog = db.Blogs.Find(id)
'Return View(blog)
Return View("Details", "_MyLayout", blog)
Else
Return (View(db.Blogs.ToList()))
End If
'Return View(db.Blogs.Where(Function(x) x.Name = "Test").ToList())
End Function
However, the error I get is:
Server Error in '/' Application.
The parameters dictionary contains a null entry for parameter 'id' of
non-nullable type 'System.Int32' for method 'System.Web.Mvc.ViewResult
Index(Int32)' in 'Blog.Blog.BlogController'. An optional
parameter must be a reference type, a nullable type, or be declared as
an optional parameter. Parameter name: parameters
Description: An unhandled exception occurred during the execution of
the current web request. Please review the stack trace for more
information about the error and where it originated in the code.
Exception Details: System.ArgumentException: The parameters dictionary
contains a null entry for parameter 'id' of non-nullable type
'System.Int32' for method 'System.Web.Mvc.ViewResult Index(Int32)' in
'Blog.Blog.BlogController'. An optional parameter must be a
reference type, a nullable type, or be declared as an optional
parameter. Parameter name: parameters
Source Error:
An unhandled exception was generated during the execution of the
current web request. Information regarding the origin and location of
the exception can be identified using the exception stack trace below.

You cannot have 2 actions on the same controller accessible with the same HTTP verb. So either change the action name or you will have to disambiguate using different HTTP verbs. For example:
<HttpPost>
Function Index(id As Integer) As ViewResult
Dim blog As Blog = db.Blogs.Find(id)
Return View("Details", "_MyLayout", blog)
End Function
But since the other action also seems to be fetching data I guess that you don't want to make it POST accessible only. So simply rename it in this case. Sticking to standard RESTful conventions you could use Index for returning a list of resources and Show to return a particular resource:
Function Index() As ViewResult
Return (View(db.Blogs.ToList()))
End Function
'
' GET: /Blog/(Integer)
Function Show(id As Integer) As ViewResult
Dim blog As Blog = db.Blogs.Find(id)
Return View("Details", "_MyLayout", blog)
End Function

There are a couple of ways you could do this. The easiest would be to rename the first method to "ShowBlog" or whatever you want, then setup a route in your global.asax that routes to the /Blog route without a parameter.
For example (in c#):
routes.MapRoute("Blog", "Blog", new { controller = "Blog", action = "ShowBlog" });
Make sure the MapRoute comes before the default route.
To make your second method work, you would need to make the id nullable, and then check for null in your method.

Since it is not nullable, it automatically assumes you are providing an id by default. Make the Id a Nullable Integer, and it will work for both URLs.
Function Index(id As Nullabe( Of Integer )) As ViewResult

Small amendment to Erik's post to make it work (I am using MVC4)
routes.MapRoute("Blog", "Blog/{id}", new { controller = "Blog", action = "ShowBlog" });

Related

OData unbound function opt-in

I have a controller which contains certain kinds of methods. This controller is housed in a separate assembly. The idea being that other APIs can then opt in if they want to use this functionality as unbound functions.
The problem is that if I don't implement this functionality with the ODataConventionModelBuilder OData returns the following error:
System.AggregateException
HResult=0x80131500
Message=One or more errors occurred.
Source=<Cannot evaluate the exception source>
StackTrace:
<Cannot evaluate the exception stack trace>
Inner Exception 1:
InvalidOperationException: The path template 'TestFunction' on the action 'TestFunction' in controller 'OptInFunctions' is not a valid OData path template. Resource not found for the segment 'TestFunction'
If I remove the ODataRouteAttribute from the functions then OData does not return an error during startup, but a 404 when I want to call the function.
My controller with the opt-in methods looks something like this:
public class OptInFunctions : ODataController
{
[HttpGet]
[ODataRoute("TestFunction")]
public ActionResult<bool> TestFunction()
{
return Ok(true);
}
}
How can I make an OData unbound function opt-in?

Generate a URL inside Application_Start()

ASP.NET MVC 5
T4MVC (https://github.com/T4MVC/T4MVC)
ABP (http://aspnetboilerplate.com/)
I know it is possible to use T4MVC to generate a URL based on a specific action using UrlHelper(). See SO and here. However I ultimately need to do this during Application_Start() due to ABP requirements.
So in essence within Application_Start() I have:
//Areas
AreaRegistration.RegisterAllAreas();
//Routes
RouteConfig.RegisterRoutes(RouteTable.Routes);
//Get the URL
var urlHelper = new UrlHelper();
var url = urlHelper.ActionAbsolute(MVC.Home.Index())
however this blows up with the following exception:
'UrlHelper.ActionAbsolute(MVC.Home.Index())' threw an exception of type 'System.NullReferenceException'
Data: {System.Collections.ListDictionaryInternal}
HResult: -2147467261
HelpLink: null
InnerException: null
Message: "Object reference not set to an instance of an object."
Source: "T4MVCExtensions"
StackTrace: " at System.Web.Mvc.T4Extensions.ActionAbsolute(UrlHelper urlHelper, ActionResult result)"
TargetSite: {System.String ActionAbsolute(System.Web.Mvc.UrlHelper, System.Web.Mvc.ActionResult)}
however if I inspect the contents of things (at runtime) they appear to all be populated:
MVC.Home.Index()
{T4MVC_System_Web_Mvc_ActionResult}
Action: "Index"
Controller: "Home"
Protocol: null
RouteValueDictionary: {System.Web.Routing.RouteValueDictionary}
MVC.Home.Index().GetRouteValueDictionary()
{System.Web.Routing.RouteValueDictionary}
Count: 3
Keys: Count = 3
Values: Count = 3
Results View: Expanding the Results View will enumerate the IEnumerable
So can anyone shed any light on why this is blowing up? Or put another way how do I get the absolute url for an action using T4MVC inside Application_Start()?
The common theme between the links you provided were that those calls were made in an action which means that a request has already been materialized. Meaning that a context already exists. At startup there are no requests as yet. That is why you are getting the error.

OData routing for function with 2 parameters

Im creating OData controller and want it to support function with 2 params.
Here is my current code.
OData cofig:
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.Namespace = "hop";
builder.EntitySet<ScheduleDTO>("Schedules");
var function = builder.Function("GetByEntityAndJurisdiction");
function.Parameter<Guid>("EntityId");
function.Parameter<Guid>("JurisdictionId");
function.ReturnsCollectionFromEntitySet<ScheduleDTO>("Schedules");
Controller:
[ODataRoutePrefix("Schedules")]
public class ScheduleODataController : BaseODataManager, IScheduleODataManager
{
[ODataRoute]
public async Task<IHttpActionResult> GetAsync(ODataQueryOptions<ScheduleDTO> options)
{
.....
return Ok(schedules.Select(x => Mapper.Map<ScheduleDTO>(x)));
}
[HttpGet]
[ODataRoute("GetByEntityAndJurisdiction(EntityId={entityId}, JurisdictionId={jurisdictionId})")]
public async Task<IHttpActionResult> GetByEntityAndJurisdiction(ODataQueryOptions<ScheduleDTO> options, [FromODataUri] Guid entityId, [FromODataUri] Guid jurisdictionId)
{
.....
return Ok(schedules.Select(x => Mapper.Map<ScheduleDTO>(x)));
}
}
Starting my app, I have following error:
A first chance exception of type 'System.InvalidOperationException' occurred in System.Web.OData.dll
Additional information: The path template 'Schedules/GetByEntityAndJurisdiction(EntityId={entityId}, JurisdictionId={jurisdictionId})' on the action 'GetByEntityAndJurisdiction' in controller 'ScheduleOData' is not a valid OData path template. The request URI is not valid. Since the segment 'Schedules' refers to a collection, this must be the last segment in the request URI or it must be followed by an function or action that can be bound to it otherwise all intermediate segments must refer to a single resource.
How to resolve this problem? Thanks in advance.
#Vladimir
In your controller, you add a prefix attribute [ODataRoutePrefix("Schedules")] on the controller. Doing so will add the prefix string at head of all the [ODataRoute] in the same controller. So, for below action
public async Task<IHttpActionResult> GetByEntityAndJurisdiction(ODataQueryOptions<ScheduleDTO> options, [FromODataUri] Guid entityId, [FromODataUri] Guid jurisdictionId)
{...}
the full Uri template should be:
Schedules/GetByEntityAndJurisdiction(EntityId={entityId}, JurisdictionId={jurisdictionId})
Obviously, This Uri is invalid because:
The collection of Schedules doesn't have a bound function named GetByEntityAndJurisdiction
Even though GetByEntityAndJurisdiction is a bound function, you should call the bound function through it's namespace-qualified function name.
Maybe, It's confused that you have build the function as the following codes:
var function = builder.Function("GetByEntityAndJurisdiction");
However, it means to build an unbound function. An unbound function is called through function import by issuing a GET request to a URL identifying the function import and passing parameter values using inline parameter syntax. The canonical URL for a function import is the service root, followed by the name of the function import.
So, you can change your codes as follows to make it work:
If you want to keep the model schema unchanged, that is to build GetByEntityAndJurisdiction as unbound function, please remove the ODataRoutePrefix("Schedules")] from your controller. Or create a new controller (any controller), move the action into the new controller but don't add the Prefix attribute.
If you want to change the schema and keep the controller unchanged, that is to GetByEntityAndJurisdiction as bound function.
Please do as follows :
var entity = builder.EntitySet<ScheduleDTO>("Schedules").EntityType;
var function = entity.Collection.Function("GetByEntityAndJurisdiction");
...
For more information about function, you can refer to OData.Org or Function Sample page, or Function blog.

Breeze Web API Controller Method Name Convention

In Official Docs about breeze and the Web API controller, we see some kind of naming convention for the method names on web Api controller. For example, for the Todo entity type, there is a Todos() method.
Suppose I have an entityType "Customer". Then I create a method on apiController:
[HttpGet]
public IQueryable<Customer> GetCustomers() { ... }
In my client javascript app, I run EntityQueries like that:
//api method: GetCustomers
//type name: Customer
var query = EntityQuery.from("GetCustomers");
manager.execute(query); //works
manager.fetchEntityByKey("Customer", 53) //doesn't works!
It fails, I receive the folowwing error:
No HTTP resource was found that matches the request URI
'http://localhost:4119/api/SomeController/Customers?$filter=Id eq 53'
So, Am I forced to rename my GetCustomers method to Customers() or Am I missing something ?

ASP.NET MVC: The parameters dictionary contains a null entry for parameter

I am getting an error on a web site deployed on client machines. At first I thought it was caused by Windows authentication and multiple error providers as per my answer to the question here. But I have added Elmah to do logging on the client and am noticing a pattern....
The controller signature is:
public ActionResult Class(int subjectId, int classGroupId)
The request that is failing is listed in the elmah log:
HTTP_REFERER - https://someUrl.com/Results/Class/4372/16202
however the path info only has one parameter.
PATH_INFO - /Results/Class/4372/
The error message is
System.ArgumentException The parameters dictionary contains a null
entry for parameter 'subjectId' of non-nullable type 'System.Int32'
for method 'System.Web.Mvc.ActionResult Class(Int32, Int32)' in
'MyNamespace.Controllers.ResultsController'. An optional
parameter must be a reference type, a nullable type, or be declared as
an optional parameter. Parameter name: parameters
So somewhere along the line I am losing a parameter. Curious that the error message says the first parameter is missing and not the second.
I have a route set up for this action:
routes.MapRoute(
"ClassResults", // Route name
"{controller}/{action}/{subjectId}/{classGroupId}", // URL with parameters
new { controller = "Results", action = "Class" } // Parameter defaults
);
How can I debug this error, what should I be looking at to determine what the issue is?

Resources