Calling Get method with Object Parameter .NET core - c#

I am developing a dashboard in react which calls backend API to fetch all recipes from the database. So the search criteria would be huge. Its required to pass many filter attributes into the backend to get the correct recipes.
As an example below I have defined a class for Search Parameters
public class SearchParams
{
public string TemplateName { get; set; } = "";
public DateTime DateFrom { get; set; }
public DateTime DateTo { get; set; }
public String CreatedBy { get; set; } = "";
public Guid Id { get; set; }
}
So the GET method is required to handle whatever the parameters provided fetch the corresponding recipes from the DB accordingly.
But since GET requests doesnt support accepting parameters as OBJECT (SOrry if I am wrong) I thought about trying with POST. But that feels a little confused to use POST for a search functionality.
So with GET method do I need to define with all the parameters like this
[HttpGet]
public IEnumerable<Recipes> Get(string TemplateName,DateTime DateFrom....)
{
return new string[] { "value1", "value2" };
}
Or any best approach for this?
Please note, my real search criteria include many attributes other than the properties in my class definition above.

nothing prevents you from using SearchParams as an input parameters
[HttpGet]
public IEnumerable<Recipes> Search(SearchParams par)
the only problem is that Get doesn't include a body, so all data should be included in a query string
.../search?TemplateName=TemplateName&CreatedBy=....

Related

Using Tuple field names in JSON responses

B"H
Is there a way to return field names when using Tuples as return types for actions?
What I would like to do is skip creating DTOs for every single function in every single controller. When I have a complex system with many controllers each with many actions (functions). I often find that there are a handful of central DTOs. Then there are hundreds of slight variations of them. One for each function. I would like to stick with the handful of central classes and skip the extra classes. Replacing them with Tuples.
For example. I have a Customer class
public class Customer
{
public string Name{ get; set; }
public string Email{ get; set; }
}
with a many to many relation to store locations
public class StoreLocation
{
public string Name{ get; set; }
public string City{ get; set; }
public string State{ get; set; }
public string Focus{ get; set; }
}
I then have a function in a controller
[HttpGet("TopCustomersByState")]
public IEnumerable<(Customer customer, string state )> TopCustomersByState()
{
}
and a function
[HttpGet("TopCustomersByFocus")]
public IEnumerable<(Customer customer, string focus)> TopCustomersByState()
{
}
and a function
[HttpGet("CustomersAndTotalMoneySpent")]
public IEnumerable<(Customer customer, float moneySpent)> CustomersAndTotalMoneySpent()
{
}
These function are all accessed from Javascript in the browser expecting JSON.
Until now, I'd make a separate class for each return type. This quickly gets out of hand.The solution that I present above in my examples if it were to work would be perfect.The issue is that the JSON being returned is
{
"item1": {
},
"item2": null
}
instead of the property names customer, moneySpent, etc. as you'd expect.
Thank you
You don't need to put any return type of data at all if you want to make it generic. In any case it is converted to a json string. You can use an anonymous class for example
[HttpGet("CustomersAndTotalMoneySpent")]
public IActionResult CustomersAndTotalMoneySpent()
{
... your code
return Ok (new { Focus = focus, Customer = customer } );
//or
return Ok (new List<object> { { Focus = focus, Customer = customer} });
//or List<dynamic>
}

Azure Functions HttpTrigger all properties null on int overflow

I use some Azure Functions as WebApi. Now I have the following DTO to create a vehicle:
public class CreateVehicleDto
{
public string LicensePlate { get; set; }
public int? Mileage { get; set; }
public string Manufacturer { get; set; }
}
My method header looks like this:
[FunctionName("CreateVehicle")]
public async Task<ActionResult<ReadVehicleDto>> CreateVehicle([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "vehicles")] CreateVehicleDto createVehicleDto){}
The problem is that when my client sends a mileage higher than int.MaxValue all properties of the DTO are null and the method runs without throwing any exception whatsoever.
Is there a way to handle that? In case of a too high mileage I want to return a BadRequestResult.
I've also tried to use the System.ComponentModel.DataAnnotations to set a maximum like this [Range(0, int.MaxValue)] and validate it with the System.ComponentModel.DataAnnotations.Validator. But when the object gets validated it's already too late because all properties of the DTO get passed into the method with a value of null.

