OData unbound function opt-in - odata

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?

Related

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 ?

Overload Index() Method in MVC Controller

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

Error Handling in asp.net mvc 3

Is there a built in or a proper way to handle errors in asp.net mvc 3?
This is what I want to do:
If the application crashes, or throws an error, it goes to a specific error page.
I can throw my own error from the controller action. (and it goes to an error page).
I found the following ways:
I see there is a long way to do it
here. (for v1 and v2 but also
applies to v3).
Using errorhandle attribute here.
How do I handle this the proper way?
If the solution is similar or is like #1 in the list above, I am using ninject and I have not created a base class. How do I still do this?
For Global Error Handling
All you have to do is change the customErrors mode="On" in web.config page
Error will be displayed through Error.cshtml resides in shared folder.
Make sure that Error.cshtml Layout is not null.
[It sould be something like: #{ Layout = "~/Views/Shared/_Layout.cshtml"; }
Or remove Layout=null code block]
A sample markup for Error.cshtml:-
#{ Layout = "~/Views/Shared/_Layout.cshtml"; }
#model System.Web.Mvc.HandleErrorInfo
<!DOCTYPE html>
<html>
<head>
<title>Error</title>
</head>
<body>
<h2>
Sorry, an error occurred while processing your request.
</h2>
<p>Controller Name: #Model.ControllerName</p>
<p>Action Name : #Model.ActionName</p>
<p>Message: #Model.Exception.Message</p>
</body>
</html>
For Specific Error Handling
Add HandleError attribute to specific action in controller class. Provide 'View' and 'ExceptionType' for that specific error.
A sample NotImplemented Exception Handler:
public class MyController: Controller
{
[HandleError(View = "NotImplErrorView", ExceptionType=typeof(NotImplementedException))]
public ActionResult Index()
{
throw new NotImplementedException("This method is not implemented.");
return View();
}
}
I would suggest implementing a custom HandleErrorAttribute action filter.
See this link for more details:
http://msdn.microsoft.com/en-us/library/dd410203%28v=vs.90%29.aspx
Setting up a HandleErrorAttribute action filter gives you complete control over which actions are handled by the filter, and it's easy to set at the controller level, or even at the site level by setting it up on a custom base controller, and having all of your controllers inherit from the base controller.
Something else I do with this, is I have a separate HandleJsonErrorAttribute that responds to Ajax calls by returning a Json response, rather than the custom page.
UPDATE:
Per some questions below, here is an example of a HandleJsonErrorAttribute that I use:
public class HandleJsonErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
var serviceException = filterContext.Exception as ServiceException;
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
filterContext.Result = new JsonResult { Data = new { message = serviceException == null ? "There was a problem with that request." : serviceException.Message } };
filterContext.ExceptionHandled = true;
}
}
And here is the jQuery that I use to handle these unhanded exceptions:
$(document).ajaxError(function (event, jqXHR, ajaxSettings, thrownError) {
showPopdown($.parseJSON(jqXHR.responseText).message);
});
This allows my Ajax methods to be very lightweight -- they just handle returning normal Json, and in the event of an unhanded exception, a message w/ an error status code gets wrapped in Json and returned.
Also, in my implementation, I have a custom ServiceException that I throw from services, and this sends the message from the service layer instead of a generic message.
The easiest way I think you can do that is using the elmah library.
Take a look at this: http://code.google.com/p/elmah/wiki/MVC and this
http://www.hanselman.com/blog/ELMAHErrorLoggingModulesAndHandlersForASPNETAndMVCToo.aspx
I think the easiest way is using ExceptionHandler attribute since it's ready to use anytime you create a new ASP.NET MVC 3 project. You can still configure Web.config to use a custom error page and handling exceptions in global Application_Error method as usual but when an exception occurs the URL is not displayed as nice as the new MVC 3's way.
you can create custom exception in MVC if you want to customize a way of exception handling.
you can find useful post here .
http://www.professionals-helpdesk.com/2012/07/creating-custom-exception-filter-in-mvc.html

Resources