I want to have one API Endpoint that allow me to perform multiple operations with a generic input. for instance, i want to delete an employee either by ID or BY name. Just by changing parameter.
what i have tried is using the OR operator (||):
public int DeleteBusinessStream(int id, string name)
{
var deleteBusinessStream = this.dbcontext.BusinessStreams.FirstOrDefault(p => p.BusinessStreamId == id || p.BusinessStreamName == name);
if(deleteBusinessStream == null)
{
return 1;
}
else
{
this.dbcontext.BusinessStreams.Remove(deleteBusinessStream);
this.dbcontext.SaveChanges();
return 0;
}
}
Controller
[HttpDelete]
[Route("deletebusinessstream")]
public ActionResult deleteBusinessStream(int id, string name)
{
PostMessage pm = new PostMessage();
int returnedValue = this.jobsApi.DeleteBusinessStream(id, name);
if(returnedValue == 1)
{
pm.error = true;
pm.message = "Delete Business Stream was not successful!!";
}
else
{
pm.error = false;
pm.message = "Delete Business Stream was successful";
var deleteStream = this.jobsApi.DeleteBusinessStream(id, name);
}
return Ok(pm);
}
This works well but it requires input two parameter values ( id and name ) i want to be able to use one to switch and perform delete based on any one i choose like
https://localhost:7190/api/Employer/deletebusinessstream?id=111
or
https://localhost:7190/api/Employer/deletebusinessstream?name=ICT
instead of passing both at the same time
You can make both parameters optional like this in your Web API Action
public ActionResult deleteBusinessStream(int id = 0, string name = null)
And later check in code if the id is 0, then use the name to delete and vice-versa i.e if the name is null use id to delete the records
Also, you cannot restrict user from passing both parameters in the request so you need to handle that request separately and return BadRequest maybe
This way you can call your endpoint with either of the URLs
https://localhost:7190/api/Employer/deletebusinessstream?id=111
https://localhost:7190/api/Employer/deletebusinessstream?name=ICT
Although I would say that Name will not be a unique identifier for a record, so I suggest you take a dig and check whether you actually want to delete the record using the name
Related
I made my database with its stored procedures then attached it with my project Entity Framework database-first.
This function to insert a company info and return its id back and insert it to another table in relation with company table
public string InsertCompany(company company, out int index)
{
try
{
using (vendors_managerEntities db = new vendors_managerEntities())
{
db.companies.Add(company);
db.SaveChanges();
index = company.id_company;
return $"{company.name_company} Is Saved";
}
}
catch (Exception ex)
{
index = 0;
return ex.Message;
}
}
But when I tried to my stored procedure which has been created in database, I couldn't return any value the id always be 0
public string InsertCompany(company company, out int index)
{
try
{
using (vendors_managerEntities db = new vendors_managerEntities())
{
db.SP_insert_companies(company.name_company, company.website_company, company.adress_company, company.county_company, company.decription_company);
index = company.id_company;
return $"{company.name_company} Is Saved";
}
}
catch (Exception ex)
{
index = 0;
return ex.Message;
}
}
I read that I can make it in SQL but I'm looking for a solution in C#, so I opened the stored procedure definition in C# and found the following code and was thinking if can I change its return value because it's not return the id value
public virtual int SP_insert_companies(string name, string website, string address, string country, string description)
{
var nameParameter = name != null ?
new ObjectParameter("name", name) :
new ObjectParameter("name", typeof(string));
var websiteParameter = website != null ?
new ObjectParameter("website", website) :
new ObjectParameter("website", typeof(string));
var addressParameter = address != null ?
new ObjectParameter("address", address) :
new ObjectParameter("address", typeof(string));
var countryParameter = country != null ?
new ObjectParameter("country", country) :
new ObjectParameter("country", typeof(string));
var descriptionParameter = description != null ?
new ObjectParameter("description", description) :
new ObjectParameter("description", typeof(string));
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction("SP_insert_companies", nameParameter, websiteParameter, addressParameter, countryParameter, descriptionParameter);
}
Please tell me if there's a solution in C# or should I go back to old code without stored procedure in that case?
The issue is that you are using a stored procedure to insert the company entity which I suspect does not cause the object to be refreshed by the context:
db.SP_insert_companies(company.name_company, company.website_company, company.adress_company, company.county_company, company.decription_company);
You then try to get the id from the object which is 0 because it hasn't been refreshed:
index = company.id_company;
If you insist on using a stored procedure, what I would suggest is that you have the SP return the id of the company, then grab it from the call and use that as the value of index:
index = db.SP_insert_companies(company.name_company, company.website_company, company.adress_company, company.county_company, company.decription_company);
Once you modify the SP, make sure to update the definition in your code so it knows to make a function that returns a value.
If you prefer to have it in the object itself, then make sure to update it manually, although I don't recommend this as the object is not in true sync with the database:
index = db.SP_insert_companies(company.name_company, company.website_company, company.adress_company, company.county_company, company.decription_company);
company.id_company = index;
Based on what you're saying it's automatically going to the "Catch" and then the index is already set to 0. So chances are your code elsewhere, not listed is messing up. I suspect wherever your code for saving the company information isn't saving properly. Try manually inputting a company into the DB and then check it with your program. If it returns that it's "Saved" then you know your problem isn't within this method and is a result of your saving method.
Let's assume the following:
I have a rest API that will return me names of fruits, and there are only 5 fruits.
To get the fruit name, I have to request an ID.
Consider the following code:
public class Fruit {
public int FruitID { get; set; }
public string FruitName { get; set; }
public Fruit(string json){
JObject o = JObject.Parse(json);
FruitID = Int32.Parse((string) o["id"]);
FruitName = (string) o["name");
}
}
public static Fruit getFruit(int id){
Task<Fruit> task = "http://fruit.com/get_fruit"
.SetQueryParams(new { fruit_id = id })
.GetStringAsync();
return new Fruit(task.Result);
}
(If anything looks wrong at this point please correct me, I am new to C# Tasks)
Let's say when that Task returns, the json could look like the following if it received a valid ID...
{
"status":1,
"id": 3,
"name": "apple"
}
Or this if it received an invalid ID.
{
"status":0
}
If the user is supposed to enter which ID is searched for, then there is a chance they could enter an ID which does not exist, since there are only 5, (0 through 4). Based on the code I entered above, I can see the application crashing if a "status":0 is returned, as it would not have the two fields the class constructor is looking for.
My question is: What is the best way to handle possible invalid inputs (such as the user entering ID of 20)?
The recommended way for a RESTful API is to use HTTP Error codes, in your case it would be 404 (Not found), since the fruit requested does not exist.
You should handle the error codes before trying to create the object. So check whether the request has been successfully executed (200 OK), and then process the payload.
Here's a reference of status codes:
http://www.restapitutorial.com/httpstatuscodes.html
input validation is one of the important tasks in web service development. i personally have two phase. at first i check the object for null values. i wrote this method in order to do it:
private bool HasNull(object webServiceInput, string[] optionalParameters = null)
{
if (ReferenceEquals(null, webServiceInput))
return false;
if (optionalParameters == null)
optionalParameters = new string[0];
var binding = BindingFlags.Instance | BindingFlags.Public;
var properties = webServiceInput.GetType().GetProperties(binding);
foreach (var property in properties)
{
if (!property.CanRead)
continue;
if (property.PropertyType.IsValueType)
continue;
if (optionalParameters.Contains(property.Name))
continue;
var value = property.GetValue(webServiceInput);
if (ReferenceEquals(null, value))
return false;
}
return true;
}
then if some of the inputs should have specified validation i check it individually. for example i check the ID be between 0 and 5;
i hope it could help you.
Here is a paste of the action method MovieCustomer in the EverythingController.
The Viewmodel is used to Combine two Models: Customer & Movies, and is populated with information from the Database via the ApplicationDbContext (_context).
The Routeing works successfully and renders the page when there are values for MovieId and CustomerId
e.g. /Everything/MovieCustomer/1/1
I want the page to also load if one or both of the values are null. So far both of the int parameters were made nullable and there is an if statement in the method to change the parameters to 1 if either is null.
So far if the values are null the browser returns a 404 error.
How can I get the page to function when one or either of the parameters are null? Thanks
[Route("Everything/MovieCustomer/{movieId}/{customerId}")]
public ActionResult MovieCustomer(int? movieId, int? customerId)
{
var viewmodel = new ComboViewModel
{
_Customers = new List<Customer>(),
_Movies = new List<Movies>(),
_customer = new Customer(),
_movie = new Movies()
};
viewmodel._Customers = _context.Customers.ToList();
viewmodel._Movies = _context.Movies.ToList();
if (!movieId.HasValue)
movieId = 1;
if (!customerId.HasValue)
customerId = 1;
viewmodel._customer = viewmodel._Customers.SingleOrDefault(a => a.Id == customerId);
viewmodel._movie = viewmodel._Movies.SingleOrDefault(a => a.Id == movieId);
return View(viewmodel);
}
You can achieve this using separate routes, or change your parameters to be optional.
When using 3 attributes, you add separate routes for each of the options that you have - when no parameters are specified, when only movieId is specified, and when all 3 parameters are specified.
[Route("Everything/MovieCustomer/")]
[Route("Everything/MovieCustomer/{movieId}")]
[Route("Everything/MovieCustomer/{movieId}/{customerId}")]
public ActionResult MovieCustomer(int? movieId, int? customerId)
{
// the rest of the code
}
Alternatively you an combine change your route parameters to optional (by adding ? in route definition) and this should cover all 3 cases that you have:
[Route("Everything/MovieCustomer/{movieId?}/{customerId?}")]
public ActionResult MovieCustomer(int? movieId, int? customerId)
{
// the rest of the code
}
Keep in mind that neither sample supports the case where you provide only customerId.
Keep in mind that neither sample supports the case where you provide only customerId.
Check it out. I think you can use the multiple route method with EVEN ANOTHER route like this if you do want to provide only customerId:
[Route("Everything/MovieCustomer/null/{customerId}")]
Interstingly, I had to add optional parameter to the signature as well for it to work from Angular client like so:
[HttpGet]
[Route("IsFooBar/{movieId?}/{customerId?}")]
[Route("IsFooBar/null/{customerId?}")]
public bool IsFooBar(int? movieId = null, int? customerId = null)
{
// the rest of the code
}
In Angular
public IsFoobar(movieId: number | null, customerId: number | null): Observable<boolean> {
return this.httpService.get<boolean>(`api/IsFooBar/${movieId}/${customerId}`);
}
I'm fairly new to this Web API stuff and I'm trying to set up simple http requests to a local database on my computer. I have a get request that looks like this:
[HttpGet]
[Route("")]
[Route("{ID:int}")]
public IQueryable<ListTable> Get(int id = -1)
{
if(id == -1)
return db.ListTables;
else
return db.ListTables.Where(lt => lt.ID == id);
}
This just returns all items in the database or one item relating to the specified ID. Now I'm trying to make a put request where I can add a new item to the database or edit item related to a certain ID. I was trying something like this:
[HttpPut]
[Route("{ID:int}")]
[Route("{ID:int}/{TITLE:string}")]
[Route("{ID:int}/{TITLE:string}/{DESCRIPTION:string}")]
public ListTable Put(int id = -1, string title = null, string descr = null)
{
//if ID == -1 add a new item to the DB
//else add title and description to the item with the specified ID
}
I am a little unsure of how to add a new item to the database and save the changes. I tried something like db.ListTables.Add(new ListTable()) and db.SaveChanges() but those didn't seem to actually save anything because when I called the Get() method again the new item wasn't there.
You will need to new up an instance of the entity to add [ListTable] and then add it to your database context (assuming it's db based on your GET example. Once you add it to the context, then you .SaveChanges() - I'm assuming your ListTable entity has columns called Title and Description (change those lines to whatever you have them named):
ListTable listTableToAdd = new ListTable()
{
Title = title,
Description = descr
}
db.ListTables.Add(listTableToAdd);
db.SaveChanges();
You need to set the properties after new ListTable(). Something like new ListTable() { Title = title, ... }
I am working on setting up database storage for a game project I am working on for school, and am at the very beginning of doing so. I am using MongoDB at the suggestion of our sponsor. I have successfully implemented inserting a list of names with individual indices, but I cannot get it to return them. The code I am using is as follows.
public string getName(int id) {
var query = Query.EQ("_index", id);
if (Names.FindOne(query) != null) return (Names.FindOne(query)).FirstName;
else return "Error";
}
I have now determined the problem is that the objects aren't being saved. My code for doing so is
public void storeName(string name, int number) {
Name Insert = new Name{FirstName = name, index = number};
var query = Query.EQ("_FirstName", name);
if (Names.FindOne(query) == null) { Names.Save<Name>(Insert); Console.WriteLine(Insert.index+" "+Insert.FirstName); }
else {Console.WriteLine("Bork"); }
}
And the Name object is automapped.
I solved my problem. The index and FirstName fields needed to not have the underscores.