odata routing not working as expected - asp.net-mvc

This is my first OData application with asp.net MVC and i am not able to make it work. I need to return a single Summary object from the SummaryController, but facing an issue.
routing configuration -
public static IEdmModel CreateEdmModel()
{
ODataConventionModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Summary>("Summary");
return modelBuilder.GetEdmModel();
}
public static void Register(HttpConfiguration config)
{
config.Routes.MapODataRoute("OData", "odata", CreateEdmModel());
config.EnableQuerySupport();
config.EnableSystemDiagnosticsTracing();
}
controller and action method -
public class SummaryController : ODataController
{
public Summary Get()
{
//....
return someObj;
}
}
The route that does not work -
/odata/Summary
Can anyone please help me understand how can i make the routing work?

In the model, you have setup Summary to be an EntitySet, this would return a collection of Summary objects. If you want that URL to always return one object then you need a Singleton.
The CreateEdmModel method should look like this:
public static IEdmModel CreateEdmModel()
{
ODataConventionModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.Singleton<Summary>("Summary");
return modelBuilder.GetEdmModel();
}

Related

Web API NOT returning single record, returning list instead . MVC 4

My Web API has two methods hooked up to a repository.
When I make a call to
"api/Cust/GetCustomers"
the full list of customers in my database is being returned. This is fine. As a heads up, i'm using Northwind so the IDs for a Customer are a group of letters. eg - ALFKI or ANTON
When I make a call to a specific CustomerID, for example
"api/Cust/GetCustomers/alfki"
I don't get an error, but the same list from above(containing all customers in the database) is returned. I'm finding this strange because my impression would be that i'd get a not found error if something is incorrect in my controller or repository.
Does anybody with experience know how something like this happens.
I have an already completed example to work off of, and in that example navigating to a specific will return records only for that customer, which is what i'm looking to do.
Here is the code in my api controller, which is almost identical
I'm thinking there must be something subtle in the routing configs that could cause this without causing an error
CustomersAPIController.cs
public class CustomersAPIController : ApiController
{
//
// GET: /CustomersAPI/
private INorthwindRepository _repo;
public CustomersAPIController(INorthwindRepository repo)
{
_repo = repo;
}
//This routing doesn't work, but if it is a possible issue,
the call for a specific customer wasn't working before I added it
[Route("api/Cust/GetOrders({id})")]
public IQueryable<Order> GetOrdersForCustID(string id)
{
return _repo.GetOrdersForCustID(id);
}
[Route("api/Cust/GetCustomers")]
public IQueryable<Customer> GetAllCustomers()
{
return _repo.GetCustomers();
}
[HttpGet]
[Route("api/Cust/GetCustomers/alfki")]
public Customer GetCustomerByID(string id)
{
Customer customer = _repo.GetCustomerByID(id);
return customer;
}
//===========================================
protected override void Dispose(bool disposing)
{
_repo.Dispose();
base.Dispose(disposing);
}
}
and here is my repo
repo.cs
public interface INorthwindRepository:IDisposable
{
//private northwndEntities _ctx = new northwndEntities();
IQueryable<Customer> GetCustomers();
IQueryable<Customer> TakeTenCustomers();
Customer GetCustomerByID(string id);
IQueryable<Order> GetOrders();
IQueryable<Order> GetOrdersForCustID(string id);
Order FetchOrderByID(int orderID);
}
public class NorthwindRepository : INorthwindRepository
{
northwndEntities _ctx = new northwndEntities();
public IQueryable<Customer> GetCustomers()
{
return _ctx.Customers.OrderBy(c => c.CustomerID);
}
public IQueryable<Customer> TakeTenCustomers()
{
var foo = (from t in _ctx.Customers
select t).Take(10);
return foo;
}
public IQueryable<Order> GetOrdersForCustID(string id)
{
var orders = _ctx.Orders.Where(x => x.CustomerID == id).OrderByDescending(x=>x.OrderDate).Take(4);
return orders;
}
public Customer GetCustomerByID(string id)
{
return _ctx.Customers.Find(id);
}
public void Dispose()
{
_ctx.Dispose();
}
Here is a link to a screenshot of the url in my example to work off of, working as intended and returning the records for a specific ID
http://postimg.org/image/oup88k83f/
In this second one, it is a link to my api that I have been basing on my example to work from.
http://postimg.org/image/858t1oph9/
As mentioned above, the code is nearly identical, except for some small changes to the routing and maybe the api controller names.
If anyone has any idea what is causing this, all suggestions are appreciated.
Thank you
*Update fixed a typo in my code
My routeconfig.cs (the same as the template provided my MVC4 API selection when creating a new project)
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
please, fixed the Route for the action GetCustomerById, look:
[Route("api/Cust/GetCustomers/{id}")]
public Customer GetCustomerByID(string id)

OData V3 action in ASP.NET web api doesn't get trigger

I am using OData V3 endpoints using asp.net with webapi 2.2. I have successfully implemented CRUD operation with it. Now, I would like to add some custom actions along with CRUD operations. I have followed the article ( http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v3/odata-actions ) to create the action with OData V3 with web api.
When I type
URI:
http://localhost:55351/odata/Courses(1101)/AlterCredits
it throws following error:
<m:error><m:code/><m:message xml:lang="en-US">No HTTP resource was found that matches the request URI 'http://localhost:55351/odata/Courses(1101)/AlterCredits'.</m:message><m:innererror><m:message>No routing convention was found to select an action for the OData path with template '~/entityset/key/unresolved'.</m:message><m:type/><m:stacktrace/></m:innererror></m:error>
I have also tried adding a custom route convetion for non-bindable actions. (https://aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/OData/v3/ODataActionsSample/ODataActionsSample/App_Start/WebApiConfig.cs ) Not sure if I have to use this.
Here is my code:
WebApiConfig.cs :---
namespace ODataV3Service
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
IList<IODataRoutingConvention> conventions = ODataRoutingConventions.CreateDefault(); //Do I need this?
//conventions.Insert(0, new NonBindableActionRoutingConvention("NonBindableActions"));
// Web API routes
config.Routes.MapODataRoute("ODataRoute","odata", GetModel(), new DefaultODataPathHandler(), conventions);
}
private static IEdmModel GetModel()
{
ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.ContainerName = "CollegeContainer";
modelBuilder.EntitySet<Course>("Courses");
modelBuilder.EntitySet<Department>("Departments");
//URI: ~/odata/Course/AlterCredits
ActionConfiguration atlerCredits = modelBuilder.Entity<Course>().Collection.Action("AlterCredits");
atlerCredits.Parameter<int>("Credit");
atlerCredits.Returns<int>();
return modelBuilder.GetEdmModel();
}
}
}
CoursesController.cs:----
[HttpPost]
//[ODataRoute("AlterCredits(key={key},credit={credit})")]
public async Task<IHttpActionResult> AlterCredits([FromODataUri] int key, ODataActionParameters parameters)
{
if (!ModelState.IsValid)
return BadRequest();
Course course = await db.Courses.FindAsync(key);
if (course == null)
{
return NotFound();
}
int credits = course.Credits + 3;
return Ok(credits);
}
Global.asax:----
namespace ODataV3Service
{
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
}
}
}
I have done research online and found this link. Web API and OData- Pass Multiple Parameters But this one is for OData V4. I am using OData V3 and Action.
Thanks,
First, your action AlterCredits is defined as:
ActionConfiguration atlerCredits = modelBuilder.Entity<Course>().Collection.Action("AlterCredits");
It means AlterCredits bind to the collection of Course.
Second, your method AlterCredits in your controller is defined as:
public async Task<IHttpActionResult> AlterCredits([FromODataUri] int key, ODataActionParameters parameters)
{
...
}
It means AlterCredits listen to the call on the entity of Course.
Therefore, you got the No HTTP resource was found error message.
Based on your sample code, I create a sample method for your reference:
[HttpPost]
public async Task<IHttpActionResult> AlterCredits(ODataActionParameters parameters)
{
if (!ModelState.IsValid)
return BadRequest();
object value;
if (parameters.TryGetValue("Credit", out value))
{
int credits = (int)value;
credits = credits + 3;
return Ok(credits);
}
return NotFound();
}
Then, if you send a request:
POST ~/odata/Courses/AlterCredits
Content-Type: application/json;odata=verbose
Content: {"Credit":9}
You can get a response like this:
{
"d":{
"AlterCredits":12
}
}
For your questions:
IList conventions = ODataRoutingConventions.CreateDefault(); //Do I need this?
Answer: No, you needn't. Just using the default as:
config.Routes.MapODataServiceRoute("ODataRoute", "odata", GetModel());
//[ODataRoute("AlterCredits(key={key},credit={credit})")]
Answer: No, you needn't the ODataRouteAttribute for bind action.
Thanks.

