How to dynamically order by the records - c#

I have the following index action method which display a list of objects as follow:-
public ActionResult Index(string searchTerm = "", int page = 1)
{
string withOutSpace = searchTerm.Trim();
ViewBag.searchTerm = searchTerm;
int pagesize;
bool succeed = int.TryParse(System.Web.Configuration.WebConfigurationManager.AppSettings["TechPageSize"], out pagesize);
var racks = repository.AllFind(withOutSpace).OrderBy(a => a.Technology.SerialNumber).ToPagedList(page, pagesize);
currently I am always ordering by the SerialNumber, but my question is how I can pass a parameter to my index actionmethod and do the OrderBy based on the passed parameter, such as price, date, etc.
can anyone advice?
And second question how I can make the first call to orberby ascending while the second call to do the order descending ?
Thanks

public ActionResult Index(string searchTerm = "", string sort, bool asc, int page = 1)
{
string withOutSpace = searchTerm.Trim();
ViewBag.searchTerm = searchTerm;
int pagesize;
bool succeed = int.TryParse(System.Web.Configuration.WebConfigurationManager.AppSettings["TechPageSize"], out pagesize);
var racks = repository.AllFind(withOutSpace);
if(asc)
{
switch(sort)
{
case "price":
racks = racks.OrderBy(a => a.Technology.Price);
break;
case "date":
racks = racks.OrderBy(a => a.Technology.Date);
break;
case default:
racks = racks.OrderBy(a => a.Technology.SerialNumber);
break;
}
}
else
{
switch(sort)
{
case "price":
racks = racks.OrderByDescending(a => a.Technology.Price);
break;
case "date":
racks = racks.OrderByDescending(a => a.Technology.Date);
break;
case default:
racks = racks.OrderByDescending(a => a.Technology.SerialNumber);
break;
}
}
racks = racks.ToPagedList(page, pagesize)

you can use reflection inside the orderBymethod..something like
racks.OrderBy(a => {
//use reflection get the property
PropInfo prop = a.GetType().GetProperty("price");
return prop;
})
I haven't tested this code..this just an idea..

Related

ASP.NET - Help to understand this block of code

I'm trying to understand a block of code from a school project, but I'm not understanding what this does.
I know that this is a controller method and is passing some information to the view?
Can you help me?
public ActionResult Index(int ? page, string sort) {
using(istecEntities ctx = new istecEntities()) {
var Persons = ctx.Persons.ToList()
.Select(p => new Person {
Num = p.num,
Name = p.Name,
Area = p.Area,
Grades = p.Grades as List <Grade>
}).ToList<Person>();
switch (sort) {
case "numdesc":
Persons = Persons.OrderByDescending(p =>p.Num).ToList<Person>();
break;
case "NameDesc":
Persons = Persons.OrderByDescending(p =>p.Name).ToList<Person>();
break;
case "NameAsc":
Persons = Persons.OrderBy(p =>p.Name).ToList<Person>();
break;
default:
Persons = Persons.OrderBy(p =>p.Num).ToList<Person>();
break;
}
ViewBag.sortnum = (String.IsNullOrEmpty(sort)) ?
"numdesc": "";
ViewBag.sortName = (sort == "NameDesc") ?
"NameAsc": "NameDesc";
ViewBag.sort = sort;
int size = 3;
int pagenumber = (page ?? 1);
return View(Persons.ToPagedList<Person>(pagenumber, size));
}
}

CS0266 cannot convert type

I have an error:
CS0266 C# Cannot implicitly convert type
'System.Linq.IQueryable' to
'System.Linq.IOrderedIQueryable'. An
explicit conversion exists (are you missing a cast?)
Here is my controller:
public ViewResult Index(string sortOrder, string currentFilter, string searchString, int? page)
{
ViewBag.CurrentSort = sortOrder;
ViewBag.NameSurnameSortParm = sortOrder == "NameSurname" ? "NameSurname_desc" : "NameSurname";
ViewBag.ReasonSortParm = sortOrder == "Reason" ? "Reason_desc" : "Reason";
ViewBag.AccessSortParm = sortOrder == "Access" ? "Access_desc" : "Access";
ViewBag.UserOrAdminSortParm = sortOrder == "UserOrAdmin" ? "UserOrAdmin_desc" : "UserOrAdmin";
ViewBag.DepartmentSortParm = sortOrder == "Department" ? "Department_desc" : "Department";
ViewBag.UNCPathSortParm = sortOrder == "UNCPath" ? "UNCPath_desc" : "UNCPath";
if (searchString != null)
{
page = 1;
}
else
{
searchString = currentFilter;
}
ViewBag.CurrentFilter = searchString;
var request = from c in _context.RaidRequest
orderby c.Id
select c;
if (!String.IsNullOrEmpty(searchString))
{
request = request.Where(s => s.NameSurname.Contains(searchString));
}
switch (sortOrder)
{
case "NameSurname_desc":
request = request.OrderByDescending(c => c.NameSurname);
break;
case "Reason":
request = request.OrderBy(c => c.Reason);
break;
case "Reason_desc":
request = request.OrderByDescending(c => c.Reason);
break;
case "Access":
request = request.OrderBy(c => c.Access);
break;
case "Access_desc":
request = request.OrderByDescending(c => c.Access);
break;
case "UserOrAdmin":
request = request.OrderBy(c => c.UserOrAdmin);
break;
case "UserOrAdmin_desc":
request = request.OrderByDescending(c => c.UserOrAdmin);
break;
case "Department":
request = request.OrderBy(c => c.Department);
break;
case "Department_desc":
request = request.OrderByDescending(c => c.Department);
break;
case "UNCPath":
request = request.OrderBy(c => c.UNCPath);
break;
case "UNCPath_desc":
request = request.OrderByDescending(c => c.UNCPath);
break;
}
int pageSize = 10;
int pageNumber = (page ?? 1);
return View(request.ToPagedList(pageNumber, pageSize));
}
This part of its broken :
if (!String.IsNullOrEmpty(searchString))
{
request = request.Where(s => s.NameSurname.Contains(searchString));
}
Can you please explain what to do ?
I followed the tutorial word by word. It worked until I added pagination.
could it be something with my View?
All your queries contain an orderby, therefore returning an IOrderedQueryable, except the query where you use the searchstring. This query returns an IQueryable.
Since IOrderedQueryable is inheriting from IQueryable you can assign an IOrderedQueryable to an IQueryable, but not the other way around.
Remove the orderby from the first query, and it will become an IQueryable, add a default: to the switch statement to do the default sorting. This will also prevent you sorting the resulting query twice.
var request = from c in _context.RaidRequest
select c;
// Your code
switch (sortOrder)
{
// other cases
case "UNCPath_desc":
request = request.OrderByDescending(c => c.UNCPath);
break;
default:
request = request.OrderBy(c => c.Id);
break;
}

Dynamic switch statement foreach [duplicate]

This question already has answers here:
How do I specify the Linq OrderBy argument dynamically? [duplicate]
(12 answers)
Closed 4 years ago.
I have just started this week to learn ASP.Net core 2.0 with EF Core and MVC using C#. So i am a complete Noob.
Is they a way to create a dynamic Switch (SortOrder) using a dynamic parameters pulled from a Model/restful API? this is so my Switch statements don't end up 30+ cases deep.
i am looking for something on the lines of this Sudo:
switch (sortOrder)
{
default:
Tickets = Tickets.OrderBy(s => s.ID);
break;
foreach (string Tick in Tickets)
{
case Tick :
Tickets = Tickets.OrderBy(T => Tick);
break;
}
for reference part of my TicketController
public async Task<IActionResult> Index(string sortOrder, string searchString)
{
//sorting
ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "ID" : "";
ViewData["DateSortParm"] = sortOrder == "ID" ? "Environment" : "Date";
ViewData["CurrentFilter"] = searchString;
var Tickets = from s in _context.MyTickets
select s;
// search
if (!String.IsNullOrEmpty(searchString))
{
Tickets = Tickets.Where(T => T.ID.ToString().Contains(searchString)
|| T.Environment.Contains(searchString)
|| T.YourRef.Contains(searchString)
|| T.Priority.Contains(searchString)
|| T.Type.Contains(searchString)
|| T.ProductName.Contains(searchString)
|| T.Environment.Contains(searchString)
|| T.Version.Contains(searchString)
|| T.Description.Contains(searchString)
|| T.Status.Contains(searchString)
|| T.Contact.Contains(searchString));
}
////sorting
//switch (sortOrder)
//{
// case "ID":
// Tickets = Tickets.OrderByDescending(s => s.ID);
// break;
// case "Date":
// Tickets = Tickets.OrderBy(s => s.CreatedDate);
// break;
// case "Environment":
// Tickets = Tickets.OrderBy(s => s.Environment);
// break;
// default:
// Tickets = Tickets.OrderBy(s => s.ID);
// break;
//}
switch (sortOrder)
{
default:
Tickets = Tickets.OrderBy(s => s.ID);
break;
foreach (string Tick in Tickets)
{
case Col :
Tickets = Tickets.OrderBy(T => Col)
}
}
// return results
return View(await Tickets.AsNoTracking().ToListAsync());
}
This will use reflection to sort by a property matching the sortOrder string:
Tickets = Tickets.OrderBy(s => s.GetProperty(sortOrder).GetValue(s));
The sortOrder will need to exactly match the name of the property you want to sort by ("CreatedDate", not "Date").
Alternatively you can change your method signature to accept a function instead of a string for the sortOrder:
public async Task<IActionResult> Index(Func<Ticket, object> sortOrder, string searchString)
{
...
Tickets = Tickets.OrderBy(s => sortOrder(s));
...
}
And you can call this passing (s => s.Environment, ...

how to sort on a field not stored within the db

I have an asp.net mvc site and I'm unable to sort on a field that is calculated when needed in the model.
private decimal _total = -1;
public decimal Total
{
get
{
if (_total < 0)
{
_total = get_total(TableId);
}
return _total;
}
}
private decimal get_total(int id)
{
....Many Calcs
}
I'm trying to sort on Total, but I get the error:
Additional information: The specified type member 'Total' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
Here is my actionlink:
#Html.ActionLink("By Total", "Index", new { sortOrder = ViewBag.Total, currentFilter = ViewBag.CurrentFilter }, new { #class = "btn btn-danger" })
I have found some similar issues, but I just can't figure out what how to sort by this.
And my controller. I tried to edit this down for clarity.
public ActionResult Index(string sortOrder)
{
ViewBag.CurrentSort = sortOrder;
ViewBag.Total = sortOrder == "total" ? "total_desc" : "total";
var records = from u in db.Records.Include(t => t.User).Where(t => t.Active == true)
select u;
switch (sortOrder)
{
case "total":
records = db.Records.OrderBy(u => u.Total).Where(t => t.Active == true);
break;
case "rating_desc":
records = db.Records.OrderByDescending(u => u.Total).Where(t => t.Active == true);
break;
default:
records = db.Records.OrderBy(u => u.Title).Where(t => t.Active == true);
break;
}
return View(records.ToList());
}
Try to call ToList() method before trying to order by this property as this cannot be translated to an SQL statement.
// I assume currently your query is something like this
DbContext.SomeEntity.Where(...).OrderBy(e => e.Total);
// After calling .ToList() you can sort your data in the memory (instead of in db)
DbContext.SomeEntity.Where(...).ToList().OrderBy(e => e.Total);
UPDATE:
The problem is that first you declare the records variable with this line:
var records = from u in db.Records.Include(t => t.User).Where(t => t.Active == true) select u;
Because of this the type of the records variable will be System.Linq.IQueryable<Project.Models.Record> and that's why in the switch case you "needed" to cast with .AsQueryable().
Additionally the initial value will be always overridden in the switch statement therefore it is totally unnecessary to initialize it as you do it currently.
What you should do:
public ActionResult Index(string sortOrder)
{
/* ViewBag things */
IEnumerable<Record> records =
db
.Records
.Include(record => record.User)
.Where(record => record.Active)
.ToList(); // At this point read data from db into memory
// Total property cannot be translated into an SQL statement.
// That's why we call it on memory objects instead of DB entities.
switch (sortOrder)
{
case "total":
records = records.OrderBy(record => record.Total);
break;
case "rating_desc":
records = records.OrderByDescending(record => record.Total);
break;
default:
records = records.OrderBy(record => record.Title);
break;
}
return View(records.ToList());
}
I needed to cast my query as IQueryable, so here is the updated switch:
switch (sortOrder)
{
case "total":
records = db.Records.Where(t => t.Active == true).AsQueryable().OrderBy(u => u.Total));
break;
case "rating_desc":
records = db.Records.Where(t => t.Active == true).AsQueryable.OrderByDescending(u => u.Total).;
break;
default:
records = db.Records.Where(t => t.Active == true).AsQueryable.OrderBy(u => u.Title).;
break;
}

C# non-null parameters to be appended to a string

I have a method as shown below.
public IResults GetResults(
string code = "",
string id = "",
DateTime? registerDate = null,
IEnumerable<Category> type = null,
int pageNum = 1,
bool all = false)
{
....
........
}
For every non-null or non-empty parameter I would like to have the parameter name and value to appended to the URI string.
For example, if the parameter code is a non-empty string then I would want it to be appended to the standard URI result/? as results/?code=passedcodevalue. If it is empty then there would be no need to append it.
Similarly, these are the rules to construct the URI:
when code or id is not empty append it to - results/? - as
results/?code=abc&id=xyz
when registerDate is not null append it to - results/? - as
results/?code=abc&id=xyz&registerdate=2004-06-29
when type is not null append it to - results/? - as
results/?code=abc&id=xyz&registerdate=2004-06-29&type=qwerty
when pagenum is not 1 append it to - results/? - as
results/?code=abc&id=xyz&registerdate=2004-06-29&type=qwerty&pagenum=5
when all is not false append it to - results/? - as
results/?code=abc&id=xyz&registerdate=2004-06-29&type=qwerty&pagenum=5&all=true
What is the optimal and generic way to do this?
Try this:
public string GetResults(
string code = "",
string id = "",
DateTime? registerDate = null,
IEnumerable<Category> type = null,
int pageNum = 1,
bool all = false)
{
var result = "results/?";
var firstFound = false; //Used for add & to string
for (int i = 0; i < 6; i++)
{
var partial = string.Empty;
switch (i)
{
case 0:
partial = GetIfNotEmpty("code", code);
break;
case 1:
partial = GetIfNotEmpty("id", id);
break;
case 2:
partial = GetIfNotEmpty("registerDate", registerDate);
break;
case 3:
partial = GetIfNotEmpty("type", type);
break;
case 4:
partial = GetIfNotEmpty("pageNum", pageNum);
break;
default:
partial = GetIfNotEmpty("all", all);
break;
}
if (string.IsNullOrWhiteSpace(partial))
continue;
if (firstFound)
result += "&";
else
firstFound = true;
result += partial;
}
return result;
}
private string GetIfNotEmpty<T>(string name, T value)
{
if (value == null)
return string.Empty;
if (value is IEnumerable)
{
var enumerableFinalString = string.Empty;
foreach (var item in ((IEnumerable)value))
{
enumerableFinalString += item.ToString();
}
return string.Format("{0}={1}", name, enumerableFinalString);
}
return string.Format("{0}={1}", name, value.ToString());
}

Categories