OData V4 attribute routing not using the right action - odata

i am experiencing a problem with attribute routing in OData V4. I have a simple Customers model class and a Controller, which has 2 Get methods. However, whenever i try to GET a specific Customer like /Customers(7) , it always uses the second Get method to get all customers, but never breaks into the method with the id parameter. What am i missing here? Thank you!
The controller:
using System.Data;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.OData;
using System.Web.OData.Routing;
using API.Models;
namespace API.Controllers
{
public class CustomersController : ODataController
{
private OContext db = new OContext();
// GET: Customers(5)
[HttpGet]
[ODataRoute("Customers({id:int})")]
[EnableQuery]
public SingleResult<Customers> Get([FromODataUri] int id)
{
IQueryable<Customers> result = db.Customers.Where(p => p.Id == id).AsQueryable();
return SingleResult.Create(result);
}
// GET: Customers
[HttpGet]
[ODataRoute("Customers")]
[EnableQuery]
public IQueryable<Customers> Get()
{
return db.Customers.AsQueryable();
}
}
}
The WebApiConfig:
using System.Web.Http;
using System.Web.OData.Builder;
using System.Web.OData.Extensions;
using API.Models;
using Microsoft.OData.Edm;
namespace API
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.MapHttpAttributeRoutes();
config.MapODataServiceRoute("odata", null, GetEdmModel());
config.EnsureInitialized();
}
private static IEdmModel GetEdmModel()
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.Namespace = "API.Controllers";
builder.ContainerName = "DefaultContainer";
builder.EntitySet<Customers>("Customers").EntityType.HasKey(c => c.Id);
builder.EntitySet<Customers_CustomField> ("Customers_CustomFields");
var edmModel = builder.GetEdmModel();
return edmModel;
}
}
}

Related

Is there get method which not found while run web api service

I'm creating a web api controller and implement ([HttpGet])parameterized get method with datatype class but when I run this its show 404 Not Found.
When i am implementing normal datatype like string or int its show me the answer or datatype like List still it's giving me answer but when I directly declare datatype as class like Student its show 404 not found error. I don't know why it's so. I am trying to learn web api with mvc please help me.
I am creating one simple class Student and one studentapicontroller.In my api controller, I create get method with datatype class and for testing purpose i make other get method with different datatype
Student.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace epay.Models
{
public class student
{
public int id { get; set; }
public string name { get; set; }
}
}
studentapiController :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using epay.Models;
namespace epay.Controllers
{
public class studentapiController : ApiController
{
// GET api/studentapi
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/studentapi/5
{
return "value";
}
// POST api/studentapi
public void Post([FromBody]string value)
{
}
// PUT api/studentapi/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/studentapi/5
public void Delete(int id)
{
}
//POST api/studentapi/
[HttpGet]
public student getdetails(int id, string na)
{
student st = new student();
st.id = id;
st.name = na;
return st;
}
//GET api/studentapi/getstud
[HttpGet]
public List<student> getstud()
{
List<student> lst = new List<student>();
student st = new student();
st.name = "manlai";
st.id = 5;
lst.Add(st);
return lst;
}
}
}
I just want getdetails result or how to do if I want my method datatype as a class and I am passing parameter with my get method how to do this
Make getdetails route like below because it is conflicting with get
[HttpGet("getdetails")]
public student getdetails(int id, string na)
{
student st = new student();
st.id = id;
st.name = na;
return st;
}
you can call route something like this with querystring /studentapi/getdetails?id=1&na=test

Why am I getting 'No non-OData HTTP route registered.'

I am trying to implement Odata on a controller for the first time and am getting the error in the subject. I have tried many different methods i have found here and on other sites but I am not having any luck.
I have the following controller declared.
using System.Web.Http;
using System.Collections.Generic;
using AutoMapper;
using Microsoft.AspNet.OData;
using System.Linq;
using Microsoft.AspNet.OData.Routing;
using Microsoft.AspNet.OData.Query;
namespace Site.API.Areas.Prices.Controllers
{
public class PricesController : ODataController
{
IPricesManager pricesManager;
public PricesController()
{
this.pricesManager = new PricesManager();
}
public IHttpActionResult GetPrice()
{
var result = this.PricesManager.RetrieveAll();
return Ok(result.AsQueryable());
}
}
}
And the following in my WebApiConfig
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<PricesModel>("Prices").EntityType.HasKey(p => p.Facility);
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: null,
model: builder.GetEdmModel());
If I were to call any one of my normal API's I would use:
https://localhost/API/Site.API/Prices/Prices
For OData I am trying
https://localhost/API/Site.API/odata/Prices
And I get the subject error as a result. What am I missing?

Web API - Entity Framework is trying to ruin my life

