ASP.NET MVC Attribute-Routing with Null Values - c#

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}`);
}

Related

How to pass query parameter value into HTML.Partial model?

I have an MVC page (Not .Net Core) which contains the below code
#Html.Partial("~/Views/ProductLanding/product.cshtml", Model.Product(1))
The query in the address bar is similar to ..../products/product?id=4
How could i pass in the query value of 4 (or whatever it might be) into the Model.Product(ID) which then calls some code in my database to retrieve that product?
Using a hardcoded value works so im not sure if i should be doing this differently or if there is a better approach?
Finally hopefully this wont make a difference but once i have this working i would like to change the URL to something more friendly i.e. ..../products/product/4
method 1 : get route data in razor view
{
var id = Context.GetRouteData().Values["id"];
}
#Html.Partial("~/Views/ProductLanding/product.cshtml", Model.Product(id))
method 2 : pass id with controller (viewbag or model)
public ActionResult Product(int id){
var model= {
...
Id = id
}
//ViewBag.Id = id;
return View(model)
}
view:
#model ViewModel
{
var id = int.Parse(ViewBag.Id); // alternative sln.
}
#Html.Partial("~/Views/ProductLanding/product.cshtml", Model.Product(Model.Id))
I have been using the below method and it works for me you can also try this
#Html.Action(, , new { #<Parameter_name> = })
Eg:
#Html.Action("DetailsProducts", "CREDITNOTEs", new { #id = Model.ID })

How to use a Generic Parameter in Web Api Method

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

HttpPut Request in C#

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, ... }

In 3 level JqGrid, get the first table's primary key when the 2nd table's "+" sign is pressed

I am new to JqGrid and please help me with this request.
I have a 3 level hierarchical JqGrid setting as shown in the link. It is not exactly the same, but very similar. My requirement is to have the CustomerGrid's primary key also passed when OrderGrid is expanded.
Or in short, I would like to have
public void SetUpThreeLevelGrids(ThreeLevelHierarchyJqGridModel model)
{
var customersGrid = model.CustomersGrid;
// code here
int cId;
//cId = <CustomerId from the CustomerGrid>; //*****How to get this******
ordersGrid.DataUrl = Url.Action("ThreeLevel_OrdersDataRequested", new { customerId = cId });
// code here
}
I would like to use that variable passed to the ThreeLevel_OrderDetailsDataRequested method:
public JsonResult ThreeLevel_OrderDetailsDataRequested(int customerId, string parentRowID)
{
// code here
}
I created a static variable named CustomerId in my Controller. I do not know whether this can break anything or not. I just wanted to make it work.
public static int customerId = 0;
In the Action method for the second grid, I assigned the CustomerId value.
public JsonResult ThreeLevel_OrdersDataRequested(string parentRowID)
{
var northWindModel = new NorthwindDataContext();
var model = new ThreeLevelHierarchyJqGridModel();
customerId = int.Parse(parentRowID);
SetUpThreeLevelGrids(model);
//code
}
Accessed the global static variable in the third level grid.
public JsonResult ThreeLevel_OrderDetailsDataRequested(string parentRowID)
{
//Code
var orderDetails = from o in myDbModel.MyCustomerOrderDetails
where o.OrderID == Convert.ToInt32(parentRowID)
and o.CustomerId == customerId //static global variable
select o;
//Code
}
If anyone has a better suggestion to get the 1st level table's selected primary key in the third level grid, please let me know.

Reusing projections in queries, the part after "select new"

In my ASP.NET MVC Application, I have many actions that return JSONs.
In these JSONs, some sub-structures are repeated.
For example
ajax: "/Bands/Members" is supposed to return the members of a band, which are all musicians
ajax: "/Musicians/All" is supposed to return all the musicians on the system
ajax: "/Musicians/Search" is supposed to return all the musicians that match something
etc...
Here I show (1):
public JsonResult Members(long musicBandId)
{
MusicBand b = db.MusicBands.SingleOrDefault(b => b.MusicBandId == musicBandId);
if (b == null)
return null;
return Json(new
{
error = false,
message = "",
persons = from m in b.Members select new
{
musicianId = p.MusicianId,
name = m.Name,
instrument = new
{
instrumentId = m.instrument.InstrumentId,
model = m.instrument.Model
}
}
}, JsonRequestBehavior.AllowGet);
}
And here I show (2):
public JsonResult All(int page, int pageSize)
{
var musicians = db.Musicians;
var pageOfMusicians = musicians.Skip((page-1) * pageSize).Take(pageSize);
return Json(new
{
error = false,
message = "",
musiciansCount = musicians.Count(),
page = page,
pageSize = pageSize
musicians = from m in pageOfMusicians select new
{
musicianId = m.MusicianId,
name = m.Name,
instrument = new
{
instrumentId = m.instrument.InstrumentId,
model = m.instrument.Model
}
}
}, JsonRequestBehavior.AllowGet);
}
This has several problems:
If I want to change the JSON format, I have to change it in every single action!
example: If I want to change "name" to "fullname", I have to change it in Members() and All()
Lot of "copy pasting": If I'm creating a new action that somewhere in the structure returns a musician, I need to copy and paste that piece of code that represents the musician
{
musicianId = p.MusicianId,
name = p.Name,
instrument = new
{
instrumentId = instrument.InstrumentId,
model = instrument.Model
}
}
Code is too large
What solution exists to this problem?
If you propose a framework, please show me how would the previous queries look with that framework
Notice I'm always using Linq-to-entities in my examples, and I would like to keep it like that because of performance issues. I know that with Linq-to-objects I could to something like:
from m in pageOfMusicians.ToList() select m.GetDTO()
Being GetDTO() some method that can be run with Linq-to-Objects.
But then, again, I want to stick to Linq-to-Entities
Alternative 1
If you don't worry about using dynamics mixed with regular typed C# code you could make a utility method like...
public static dynamic PrepareForMusiciansView(IQuerable<Musician> musicians)
{
return musicians.Select(m => new
{
musicianId = m.MusicianId,
name = m.Name,
instrument = new
{
instrumentId = m.instrument.InstrumentId,
model = m.instrument.Model
}
}
}
...and then...
return Json(new
{
error = false,
message = "",
musiciansCount = musicians.Count(),
page = page,
pageSize = pageSize
musicians = Util.PrepareForMusiciansView(pageOfMusicians)
}, JsonRequestBehavior.AllowGet);
The method name should clearly reflect its purpose in terms of your application. Maybe you want to focus more on the serialization function and use a name like PrepareForJson. Also, it should be consistent with your coding standards. I would avoid it if nowhere else dynamics is used.
Alternative 2
Use AutoMapper (available via NuGet).
With AutoMapper you'd typically have DTO classes for Musician and Instrument, having the properties you want to expose in the view. If these properties have the same names as those in the source classes, AutoMapper will match them by name convention.
Using AutoMapper always involves defining the mappings and executing mappings. Defining the mappings should be done once at application startup. It looks like...
Mapper.CreateMap<Musician, MusicianDto>();
Mapper.CreateMap<Instrument, InstrumentDto>();
There are different ways to execute the mappings, but when working with IQueryable the preferred way is
musicians = pageOfMusicians.Project().To<MusicianDto>()
This projects IQueryable<Musician> to IQueryable<MusicianDto>, including the nested Instrument (if the DTO has a property of that name).
These are two viable alternatives I can think of to reduce the awkward new {} statements to reusable one-liners. It really depends on your application and coding style which alternative you prefer.

Categories

Resources