ASP.NET MVC | HOW to update specific column through EF using Ajax

Im New to ASP.NET MVC.
im just learning MVC and i am stuck in a situation where i want to update data in database using Ajax and EF.
I am using code first approach.
I have two projects in my solution. First is The Web MVC project named as Gem, The other i have entities in it with project name Gem.Domain
I have this entity named Category with file name Category.cs
namespace Gem.Domain
{
public class Category
{
public virtual int CategoryID { get; set; }
public virtual int ParentCategory { get; set; }
public virtual string Name { get; set; }
[MaxLength]
public virtual string Description {get;set; }
public virtual ICollection<Product> Products { get; set; }
}
}
with datasource file
namespace Gem.Domain
{
public interface IStoreDataSource
{
IQueryable<Product> Products { get; }
IQueryable<Category> Categories { get; }
}
}
Now in other Project Named Web
I have Area Registered with Name Admin which contains some controllers, but to be specific CategoriesController.cs reside in it.
And i have this method in this CategoriesController
public string UpdateCategoryName_DT(Category category)
{
return "Just A Test";
}
Finally coming to view.
I want to use ajax on a popup that appears on datatables.
Ajax request works fine.. and request do generates to correct method.
Here is my ajax code.
//Category Name Update Using Ajax.
$('#datatable').on('click', '.editable-submit', function () {
var rowID = $(this).parents('tr').find('td:eq(0)').text();
var updatedCategoryName = $(this).parents('div').siblings('div.editable-input').find('input').val();
var postData = {
ID: rowID,
CategoryName: updatedCategoryName
};
//For Ajax Request.
$.ajax({
type:"POST",
data:JSON.stringify(postData),
url:"#Url.Action("UpdateCategoryName_DT", "Categories", new { area = "Admin" })",
success: function (output) {
console.log(output);
}
});
});
Below is generated Ajax Request screen cap, using firebug for showing post info.
Main Question:
I want to know how to get the posted values in the controller through this ajax request and update the category name in database on base of posted values.
e-g i am getting ID of row and New Category name in ajax post, and i want to update the record in category table in database using entity framework.
How to do it, and what is the right method as i am using ajax.
I have tried youtube and tutorials but i am not understanding it.
I have DBContext with name of StoreDb which resides in Gem Project and Infrastructure Folder.
namespace Gem.Infrastructure
{
public class StoreDb : DbContext, IStoreDataSource
{
public StoreDb() : base("GemStoreConnection")
{
}
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
IQueryable<Product> IStoreDataSource.Products
{
get
{
return Products;
}
}
IQueryable<Category> IStoreDataSource.Categories
{
get
{
return Categories;
}
}
}
}
plus i am using structuremap.mvc5 dependency resolution.
namespace Gem.DependencyResolution {
using Domain;
using Infrastructure;
using StructureMap.Configuration.DSL;
using StructureMap.Graph;
public class DefaultRegistry : Registry {
#region Constructors and Destructors
public DefaultRegistry() {
Scan(
scan => {
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.With(new ControllerConvention());
});
For<IStoreDataSource>().Use<StoreDb>();
}
#endregion
}
}
i am new but i have did this setup using tutorial for what i have understood so far. but there is nothing related to ajax so i need little help with ajax.
-=-=-=-=-=-=-=-=-=-=
Update:
Used a debugger, i think i am getting null, values are posting fine but i am getting null in controller ?
=-=-=-=-=-=-=-=-=--=-=-=-=-
Update 2:
i removed the JSON.stringify() and changed the Posted Data to this
var postData = {
CategoryID: rowID,
Name: updatedCategoryName
};
As now it matches to schema, so its working now..
but on other hand it also exposes my DB Schema. What if i want to post Ajax with Different ValuesNames, other than the database column names, what to do in that case?
Based on your comments, I think you are not much familiar with HTTP concept.
The simpliest way to read the object from ajax request (or any other POST request) is to update your Category model property names to match the once in json request (you can keep the first letter upper in c#, the rest has to be the same). Right now, your model has CategoryID and CategoryName, but in the json request, you are sending ID and Name parameters. Then, you need to add [FromBody] attribute to you action:
public string UpdateCategoryName_DT([FromBody]Category category)
{
return "Just A Test";
}
The attribute tells the framework, that it should parse the json from body of the request and creates an instance of Category object. Then you will not get null in as seen in your updated question.
UPDATE
You can have different names in json request and in database. You just need to use JsonProperty attribute on the property.
public class Category
{
[JsonProperty(PropertyName = "yourDesiredNameInJsonRequest")]
public virtual int CategoryID { get; set; }
public virtual int ParentCategory { get; set; }
[JsonProperty(PropertyName = "yourAnotherDesiredNameInJsonRequest")]
public virtual string Name { get; set; }
[MaxLength]
public virtual string Description {get;set; }
public virtual ICollection<Product> Products { get; set; }
}
To clarify the code above - the attribute will tell Json.Net to deserialize property "yourDesiredNameInJsonRequest" in your json and save it to Category.CategoryID field. It is just mapping.

Optional query string parameters in ASP.NET Web API

I need to implement the following WebAPI method:
/api/books?author=XXX&title=XXX&isbn=XXX&somethingelse=XXX&date=XXX
All of the query string parameters can be null. That is, the caller can specify from 0 to all of the 5 parameters.
In MVC4 beta I used to do the following:
public class BooksController : ApiController
{
// GET /api/books?author=tolk&title=lord&isbn=91&somethingelse=ABC&date=1970-01-01
public string GetFindBooks(string author, string title, string isbn, string somethingelse, DateTime? date)
{
// ...
}
}
MVC4 RC doesn't behave like this anymore. If I specify fewer than 5 parameters, it replies with a 404 saying:
No action was found on the controller 'Books' that matches the request.
What is the correct method signature to make it behave like it used to, without having to specify the optional parameter in the URL routing?
This issue has been fixed in the regular release of MVC4.
Now you can do:
public string GetFindBooks(string author="", string title="", string isbn="", string somethingelse="", DateTime? date= null)
{
// ...
}
and everything will work out of the box.
It's possible to pass multiple parameters as a single model as vijay suggested. This works for GET when you use the FromUri parameter attribute. This tells WebAPI to fill the model from the query parameters.
The result is a cleaner controller action with just a single parameter. For more information see: http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api
public class BooksController : ApiController
{
// GET /api/books?author=tolk&title=lord&isbn=91&somethingelse=ABC&date=1970-01-01
public string GetFindBooks([FromUri]BookQuery query)
{
// ...
}
}
public class BookQuery
{
public string Author { get; set; }
public string Title { get; set; }
public string ISBN { get; set; }
public string SomethingElse { get; set; }
public DateTime? Date { get; set; }
}
It even supports multiple parameters, as long as the properties don't conflict.
// GET /api/books?author=tolk&title=lord&isbn=91&somethingelse=ABC&date=1970-01-01
public string GetFindBooks([FromUri]BookQuery query, [FromUri]Paging paging)
{
// ...
}
public class Paging
{
public string Sort { get; set; }
public int Skip { get; set; }
public int Take { get; set; }
}
Update:
In order to ensure the values are optional make sure to use reference types or nullables (ex. int?) for the models properties.
Use initial default values for all parameters like below
public string GetFindBooks(string author="", string title="", string isbn="", string somethingelse="", DateTime? date= null)
{
// ...
}
if you want to pass multiple parameters then you can create model instead of passing multiple parameters.
in case you dont want to pass any parameter then you can skip as well in it, and your code will look neat and clean.
Default values cannot be supplied for parameters that are not declared 'optional'
Function GetFindBooks(id As Integer, ByVal pid As Integer, Optional sort As String = "DESC", Optional limit As Integer = 99)
In your WebApiConfig
config.Routes.MapHttpRoute( _
name:="books", _
routeTemplate:="api/{controller}/{action}/{id}/{pid}/{sort}/{limit}", _
defaults:=New With {.id = RouteParameter.Optional, .pid = RouteParameter.Optional, .sort = UrlParameter.Optional, .limit = UrlParameter.Optional} _
)

How to structure controller to sort multiple criteria asp.net mvc

What's the best way to set up a controller to sort by many (possibly null) criteria? Say for example, I was building a site that sold cars. My CarController has a function Index() which returns an IList of cars to the view, and details on each car are rendered with a partial view.
What's the best way to structure this? Especially if there are a lot of criteria: Car Type (aka SUV), Car Brand, Car Model, Car Year, Car Price, Car Color, bool IsNew, or if I want to sort by closest to me etc... I'm using NHibernate as my ORM. Should I have just a ton of possible NHibernate queries and figure out which one to choose based on if/then's in the controller? Or is there a simpler way.
Thanks so much for the help.
I have tasks in my system and created special class for searching:
public class TaskSearchCriteria
{
public List<int> Statuses { get; set; }
public List<int> Severities { get; set; }
public List<int> CreatedBy { get; set; }
public List<int> ClosedBy { get; set; }
public List<int> LastModificationBy { get; set; }
public DateTime? CreationDateFrom { get; set; }
public DateTime? CreationDateTo { get; set; }
public bool SearchInComments { get; set; }
public bool SearchInContent { get; set; }
public bool SearchInTitle { get; set; }
}
One method in that class applies filters:
public IQueryable<Task> Filter(IQueryable<Task> tasks)
{
return
FilterDates(
FilterAssignedTo(
FilterGroups(
FilterOperationSystems(
FilterPlatforms(
FilterPriorities(
FilterSeverities(
FilterVersionsResolved(
FilterVersionsReported(
FilterTaskContent(
FilterStatuses(tasks)))))))))));
}
This is one of methods used for filtering:
private IQueryable<Task> FilterSeverities(IQueryable<Task> tasks)
{
if (Severities.Contains(TaskSearchConsts.All) || (!Severities.Any()))
return tasks;
var expressions = new List<Expression>();
if (Severities.Contains(TaskSearchConsts.Active))
expressions.Add(_taskParameter.EqualExpression("Severity.IsActive", 1));
return tasks.WhereIn(_taskParameter, "Severity.ID", Severities, expressions);
}
This is Entity Framework, but it can be easily done in nHibernate too by adding where clauses to IQuery.
To search I have a method in controller:
[HttpPost]
public ActionResult TaskSearch([Bind(Prefix = "Search")]TaskSearchCriteria criteria)
Having one class for filtering is nice, because it can be serialized and save to database for future use. This way user can reuse filter parameters.
You would have:
public class CarSearchCriteria
{
List<int> ListOfCarTypesIds;
List<int> ListOfCarBrandIds;
bool IsNew;
//and more
}
If list is empty, you don't apply filter.
If you've got a lot of criteria, I find it best to encapsulate in a class rather than passing in lots of action parameters. For example, a project i'm working on at the moment has an action with the following signature:
public claass JobsController : Controller
{
public IList<JobDto> Index(JobSearchCriteria criteria)
{
IList<JobDto> jobs = _jobs.Find(criteria);
//...
}
}
The repository method simply steps through the criteria building up an IQueryable as it goes along. This particular project uses Linq To Sql but the same principal can apply using NHibernates Criteria API (or Linq to NH).

Categories