Getting System.InvalidOperationException exception with breeze and WebApi

I am very new to breeze. I have downloaded the template for AngularBreeze and trying to create a sample application but i am stuck on Breeze WebApi Controller.
BreezeController]
public class ProductsBreezeController : ApiController
{
private readonly ProductRepository context;
public ProductsBreezeController()
{
context = new ProductRepository();
}
[HttpGet]
public string Metadata()
{
return context.Metadata();
}
//// GET api/productsbreeze
public IQueryable<Product> GetAllProducts()
{
return context.TodoLists;
}
}
public class ProductRepository : EFContextProvider<SampleEntities>
{
public DbQuery<Product> TodoLists
{
get { return Context.Products; }
}
}
Exception Message
Multiple actions were found that match the request: System.String Metadata() on type AngularWebApi.ApiControllers.ProductsBreezeController System.Linq.IQueryable`1[AngularWebApi.DataAccess.Model.Product] GetAllProducts() on type AngularWebApi.ApiControllers.ProductsBreezeController
ExceptionType: "System.InvalidOperationException"
You need to set your breezewebapiconfig.cs up to accept an action parameter as we'll. currently you have a controller only probably.
Open appstart folder and BreezeWebApiConfig.cs and add it there (should see something like ) -
Breeze/{controller}/{action}/{id}
And you need to add the action part in there
Edit
In your question it clearly shows the route for that controller action is api/productsbreeze. If that is the route you are hitting then you need to adjust that route to accept an action as well. If it is the Breeze route you are trying to hit then add an HttpGet controller attribute on the action
//// GET api/productsbreeze
[HttpGet]
public IQueryable<Product> GetAllProducts()
{
return context.TodoLists;
}
You need to make sure that your BreezeWebApiConfig is also registered in the Global.asax, of course.
Requesting URL should be matched with Breeze Api Configuration.
Server Side Configuration
GlobalConfiguration.Configuration.Routes.MapHttpRoute("BreezeApi", "breeze/{controller}/{action}");
Client Side
var manager = new breeze.EntityManager("/breeze/ProductsBreeze");

ASP.NET MVC project filterattribute to a normal function

I have an asp.net mvc project which is divided into projectlayers:
Test has the controller which calls the taskproject. In task a function calls the database and returns the data.
TestProject:
Homecontroller()
{
public ActionResult Index()
{
List<Person> pers = PersonTask.getPersons();
return View(pers);
}
}
TaskProject:
public static List<Person> getPersons()
{
using (var context = new FilterTestEntities())
{
return context.People.OrderBy(p => p.PersonID).ToList();
}
}
I want to have a filter on the data i return to the controller. I know there is a FilterAttribute, but as far as I know this is only possible on an action function. Is it possible to put a filterAttribute on my getPersons() function. Like:
[PersonFilter]
public static List<Person> getPersons()
Thanks.

I want to use session in the asp.net mvc controller constructor

I'm new to Mvc.
Sorry to my english. ^^
I have some question about asp.net MVC session in the controller.
The Scenario things that I want to do is like follows..
First of all, My development circumstance is entityframework and mvc3.
When Someone logged in each one has different database. So, Each has connect different database.
So, Each person has his own session value which is database connection string. So far so good.
I have simple database Repository and at the each repository's constructor can change database connection.
At controller which calls Repository class, I need session value. But As I know Controller's construction can't keep session value. right?
I want your good advice. Thanks in advance.
Code samples are below:
public class MasterRepository
{
DBEntities _db;
public MasterRepository(string con)
{
_db = new DBEntities(con);
}
}
public class TestController : Controller
{
private string con;
MasterRepository _db;
public TestController()
{
_db = new MasterRepository(Session["conn"].ToString()); // Session is null I want to solve this Part...
}
public ActionResult Index()
{
string con = Session["conn"].ToString(); // Session is assigned.
return View();
}
}
These should explain what's happening to cause Session to be null, and give you a few possible solution options:
Is ASP.NET MVC Session available at any point durign controller construction
Why my session variables are not available at construction of a Controller?
Session null in ASP.Net MVC Controller Constructors
I think you have missed out the "service" part of the controller - service - repository pattern:
http://weblogs.asp.net/fredriknormen/archive/2008/04/24/what-purpose-does-the-repository-pattern-have.aspx
But when you go down this path you will probably also need to learn IoC as well.
Then your code would look more like:
public class MasterRepository
{
public Foo GetAllFoo()
{
return ObjectContextManager.GetObjectContext().AsQueryable().ToList();
}
}
public class MasterService
{
MasterRepository _repository;
public MasterService(MasterRepository repository) // use IoC
{
_repository = repository;
}
public Foo GetAllFoo()
{
return _repository.GetAllFoo();
}
}
public class TestController : Controller
{
MasterService _service;
public TestController(MasterService service) // use IoC
{
_service = service;
}
public ActionResult Index()
{
var model _service.GetAllFoo();
return View(model);
}
}

Resources