DataTables multisort columns .net mvc hint - c#

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");

Related

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

Order by using Iqueryable for datatable

I have following function that prepare data for jquery datatable grid.
Now I am facing following error for Datatype other than string
Unable to cast the type 'System.DateTime' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types.
Code:
public GeneralResponse<IEnumerable<Holiday>> GetHolidays(string filter,
int initialPage,
int pageSize,
out int totalRecords,
out int recordFilterd,
int sortColumn,
string sortDirection)
{
var response = new GeneralResponse<IEnumerable<Holiday>>();
totalRecords = 0;
recordFilterd = 0;
filter = filter.Trim().ToLower();
try
{
Expression<Func<Holiday, dynamic>> expr;
switch (sortColumn)
{
case 0:
expr = p => p.HolidayDate;
break;
case 1:
expr = p => p.Name;
break;
case 2:
expr = p => p.ExchangeMarket.Name;
break;
default:
expr = p => p.CreatedOn;
break;
}
var data = holidayRepository.Query(true);
//var data = holidayRepository.GetAll(true).AsQueryable(); previous working one
totalRecords = data.Count();
//filter
if (!string.IsNullOrWhiteSpace(filter))
{
data = data.Where(x => x.Name.ToLower().Contains(filter));
//todo : Add date search as well
}
recordFilterd = data.Count();
//sort
data = sortDirection == "asc" ? data.OrderBy(expr) : data.OrderByDescending(expr);
data = data
.Skip(initialPage * pageSize)
.Take(pageSize);
var result = data.ToList();
response.Data = result;
}
catch (Exception e)
{
response.Error = true;
response.Exception = e;
}
return response;
}
// This method is under generic repository
public IQueryable<T> Query()
{
return Query(false);
}
earlier I was using IEnumerable which was first loading all data in list & then performing filter which was working fine (but not correct or best practice)
Now I am stuck with the filter part. How can I fix this error to have order by all type of property column?
I did lots of research but could not found the any solution.
dynamic or object cannot be used as TKey generic argument of EF Queryable.OrderBy (actually in any Queryable method expression) - it has to be the actual type of the key. Which in turn means you cannot use a common Expression<Func<...>> variable to hold the keySelector expression.
The solution is to use conditional .OrderBy[Descending] inside your switch / case block.
To make handling the ascending/descending option easier (and avoid expression duplication), start by creating a simple custom extension method like this:
namespace System.Linq
{
public static class QueryableExtensions
{
public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, bool ascending)
{
return ascending ? source.OrderBy(keySelector) : source.OrderByDescending(keySelector);
}
}
}
Then move the switch / case block after the
recordFilterd = data.Count();
line and use the above helper method inside:
bool ascending = sortDirection == "asc";
switch (sortColumn)
{
case 0:
data = data.OrderBy(p => p.HolidayDate, ascending);
break;
case 1:
data = data.OrderBy(p => p.Name, ascending);
break;
case 2:
data = data.OrderBy(p => p.ExchangeMarket.Name, ascending);
break;
default:
data = data.OrderBy(p => p.CreatedOn, ascending);
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, ...

Using LinqToSql without load base in memory

I have one problem. If i'm using LinqToSql, my program load my database in memory.
little example:
//pageNumber = 1; pageSize = 100;
var result =
(
from a in db.Stats.AsEnumerable()
where (DictionaryFilter(a, sourceDictionary) && DateFilter(a, beginTime, endTime) && ErrorFilter(a, WarnLevel))
select a
);
var size = result.Count(); // size = 1007
var resultList = result.Skip((pageNumber-1)*pageSize).Take(pageSize).ToList();
return resultList;
DictionaryFilter, DateFilter and ErrorFilter are functions that filter my datebase.
after this my program use ~250Mb of Ram.
if i dont use:
var size = result.Count();
My program use ~120MB Ram.
Before use this code, my program use ~35MB Ram.
How can I use count and take functions not loading all my datebase in memory?
static bool DateFilter(Stat table, DateTime begin, DateTime end)
{
if ((table.RecordTime >= begin.ToFileTime()) && (table.RecordTime <= end.ToFileTime()))
{
return true;
}
return false;
}
static bool ErrorFilter(Stat table, bool[] WarnLevel)
{
if (WarnLevel[table.WarnLevel]) return true;
else return false;
}
static bool DictionaryFilter(Stat table, Dictionary<GetSourcesNameResult, bool> sourceDictionary)
{
foreach (var word in sourceDictionary)
{
if (table.SourceName == word.Key.SourceName)
{
return word.Value;
}
}
//
return false;
}
Simple: don't use .AsEnumerable(). That means "switch to LINQ-to-Objects". Before that, db.Stats was IQueryable<T>, which is a composable API, and would do what you expect.
That, however, means that you can't use C# methods like DictionaryFilter and DateFilter, and must instead compose things in terms of the Expression API. If you can illustrate what they do I can probably advise further.
With your edit, the filtering can be tweaked, for example:
static IQueryable<Stat> ErrorFilter(IQueryable<Stat> source, bool[] WarnLevel) {
// extract the enabled indices (match to values)
int[] levels = WarnLevel.Select((val, index) => new { val, index })
.Where(pair => pair.val)
.Select(pair => pair.index).ToArray();
switch(levels.Length)
{
case 0:
return source.Where(x => false);
case 1:
int level = levels[0];
return source.Where(x => x.WarnLevel == level);
case 2:
int level0 = levels[0], level1 = levels[1];
return source.Where(
x => x.WarnLevel == level0 || x.WarnLevel == level1);
default:
return source.Where(x => levels.Contains(x.WarnLevel));
}
}
the date filter is simpler:
static IQueryable<Stat> DateFilter(IQueryable<Stat> source,
DateTime begin, DateTime end)
{
var from = begin.ToFileTime(), to = end.ToFileTime();
return source.Where(table => table.RecordTime >= from
&& table.RecordTime <= to);
}
and the dictionary is a bit like the levels:
static IQueryable<Stat> DictionaryFilter(IQueryable<Stat> source,
Dictionary<GetSourcesNameResult, bool> sourceDictionary)
{
var words = (from word in sourceDictionary
where word.Value
select word.Key.SourceName).ToArray();
switch (words.Length)
{
case 0:
return source.Where(x => false);
case 1:
string word = words[0];
return source.Where(x => x.SourceName == word);
case 2:
string word0 = words[0], word1 = words[1];
return source.Where(
x => x.SourceName == word0 || x.SourceName == word1);
default:
return source.Where(x => words.Contains(x.SourceName));
}
}
and:
IQueryable<Stat> result = db.Stats;
result = ErrorFilter(result, WarnLevel);
result = DateFiter(result, beginTime, endTime);
result = DictionaryFilter(result, sourceDictionary);
// etc - note we're *composing* a filter here
var size = result.Count(); // size = 1007
var resultList = result.Skip((pageNumber-1)*pageSize).Take(pageSize).ToList();
return resultList;
The point is we're now using IQueryable<T> and Expression exclusively.
The following SO question might explain things: Understanding .AsEnumerable in Linq To Sql
.AsEnumerable() loads the entire table.

C# LINQ to Entities does not recognize the method

I'm trying to write a function that sorts by column name
I have a problem in the second case case = "Count". How can I convert convert IQueryable to IOrderedQueryable.
Thank You
public PagedResults<activity> getList(Expression<Func<activity, bool>> criteria, PagingSpecification paging)
{
IQueryable<activity> search = criteria != null? Query.Where(criteria): Query;
IOrderedQueryable<activity> orderedQuery;
switch (paging.SortColumnName)
{=
case "Name":
orderedQuery = (paging.SortDirection == SortDirection.Ascending)
? search.OrderBy(e => e.Person.Name)
: search.OrderByDescending(e => e.Person.Name);
break;
case "Count":
orderedQuery = (paging.SortDirection == SortDirection.Ascending)
? search.ToList().OrderBy(e => e.GetCount()).AsQueryable<activity>()
: search.ToList().OrderByDescending(e => e.GetCount()).AsQueryable<activity>();
break;
default:
orderedQuery = GetOrderedQueryable(search, paging.SortColumnName, paging.SortDirection);
break;
}
IQueryable<activity> dataQuery = orderedQuery.Skip(paging.StartIndex).Take(paging.PageSize);
return new PagedResults<activity>(dataQuery.ToList(), search.LongCount());
}
Call AsQueryable before OrderBy:
orderedQuery = (paging.SortDirection == SortDirection.Ascending)
? search.ToList().AsQueryable<activity>().OrderBy(e => e.GetCount())
: search.ToList().AsQueryable<activity>().OrderByDescending(e => e.GetCount());
OrderBy turns an IEnumerable (like List) into an IOrderedEnumerable, but turns an IQueryable into an IOrderedQueryable.

Categories