I have a simple database with five tables bound together by relationships. I've built OData apps before and like the model, but wanted to make the leap to Web API. The problem is - when I make my call to the ProductType table for example - it returns EVERY record in every table! What gives?
My goal is to call a ProductType for example like this:
url/api/ProductType
(and the usual getbyId, delete, etc)
I'm using the repository pattern, and following these steps:
1. Create a Entity Framework model with the tables I want to display. Yawn.
2. Add an interface in Models, call it IProductTypeRepository, with the behaviors I want to see.
3. Add a model class (ProductModel.cs) that inherits from this interface
4. Make sure Global.asax has either default api setup or there’s a App_Start\Routeconfig and WebApiConfig class.
5. Surgery on the ValuesController class.
Maybe this is in ignorance of how WebAPI is supposed to work with relationships. Perhaps the goal is to do one call to a service - and get EVERY record across multiple tables (for example, Product and ProductDetails). If so, I think the documentation could have been a LOT clearer on this point!
============================CODE FOLLOWS!================================================
I’m going to skip the EF auto-generated code obviously – but suffice to say it contains entities and joins for five tables – ProductType, Location, Process, Step, and StepType. ProductType is a very simple table with an ID field and a description and a part # field (MaterialMasterId).
For IProductTypeRepository, here is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Perceptive.Services.Models
{
interface IProductTypeRepository
{
IEnumerable<ProductType> GetAll();
ProductType GetById(int ProductTypeID);
int Update(ProductType producttype);
ProductType Add(ProductType producttype);
void Delete(int ProductTypeId);
}
}
For ProducTTypeModel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Perceptive.Services.Models
{
public class ProductTypeModel : IProductTypeRepository
{
private PCSEntities1 context = new PCSEntities1();
public IEnumerable<ProductType> GetAll()
{
return context.ProductTypes;
}
public ProductType GetById(int ProductTypeId)
{
IQueryable<ProductType> producttypes = context.ProductTypes.Where(a => a.ProductTypeID == ProductTypeId);
return producttypes.FirstOrDefault();
}
public int Update(ProductType producttype)
{
ProductType updateProductType = context.ProductTypes.FirstOrDefault(c => c.ProductTypeID == producttype.ProductTypeID);
updateProductType.Description = producttype.Description.Trim();
updateProductType.MaterialMasterID = producttype.MaterialMasterID;
return context.SaveChanges();
}
public ProductType Add(ProductType producttype)
{
var addedProductType = context.ProductTypes.Add(producttype);
context.SaveChanges();
return addedProductType;
}
public void Delete(int producttypeid)
{
ProductType producttype = context.ProductTypes.FirstOrDefault(a => a.ProductTypeID == producttypeid);
context.ProductTypes.Remove(producttype);
context.SaveChanges();
}
}
}
… and lastly, my Controller – ProductTypeController.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
//dh added
using Perceptive.Services.Models;
namespace Perceptive.Services.Controllers
{
public class ProductTypeController : ApiController
{
IProductTypeRepository repository = new ProductTypeModel();
// GET /api/Producttype
[HttpGet]
public IEnumerable<ProductType> Get()
{
return repository.GetAll();
}
// GET /api/Producttype/5
[HttpGet]
public ProductType Get(int id)
{
ProductType producttype = repository.GetById(id);
return producttype;
}
// POST /api/ProducTtype
[HttpPost]
public void PostProductType(ProductType producttype)
{
repository.Add(producttype);
}
// PUT /api/ProductType
// DH seems scanty here. Where's the repository put?
[HttpPut]
public void PutProductType(ProductType producttype)
{
if (repository.Update(producttype) == 0)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
}
//DELETE /api/ProductType/5
[HttpDelete]
public void Delete (int id)
{
repository.Delete(id);
}
}
}
You need to use IQueryable:
interface IProductTypeRepository
{
IQueryable<ProductType> GetAll();
ProductType GetById(int ProductTypeID);
int Update(ProductType producttype);
ProductType Add(ProductType producttype);
void Delete(int ProductTypeId);
}
When you use IEnumerable it is going to pull all the records in the entity client side and then work on them. IQueryable is treated as a query until you call an action like .ToList() which will execute the query and pull the results to the client.

ASP.NET MVC WebApi: No parameterless constructor defined for this object

