The goal
Treat an offer as a category in controller.
The problem
I have a controller whose name is ProductsController. Inside it, I have an action called Category. When this method is requested, it responds with a view of products list that corresponds to the category passed as parameter. Follow the code:
[HttpGet]
public ActionResult Category(string categoryName = null)
{
if (Regex.Match(categoryName, #"\d+").Success)
{
int categoryId = Convert.ToInt32(Regex.Match(categoryName, #"\d+").Value);
string sluggedCategoryName = CommodityHelpers.UppercaseFirst(CommodityHelpers.GenerateSlug(Categories.GetDetails((sbyte)categoryId).Category_Name));
if (String.Format("{0}-{1}", categoryId, sluggedCategoryName) == categoryName)
{
ViewBag.Title = Categories.GetDetails((sbyte)categoryId).Category_Name;
ViewBag.CategoryProductsQuantity = Categories.GetDetails((sbyte)categoryId).Category_Products_Quantity;
ViewBag.CurrentCategory = sluggedCategoryName;
return View(Products.BuildListForHome(categoryId, null));
}
else
{
return View("404");
}
}
else
{
return View("404");
}
}
But I want to return other a specific view when "Offers" is passed as parameter.
How can I do this?
if (categoryName == "Offers")
return View("SomeView", Products.BuildListForHome(categoryId, null));
You can specify what view to return as a parameter like so:
return View("Offers", data);
Put an "if" "then" in the beginning of the method checking for Offers, and return your offers View if the conditions meet.
Related
I am showing search results same as searching groups on facebook
enter image description here
I have a relationship Table named CommunityUser in Database having attributes CommunityID and UserID.
Using Partial View I want to show if User not already joined that Community/Group that it will show Join Button else if user already joined that community it will show Leave button.
I have written IsMember() function in my controller that takes two parameters, CommunityID and UserID. This will return true if that Community ID exist against that user ID.
public bool IsMember(string UserID, int CommunityID) {
var Membership = db.Users.Include(x => x.CommunityUsers).Where(s => s.Id.Equals(UserID)).Count();
if(Membership>0)
return true;
else
return false;
}
Now what I actually need is, I want to call this function in an IF condition on my view class. It is not allowing me to call this function on my view Class.
#if (){
<button>#Html.ActionLink("Leave", "LeaveCommunity", new { id = ViewBag.ComID })</button>
}
else
{
<button>#Html.ActionLink("Join", "joinCommunity", new { id = ViewBag.ComID })</button>
}
In your controller you should have a method which will return this view. So in this method you call this function
public ActionResult Index(string UserID, int CommunityID)
{
var hasMembership = IsMember(serID, CommunityID);
return View(hasMembership);
}
In the View it self then you just grab this variable hasMembership you just passed from #model.
#if (Model){
<button>#Html.ActionLink("Leave", "LeaveCommunity", new { id = ViewBag.ComID })</button>
}
else
{
<button>#Html.ActionLink("Join", "joinCommunity", new { id = ViewBag.ComID })</button>
}
Note: it might be wise to create some DTO class for passing data to a view, because you might need to pass multiple value to a view at some point. Plus the whole condition would be more readable
public SomeDTO {
public bool IsMember {get;set}
public List<Community> Communities {get;set;}
}
public ActionResult Index(string UserID, int CommunityID)
{
var hasMembership = IsMember(serID, CommunityID);
var listOfCommunities = _repo.GetComunities();
var dto = new SomeDTO
{
IsMember = hasMembership,
Communities = listOfCommunities
}
return View(dto);
}
#if (Model.IsMember){
// do or do not something
}
I am stuck on a problem implementing LINQ properly on my model. Right now I have it so that when the user clicks on the "list tracks" view they will receive a listing of every song in the DB. What i want to do, is add another link at the top such as "pop tracks" and have it when the user clicks on it, it will sort all the songs by their GenreId (pop) which is 9. I am unsure how to accomplish this as i don't understand how you would return multiple views from 1 index() method (if that's even how i would do it?). Any help is appreciated.
I am now receiving this error:
xception Details: AutoMapper.AutoMapperMappingException: Missing type
map configuration or unsupported mapping.
Mapping types: Genre -> TrackBase Assignment_3.Models.Genre ->
Assignment_3.Controllers.TrackBase
Source Error:
Line 74: { Line 75: var AllPop = ds.Genres.Where(p
=> p.GenreId == 9); Line 76: return mapper.Map>(AllPop); Line 77: } Line
78:
Manager.cs:
public IEnumerable<TrackBase> TrackGetAll()
{
return mapper.Map<IEnumerable<Track>, IEnumerable<TrackBase>>(ds.Tracks);
}
public IEnumerable<TrackBase> TrackGetAllPop()
{
var AllPop = ds.Genres.Where(p => p.GenreId == 9);
return mapper.Map<IEnumerable<TrackBase>>(AllPop);
}
TrackController.cs:
public ActionResult Index(int? genreid)
{
if (genreid.HasValue && genreid.Value == 9)
{
return View(m.TrackGetAllPop());
}
return View(m.TrackGetAll());
}
AutoMapperConfig.cs:
cfg.CreateMap<Models.Track, Controllers.TrackBase>();
cfg.CreateMap<Controllers.TrackBase, Controllers.TracksController>();
You need to pass the genre id to Index action method and filter the songs accordingly. Change your controller to this
public ActionResult Index(int? genreid)
{
if (genreid.HasValue && genreid.Value == 9)
{
// return all pop songs to the view
return View(m.TrackGetAllPop());
}
// return all songs to the view
return View(m.TrackGetAll());
}
then in your index
#Html.ActionLink("All Tracks", "Index") |
#Html.ActionLink("Pop Tracks", "Index", new { genreid = 9})
You also have a problem with TrackGetAllPop method
public IEnumerable<TrackBase> TrackGetAllPop()
{
var AllPop = ds.Genres.Where(p => p.GenreId == 9);
return mapper.Map<IEnumerable<TrackBase>>(AllPop);
}
The above method returns IEnumerable<TrackBase> but you're querying from ds.Genres instead of ds.Tracks, so that's why you got the error. Assuming Track table has GenreId as the foreign key to the Genre table, TrackGetAllPop method should be like below
public IEnumerable<TrackBase> TrackGetAllPop()
{
var AllPop = ds.Tracks.Where(p => p.GenreId == 9);
return mapper.Map<IEnumerable<TrackBase>>(AllPop);
}
More General Solution
The above will work for this particular case, however you can make it more general for other genres too. Add this method in Manager.cs
public IEnumerable<TrackBase> TrackGetByGenre(int genreId)
{
var result = ds.Tracks.Where(p => p.GenreId == genreId);
return mapper.Map<IEnumerable<TrackBase>>(result);
}
and change your controller to below
public ActionResult Index(int? genreid)
{
if (genreid.HasValue)
{
// return songs based on the passed genreid to the view
return View(m.TrackGetByGenre(genreid.Value));
}
// return all songs to the view
return View(m.TrackGetAll());
}
Ideally I would like to have an URL in following format:
/api/categories/1,2,3...N/products
And this would return all products for the specified categories. Having one API call with multiple category IDs saves me several database calls, thus improves performance.
I can easily implement this in a following way.
public HttpResponseMessage GetProducts(string categoryIdsCsv)
{
// <1> Split and parse categoryIdsCsv
// <2> Get products
}
However, this doesn't look like a clean clean solution, and possibly breaking SRP principle. I also tried using ModelBinder, however it adds parameters to query string.
Questions:
Is there a clean way to implement such URL structure?
Or is there a different/better approach to retrieve all products for multiple categories?
Please let me know if you need any further clarification.
I've just found an answer to my question. Route attribute had missing parameter when using ModelBinder.
[Route("api/categories/{categoryIds}/products")]
public HttpResponseMessage GetProducts([ModelBinder(typeof(CategoryIdsModelBinder))] CategoryIds categoryIds)
{
// <2> Get products using categoryIds.Ids
}
And CategoryIds would be
public class CategoryIds
{
public List<int> Ids{ get; set; }
}
And CategoryIdsModelBinder would be
public class CategoryIdsModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
if (bindingContext.ModelType != typeof(CategoryIds))
{
return false;
}
var val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (val == null)
{
return false;
}
var key = val.RawValue as string;
if (key == null)
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Wrong value type");
return false;
}
var values = val.AttemptedValue.Split(',');
var ids = new List<int>();
foreach (var value in values)
{
int intValue;
int.TryParse(value.Trim(), out intValue);
if (intValue > 0)
{
ids.Add(intValue);
}
}
if (ids.Count > 0)
{
var result = new CategoryIds
{
Ids= ids
};
bindingContext.Model = result;
return true;
}
bindingContext.ModelState.AddModelError(
bindingContext.ModelName, "Cannot convert value to Location");
return false;
}
We can use Post methods
[RoutePrefix ( "api/categories" )]
public class TestController
{
[HttpPost]
[Route ( "getProducts" )]
public HttpResponseMessage GetProducts ( HttpRequestMessage request )
{
HttpResponseMessage message = null;
string input = string.Empty;
input = request.Content.ReadAsStringAsync ().Result;
var ids = Newtonsoft.Json.JsonConvert.DeserializeObject<List<string>> ( input );
}
}
Unfortunately Web API can not parse your data as array or as some kind of your custom object out of the box.
If you want to parse your url param as array you can try to do:
Write your own route constraint which will read and convert your param from string to array of ints/strings/whatever;
Write your custom type converter and use it with your data model;
write your value provider and also use it with your data model
Use parameter binding
Moreover you can always use query params which is never will break principles of REST :)
Please see more details about here and here
Hope that helps
I am building a simple MVC CRUD without using a database, but just making methods in a Repository model class.
To make it easier to understand i have 2 model classes. MyNote in which i have some properties and NoteRepository in which i have a list with the properties.
Then I've made a NoteController and i have already made Get and Create methods, but i can't seem to figure out what to write to make an Edit and Delete method? Hope you guys can help.
Here you will see some of the code from my project:
[HttpPost]
public ActionResult Create(MyNote mn)
{
try
{
note.Create(mn);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
this is the create from the Controller.
public static List<MyNote> notes = new List<MyNote>();
public NoteRepository()
{
notes.Add(new MyNote() { ID = 1, Titel = "Morgenmad", OprettelsesDato = DateTime.Now, Note = "Spis morgenmad i dag" });
notes.Add(new MyNote() { ID = 2, Titel = "Frokost", OprettelsesDato = DateTime.Now, Note = "Spis frokost i dag" });
notes.Add(new MyNote() { ID = 3, Titel = "Aftensmad", OprettelsesDato = DateTime.Now, Note = "Spis aftensmad i dag" });
}
public void Create(MyNote mn)
{
notes.Add(mn);
}
here is the repository class with the list and the method for the create method.
and please, ask if i have missed something! Thank you :-)
It looks like you're using a List for your in-memory repository. For delete, you can implement something like this:
public bool Delete (MyNote noteToDelete) {
return notes.Remove(noteToDelete);
}
Edit: However, in this case, the list will check for reference equality. Since you have an ID, which I will assume is unique, you can instead do this:
public bool Delete(MyNote noteToDelete) {
var matchingNote = notes.FirstOrDefault(n => n.ID == noteToDelete.ID);
return notes.Remove(matchingNote);
}
You could also implement IEquatable on your MyNote class to change how your notes are compared with each other, and return a valid match when the IDs are the same.
For the IEquatable example, you would want to change the class definition for MyNote to look like:
public class MyNote : IEquatable<MyNote>
and add in the following code to the MyNote class:
public override bool Equals(object obj) {
if (obj == null) return false;
Part objAsNote = obj as MyNote;
if (objAsNote == null) return false;
else return Equals(objAsNote);
}
public bool Equals(MyNote otherNote) {
if(otherNote == null) return false;
return (this.ID.Equals(otherNote.ID));
}
public override int GetHashCode(){
return this.ID;
}
You can do something like this:
public ActionResult Edit(MyNote noteToEdit)
{
var oldNote = notes.FirstOrDefault(n => n.Id == noteToEdit.Id);
if(oldNote == null)
return View(); //With some error message;
oldNote.Title = noteToEdit.Title;
oldNote.OprettelsesDato = DateTime.Now;
oldNote.Note = noteToEdit.Note;
return RedirectToAction("Index", "Note");
}
public ActionResult Delete(int id)
{
var noteToRemove = notes.FirstOrDefault(x => x.Id == id);
if(noteToRemove == null)
return View(); //With some error message;
notes.Remove(noteToRemove);
return RedirectToAction("Index", "Note");
}
When you are editing your note, i recommend you to use AutoMapper to make your code more easy to maintain.
I am developing a website using asp.net mvc 4 & EF6. I want to pass a string value as a parameter in a Url.action link. However, whenever I click on the link I get this error:
The argument types 'Edm.Int32' and 'Edm.String' are incompatible for this operation. Near WHERE predicate, line 1, column 76.
This is the code that creates it:
Controller
public ActionResult Edit(string EditId)
{
if (Session["username"] != null)
{
UserInfo uinfo = db.UserInfoes.Find(EditId);
return View(uinfo);
}
else
{
return RedirectToAction("HomeIndex");
}
}
View
<a class="btn btn-info"
href="#Url.Action("Edit", "Home", new { EditId = item.regno.ToString() })"><b>Edit</b></a>
How can I use a string value as a parameter?
public ActionResult Edit(string EditId)
{
if (Session["username"] != null)
{
int id;
//Check try to parse the string into an int if it fails it will return false if it was parsed it will return true
bool result = Int32.TryParse(EditId, out id);
if (result)
{
//I wouldn't use find unless you're 100% sure that record will always be there.
//This will return null if it cannot find your userinfo with that ID
UserInfo uinfo = db.UserInfoes.FirstOrDefault(x=>x.ID == id);
//Check for null userInfo
return View(uinfo);
}
else
{
return RedirectToAction("HomeIndex");
}
}