Dynamic switch statement foreach [duplicate] - c#

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

Related

C# Refactor code to make re-usable with generics

This is a very loaded question but I am new to using generics and want to become proficient using them and taking my C#/.NET skills to the next level. I created a data filter using multiple dropdowns. Before I break the below method into smaller encapsulated, single responsibility methods, I was hoping I could get some help.
My goal is to re-use this filter in many parts of our application. In order to do so I will have to add the "filter" methods to our common base EntityController and EntityRespository classes. Obviously I am going to have to make use of generics to accomplish this task. Can anyone give me some advice after looking at the below (ridiculously long) method?
[HttpPost("FilteredResults")]
[ProducesResponseType(typeof(PaginatedList<ProcessCard>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status422UnprocessableEntity)]
[ProducesDefaultResponseType]
[Produces("application/json")]
public async Task<IActionResult> GetFilteredBusinessProcesses([FromBody] List<FieldFilterCriteria> filterCriteriaList = null)
{
try
{
List<BusinessProcess> listOfCommonItems = new List<BusinessProcess>();
PaginatedList<BusinessProcess> paginatedListOfCommonItems = new PaginatedList<BusinessProcess>();
var orgIdGuid = filterCriteriaList[0]?.OrganizationId;
if (filterCriteriaList != null)
{
var results = repository.Find(null, a => a.OrganizationId == orgIdGuid);
foreach (FieldFilterCriteria filter in filterCriteriaList)
{
foreach (string value in filter?.FilterValues)
{
switch (filter.Field)
{
case "Department":
var dList = results?.Items.Where(x => x.OrganizationUnitId == Guid.Parse(value)).ToList();
listOfCommonItems.AddRange(dList);
break;
case "Status":
var sList = results?.Items.Where(x => x.Status == value).ToList();
listOfCommonItems.AddRange(sList);
break;
case "Priority":
var pList = results?.Items.Where(x => x.Priority == value).ToList();
listOfCommonItems.AddRange(pList);
break;
case "Owner":
var oList = results?.Items.Where(x => x.OwnerName == value).ToList();
listOfCommonItems.AddRange(oList);
break;
}
}
}
if (filterCriteriaList.Count == 1)
{
paginatedListOfCommonItems.Items = listOfCommonItems;
}
if (filterCriteriaList.Count > 1)
{
listOfCommonItems = listOfCommonItems.GroupBy(x => x).Where(x => x.Count() > 1).Select(x => x.Key).ToList();
paginatedListOfCommonItems.Items = listOfCommonItems;
}
var businessProcessCard = processCatalogManager.GetBusinessProcessCard(paginatedListOfCommonItems);
return Ok(businessProcessCard);
}
else
{
return Ok();
}
}
catch (Exception ex)
{
return ex.GetActionResult();
}
}

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

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

DataTables multisort columns .net mvc hint

i'm using Datatables Jquery Extension and i've made multicolum sorting reading those posts:
http://www.codeproject.com/Articles/155422/jQuery-DataTables-and-ASP-NET-MVC-Integration-Part#Sorting
http://farm-fresh-code.blogspot.it/2012/02/mvc-jquery-ui-and-datatable-pluginajax.html
My question is, those two methods are dependent to The model that i'm using in my Controller, in this case
If i need to use similar code in other controllers i need to replicate that and changing model fields and the Type with others, for example .
This seems to me not very DRY.
How to Proceed? Is controller the good position for those two methods?
private IOrderedQueryable<User> CreateSortedQuery(DataTableParameterModel parameterModel, IQueryable<User> baseQuery)
{
var orderedQuery = (IOrderedQueryable<User>)baseQuery;
for (int i = 0; i < parameterModel.iSortingCols; ++i)
{
var ascending = string.Equals("asc", parameterModel.sSortDir[i], StringComparison.OrdinalIgnoreCase);
int sortCol = parameterModel.iSortCol[i];
Expression<Func<User, string>> orderByExpression = GetOrderingFunc(sortCol);
if (orderByExpression != null)
{
if (ascending)
{
orderedQuery = (i == 0)
? orderedQuery.OrderBy(orderByExpression)
: orderedQuery.ThenBy(orderByExpression);
}
else
{
orderedQuery = (i == 0)
? orderedQuery.OrderByDescending(orderByExpression)
: orderedQuery.ThenByDescending(orderByExpression);
}
}
else
{
if (ascending)
{
orderedQuery = (i == 0)
? orderedQuery.OrderBy(c => c.Id)
: orderedQuery.ThenBy(c => c.Id);
}
else
{
orderedQuery = (i == 0)
? orderedQuery.OrderByDescending(c => c.Id)
: orderedQuery.ThenByDescending(orderByExpression);
}
}
}
return orderedQuery;
}
private Expression<Func<User, string>> GetOrderingFunc(int ColumnIndex)
{
Expression<Func<User, string>> InitialorderingFunction;
switch (ColumnIndex)
{
case 1:
InitialorderingFunction = c => c.FirstName;
break;
case 2:
InitialorderingFunction = c => c.LastName;
break;
case 3:
InitialorderingFunction = c => c.UserName;
break;
case 4:
InitialorderingFunction = c => c.Email;
break;
case 5:
InitialorderingFunction = c => c.BusinessName;
break;
default:
InitialorderingFunction = null;
break;
}
return InitialorderingFunction;
}
I guess, your question is pretty close to these two answers:
Property name evaluating from expression:
public static RouteValueDictionary GetInfo<T,P>(this HtmlHelper html, Expression<Func<T, P>> action) where T : class
{
var expression = (MemberExpression)action.Body;
string fieldName = expression.Member.Name;
and
Applying linq sorting passing string values with LINQ Dynamic Query Library:
var result = data
.Where(/* ... */)
.Select(/* ... */)
.OrderBy(fieldName + " asc");

How to dynamically order by the records

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

Categories