How to pass query string parameter to asp.net web api 2 - entity-framework

How can I pass more than 1 parameters as part of query string to my asp.net web api 2.
This is my asp.net web api 2 method, I am not able to figure out that how can I decorate this method so that it accepts the id and a complex type which is CustomerRequest, I want to use Url something like
http://localhost/api/Customer/?Mobile0012565987&Email=abcxyz.com&IsEmailVerified=true
[ResponseType(typeof(Customer))]
public IHttpActionResult GetCustomer(long id, [FromUri]CustomerRequest request)
{
var customer = db.Customers.Find(request.CustomerId);
if (customer == null)
{
return NotFound();
}
return Ok(customer);
}
This is CustomerRequest class
public class CustomerRequest
{
public string Mobile { get; set; }
public string Email { get; set; }
public Nullable<bool> IsEmailVerified { get; set; }
}
Otherwise pleaase guide me if there is a better way to do it.
Thanks

Based on your code, you need to pass 'id' as well, like this:
http://localhost/api/Customer/?id=12345&Mobile=0012565987&Email=abcxyz.com&IsEmailVerified=true
if you want to make 'id' optional, you can make your method signature look like this:
public IHttpActionResult GetCustomer([FromUri]CustomerRequest request, long id = 0)
this will set id to 0 by default, if you dont pass it in the URL. So you will be able to access your URL like you originally did:
http://localhost/api/Customer/?Mobile=0012565987&Email=abcxyz.com&IsEmailVerified=true

Related

POST parameter is null in when it's a hand made type

I have a asp.net 4.5.2 web api and one POST method receive a DTO parameter I've coded by hand like these:
using System;
namespace WebApi.Models.DTO
{
[Serializable]
public class MyModelDto
{
public MyModelDto()
{
}
public int Id { get; set; }
public string Code { get; set; }
public int Type { get; set; }
}
}
When I call this method from Postman the parameter is always null, but if I design the same DTO class in EntityFramewok (edmx model) and use it as parameter I could receive the data inside the post method as it was send.
I could not realize what I'm missing in my hand made class?
Why it works using EF and not with my class?
Setting the parameter as dynamic also works...
[HttpPost]
[JwtAuthentication]
[Route("api/bills/invoice")]
public JsonResult<WebApi.Models.DTO.MyModelDto> Post(WebApi.Models.DTO.MyModelDto param)
{
// Here param is always NULL
}
[HttpPost]
[JwtAuthentication]
[Route("api/bills/invoice")]
public JsonResult<Data.MyModelDto> Post(Data.MyModelDto param)
{
// Here param wors! based on the EF class
}
public JsonResult<Data.MyModelDto> Post(dynamic param)
{
// Here param wors! using dynamic data type
}
it depends how you send the data via Postman.
I don't have Postman here to test but make sure you select Json Type when sending data and then your sent data needs to match your DTO. Something like:
{
"Id":1,
"Type":"whatever",
"Code":"whatever"
}
This data goes in the body of the request since you are sending a Post request.
One extra observation, your DTO does not need an empty constructor, you can get rid of it. There is nothing wrong with:
public class MyModelDto
{
public int Id { get; set; }
public string Code { get; set; }
public int Type { get; set; }
}
For testing purposes get rid of your Jwt authentication, until you get the calls running correctly

Projecting from an IDbAsyncQueryProvider