I have an ASP.NET MVC 4 Application that I want to implement Unit of Work Pattern.
In my Web Project I have:
IocConfig.cs
using System.Web.Http;
using NinjectMVC.Data;
using NinjectMVC.Data.Contracts;
using Ninject;
namespace NinjectMVC
{
public class IocConfig
{
public static void RegisterIoc(HttpConfiguration config)
{
var kernel = new StandardKernel(); // Ninject IoC
// These registrations are "per instance request".
// See http://blog.bobcravens.com/2010/03/ninject-life-cycle-management-or-scoping/
kernel.Bind<RepositoryFactories>().To<RepositoryFactories>()
.InSingletonScope();
kernel.Bind<IRepositoryProvider>().To<RepositoryProvider>();
kernel.Bind<INinjectMVCUow>().To<NinjectMVCUow>();
// Tell WebApi how to use our Ninject IoC
config.DependencyResolver = new NinjectDependencyResolver(kernel);
}
}
}
Global.asax
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace NinjectMVC
{
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
// Tell WebApi to use our custom Ioc (Ninject)
IocConfig.RegisterIoc(GlobalConfiguration.Configuration);
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
}
}
}
PersonsController.cs
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using NinjectMVC.Data.Contracts;
using NinjectMVC.Model;
namespace NinjectMVC.Controllers
{
public class PersonsController : ApiControllerBase
{
public PersonsController(INinjectMVCUow uow)
{
Uow = uow;
}
#region OData Future: IQueryable<T>
//[Queryable]
// public IQueryable<Person> Get()
#endregion
// GET /api/persons
public IEnumerable<Person> Get()
{
return Uow.Persons.GetAll()
.OrderBy(p => p.FirstName);
}
// GET /api/persons/5
public Person Get(int id)
{
var person = Uow.Persons.GetById(id);
if (person != null) return person;
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
// OData: GET /api/persons/?firstname=\'Hans\''
// With OData query syntax we would not need such methods
// /api/persons/getbyfirstname?value=Joe1
[ActionName("getbyfirstname")]
public Person GetByFirstName(string value)
{
var person = Uow.Persons.GetAll()
.FirstOrDefault(p => p.FirstName.StartsWith(value));
if (person != null) return person;
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
// Update an existing person
// PUT /api/persons/
public HttpResponseMessage Put(Person person)
{
Uow.Persons.Update(person);
Uow.Commit();
return new HttpResponseMessage(HttpStatusCode.NoContent);
}
}
}
When I try to surf: http://www.domain.com/Persons/Get Im getting:
No parameterless constructor defined for this object.
Is there any stuff I have missed? I would appreciate any help.
Here is the zip file of the project for better references:
http://filebin.ca/E6aoOkaUpbQ/NinjectMVC.zip
Wep.API uses a different IDependencyResolver than the MVC framework.
When you use theHttpConfiguration.DependencyResolver it only works for ApiControllers. But your ApiControllerBase derives from Controller...
So your ApiControllerBase should inherit from ApiController.
Change it in your ApiBaseController.cs:
public abstract class ApiControllerBase : ApiController
{
}
If you want to inject dependencies to regular Controller derived classes you need to use ( in your IocConfig):
System.Web.Mvc.DependencyResolver.SetResolver(new NinjectMvcDependencyResolver(container));
Note that in this case you cannot use your NinjectDependencyResolver because it's for ApiControllers.
So you need a different NinjectMvcDependencyResolver which should implement System.Web.Mvc.IDependencyResolver.
public class NinjectMvcDependencyResolver: NinjectDependencyScope,
System.Web.Mvc.IDependencyResolver
{
private IKernel kernel;
public NinjectDependencyResolver(IKernel kernel)
: base(kernel)
{
this.kernel = kernel;
}
}
I know this is an old thread, but this might be useful for someone. :)
In my case, it was missing the DependencyResolver on NinjectWebCommon.cs.
I followed this article and everything worked fine.
RegisterServices(kernel);
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
return kernel;

asp.net MVC confused by custom attributes creation

hi i am trying to create a custom attribute for my MVC application so that i can call [CheckLogin] this is to check my cookie as i am not using forms authentification.
i have created a class CheckLogin and this is in my App_Code folder and the code is as follows:
using System.Web.Mvc;
using System.Attributes;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Web;
using System;
namespace corian_MVC.Controllers
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class CheckLoginAttribute : FilterAttribute, IAuthorizationFilter
{
public CheckLoginAttribute() {}
public void OnAuthorization(AuthorizationContext filterContext)
{
// TODO: perform your cookie checks
if (!userIsAuthenticated)
{
filterContext.Result = new RedirectResult(string.Format(
"/Admin/Login",
filterContext.HttpContext.Request.Url.AbsoluteUri));
}
}
}
}
what it does is not important here, the problem is i cant get my code to recognise this attribute if it is one in the first place, also how do i redirect to action if the login is failed ????
many thanks
my admin class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
namespace corian_MVC.Controllers
{
[HandleError]
public class AdminController : Controller
{
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Index()
{
//check login is not banned
if ((int)Session["LoginCount"] >= 3) RedirectToAction("TooMany");
return View();
}
public ActionResult Fraud()
{
Session["LoginCount"] = 3;
return View();
}
public ActionResult TooMany()
{
return View();
}
[CheckLogin]
public ActionResult Welcome()
{
return View();
}
private void Createcookie()
{
}
}
}
This scenario is best handled by implementing an IAuthorizationFilter.
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited=true, AllowMultiple=true)]
public class CheckLoginAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
// TODO: perform your cookie checks
if (!userIsAuthenticated)
{
filterContext.Result = new RedirectResult(string.Format(
"/loginUrl?ReturnUrl={0}",
filterContext.HttpContext.Request.Url.AbsoluteUri));
}
}
}
Then you can apply this attribute either at the controller level or at some particular actions.
By the way do you have any particular reason for not using the built-in FormsAuthentication?
Include .cs file with your attribute to the solution. Just placing it "near default.aspx" is not enough.

Resources