I have a project in which we use Automapper to Project() entities from our data access layer into our domain classes which are consumed by external callers (i.e. WebAPI, windows services, etc). The idea is that we want to abstract our domain model from the actual database implementation, but often the domain model is comprised of at least the members from the database, and so Automapper makes constructing those domain models a lot easier using it's projection features.
This results in code that looks something like:
public class DbTask
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Task
{
public int Id { get; set; }
public string Name { get; set; }
public User Assignee { get; set; }
public Priority CurrentPriority { get; set; }
}
And then a business service implementation that looks like:
public IQueryable<Task> QueryTasksByUser(int userId)
{
return dbContext
.Where(x => x.Assignee.Id == userId)
.Project()
.To<Task>();
}
So then at the API layer we want to leverage async/await as much as possible, and so I'm wondering if Automapper has any support for this. If I write my method like:
public async Task<IHttpActionResult> GetTopTaskForCurrentUser()
{
var task = await _taskService
.GetTasksByUser(Thread.CurrentPrincipal.AsUser().UserId)
.Where(x => x.CurrentPriority == Priority.Top)
.SingleOrDefaultAsync();
return Ok(task);
}
I get the following exception:
System.InvalidOperationException : The provider for the source IQueryable doesn't implement IDbAsyncQueryProvider. Only providers that implement IDbAsyncQueryProvider can be used for Entity Framework asynchronous operations. For more details see http://go.microsoft.com/fwlink/?LinkId=287068.
Is this something that Automapper just doesn't have support for [yet], or is there another method or extension that I'm missing?
You just need to make sure you call these methods in the correct order:
return await dbContext.Users
.Where(user => user.Id == id)
.Project().To<UserDto>()
.ToListAsync();
Put Project.To in the same place you'd put "Select" and it works fine.
I typically create an extension method to wrap the projection and async'ing:
public static async Task<List<TDestination>>
ToListAsync<TDestination>(
this IProjectionExpression projectionExpression)
{
return await projectionExpression.To<TDestination>().ToListAsync();
}
return await dbContext.Users
.Where(u => u.Id == id)
.Project().ToListAsync<UserDto>();

EF code first MVC4 - ArgumentNullException on edit - what am I doing wrong here?

Let's say I have 3 models:
[Table("UserProfile")]
public class UserProfile //this is a standard class from MVC4 Internet template
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
public class Post
{
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
public int UserProfileId { get; set; }
[ForeignKey("UserProfileId")]
public virtual UserProfile UserProfile { get; set; }
}
Now, I'm trying to edit Post
[HttpPost]
public ActionResult Edit(Post post)
{
post.UserProfileId = context.UserProfile.Where(p => p.UserName == User.Identity.Name).Select(p => p.UserId).FirstOrDefault();
//I have to populate post.Category manually
//post.Category = context.Category.Where(p => p.Id == post.CategoryId).Select(p => p).FirstOrDefault();
if (ModelState.IsValid)
{
context.Entry(post.Category).State = EntityState.Modified; //Exception
context.Entry(post.UserProfile).State = EntityState.Modified;
context.Entry(post).State = EntityState.Modified;
context.SaveChanges();
return RedirectToAction("Index");
}
return View(post);
}
And I'm getting ArgumentNullException.
Quick look into debug and I can tell that my Category is null, although CategoryId is set to proper value.
That commented out, nasty-looking trick solves this problem, but I suppose it shouldn't be there at all. So the question is how to solve it properly.
I would say it's something with EF lazy-loading, beacuse I have very similar code for adding Post and in debug there is same scenerio: proper CategoryId, Category is null and despite of that EF automagically resolves that Post <-> Category dependency, I don't have to use any additional tricks.
On edit method, EF has some problem with it, but I cannot figure out what I'm doing wrong.
This is working as intended. Your Post object is not attached to the Context, so it has no reason to do any lazy loading. Is this the full code? I don't understand why you need to set Category as Modified since you're not actually changing anything about it.
Anyway, I recommend you query for the existing post from the Database and assign the relevant fields you want to let the user modify, like such:
[HttpPost]
public ActionResult Edit(Post post)
{
var existingPost = context.Posts
.Where(p => p.Id == post.Id)
.SingleOrDetault();
if (existingPost == null)
throw new HttpException(); // Or whatever you wanna do, since the user send you a bad post ID
if (ModelState.IsValid)
{
// Now assign the values the user is allowed to change
existingPost.SomeProperty = post.SomeProperty;
context.SaveChanges();
return RedirectToAction("Index");
}
return View(post);
}
This way you also make sure that the post the user is trying to edit actually exists. Just because you received some parameters to your Action, doesn't mean they're valid or that the post's Id is real. For example, some ill intended user could decide to edit posts he's not allowed to edit. You need to check for this sort of thing.
UPDATE
On a side note, you can also avoid manually querying for the current user's Id. If you're using Simple Membership you can get the current user's id with WebSecurity.CurrentUserId.
If you're using Forms Authentication you can do Membership.GetUser().ProviderUserKey.

WebApi OData: $filter 'any' or 'all' query not working

First, using the ASP.NET WebApi tutorials I've created a basic ApiController that exposes an Entity Framework model through OData. The service works to return json for OData $filter queries.
When I perform OData $filter queries that include "any" or "all" queryies on a multi-value property it throws an ODataException
Here's the OData query I'm trying to use
~/api/Blogs?$filter=any(Tags,Name+eq+'csharp')
My ApiController looks like this:
public class BlogController : ApiController
{
public BlogsController()
{
this.Entities = new BlogEntities();
}
public ContactEntities Entities { get; set; }
[Queryable(PageSize = 25, AllowedQueryOptions = AllowedQueryOptions.All)]
public IQueryable<Blog> Get()
{
return this.Entities.Blogs;
}
}
The Blog entity has this contract
public Blog {
public Guid ID { get; set; }
public string Title { get; set; }
public Tag Tags { get; set; }
}
public Tag {
public Guid ID { get; set; }
public string Name { get; set; }
}
The exception thrown
ODataException: Type 'Blog' does not have a property 'Name'
As you can see, I have nothing out of the ordinary in my code and everything should work fine. Is it possible that "any" and "all" queryies aren't supported yet in Microsoft ASP.NET Web API OData?
Your any needs to be changed a bit. Try something like this:
~/api/Blogs?$filter=Tags/any(tag: tag/Name eq 'csharp')
This is assuming Tags actually returns a collection of Tags, not just a single Tag like you have above.
$inlinecount is only supported out of the box for the OData format. I wrote extensively about it here:
Web API OData Inlinecount not working
The short answer is that you can get it working for other formats with code that looks like this:
public PageResult<Customer> Get(ODataQueryOptions<Customer> queryOptions)
{
IQueryable results = queryOptions.ApplyTo(_customers.AsQueryable());
return new PageResult<Customer>(results as IEnumerable<Customer>, Request.GetNextPageLink(), Request.GetInlineCount());
}

MVC 2 Validation and Entity framework

I have searched like a fool but does not get much smarter for it..
In my project I use Entity Framework 4 and own PoCo classes and I want to use DataAnnotations for validation. No problem there, is how much any time on the Internet about how I do it. However, I feel that it´s best to have my validation in ViewModels instead and not let my views use my POCO classes to display data.
How should I do this smoothly? Since my repositories returns obejekt from my POCO classes I tried to use AutoMapper to get everything to work but when I try to update or change anything in the ModelState.IsValid is false all the time..
My English is really bad, try to show how I am doing today instead:
My POCO
public partial User {
public int Id { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
}
And my ViewModel
public class UserViewModel {
public int Id { get; set; }
[Required(ErrorMessage = "Required")]
public string UserName { get; set; }
[Required(ErrorMessage = "Required")]
public string Password { get; set; }
}
Controller:
public ActionResult Edit(int id) {
User user = _userRepository.GetUser(id);
UserViewModel mappedUser = Mapper.Map<User, UserViewModel>(user);
AstronomiGuidenModelItem<UserViewModel> result = new AstronomiGuidenModelItem<UserViewModel> {
Item = mappedUser
};
return View(result);
}
[HttpPost]
public ActionResult Edit(UserViewModel viewModel) {
User user = _userRepository.GetUser(viewModel.Id);
Mapper.Map<UserViewModel, User>(viewModel, user);
if (ModelState.IsValid) {
_userRepository.EditUser(user);
return Redirect("/");
}
AstronomiGuidenModelItem<UserViewModel> result = new AstronomiGuidenModelItem<UserViewModel> {
Item = viewModel
};
return View(result);
}
I've noticed now that my validation is working fine but my values are null when I try send and update the database. I have one main ViewModel that looks like this:
public class AstronomiGuidenModelItem<T> : AstronomiGuidenModel {
public T Item { get; set; }
}
Why r my "UserViewModel viewModel" null then i try to edit?
If the validation is working, then UserViewModel viewModel shouldn't be null... or is it that the client side validation is working but server side isn't?
If that's the case it could be because of the HTML generated.
For instance, if in your view you have:
<%: Html.TextBoxFor(x => x.Item.UserName) %>
The html that gets rendered could possibly be:
<input name="Item.UserName" id="Item_UserName" />
When this gets to binding on the server, it'll need your action parameter to be named the same as the input's prefix (Item). E.g.
public ActionResult Edit(UserViewModel item) {
To get around this, do as above and change your action parameter to item OR you could encapsulate the form into a separate PartialView which takes the UserViewModel as it's model - that way the Html.TextBoxFor won't be rendered with a prefix.
HTHs,
Charles
Ps. If I'm totally off track, could you please post some code for the view.