I am trying to sort data which is from Generic List object.
By using below code, I can sort my data according to Title column.
But what I would like to do is I would like to sort data according to parameter value called sidx.
public ActionResult ListingGridData(string sidx, string sord, int page, int rows)
{
int currentPage = Convert.ToInt32(page) - 1;
int totalRecords = 0;
var SeminarList = (List<Seminar>)null;
if(sord.Equals("asc"))
SeminarList = seminarRepository.AllSeminarList().Seminar_Masters
.OrderBy(x => x.Title )
.Skip(currentPage * rows)
.Take(rows)
.ToList();
else
SeminarList = seminarRepository.AllSeminarList().Seminar_Masters
.OrderByDescending(x => x.Title)
.Skip(currentPage * rows)
.Take(rows)
.ToList();
totalRecords = seminarRepository.AllSeminarList().Seminar_Masters.Count;
var totalPages = (int)Math.Ceiling((float)totalRecords / (float)rows);
....
}
So I have modified my code like that, but it does not work.
It does not sort as I have expected.
SeminarList = seminarRepository.AllSeminarList().Seminar_Masters
.OrderBy(x => sidx)
.Skip(currentPage * rows)
.Take(rows)
.ToList();
The object which I now use is pure List object, not IQueryable object.
As for sorting purpose, I don't want to go back to SQL select statement, I would like to sort data at Controller layer.
Please let me get any suggestions.
You must order by a column that is in your result set. If the sidx is not part of your resultset then it is like ordering by a constant. Thus every row will be the same and it will not REALLY be ordered. Let's assume sidx's value is 10, then it would be like ordering this resultset:
Row 1 | 10
Row 2 | 10
Row 3 | 10
...
You can see how the ordering would be pointless
What you need to do is add sidx to your result set if that is what you want:
SeminarList = seminarRepository.AllSeminarList().Seminar_Masters
.OrderBy(x => x.sidx)
.Skip(currentPage * rows)
.Take(rows)
.ToList();
See answer for dynamic where and orderby using some helper
methods here
where can I find a good example of using linq & lambda expressions to generate dynamic where and orderby sql?
So you're saying that the calling method will provide a Property name via the sidx parameter, and you want to sort by that property?
The easiest way is to use the Dynamic LINQ library. Another option would be to use reflection to find the property you're looking for, then build a lambda expression from it.
Related
I need help with filtering list data in c#.
I got 3 class named Product.cs, Storage.cs and Inventory.cs.
public class Storage{
string StorageId;
string Name;
}
public class Inventory{
string InventoryId;
string StorageId;
string ProductId;
}
I got the filled List<Storage> mStorages, List<Product> mProduct and List<Inventory> mInventories.
I have trouble to print mStorages that contain with specific productId that only can be obtained from mInventories.
So, I tried this:
List<Storage> mFilteredStorage;
for(int i=0;i<mStorages.Count;i++){
if(mStorages[i] contain (productId from inventories)){
mFilteredStorage.add(mstorages[i]);
}
So I can get mFilteredStorage that contains specific product from inventories. (in inventories there are lot of product id).
What should I do to get that filteredStorage? I tried to use list.contains() but it only return true and at last there are duplicated storage at mFilteredStorage.
Really need your help guys. Thanks in advance.
I suggest you to read about lambda-expressions, that is what you are looking for.
mFilteredStorage.AddRange(mStorages.Where(storage => inventories.Any(inventory => inventory.productId == storage.productId)).ToList());
This returns you a list with your filtered conditions. So right after Where you iterate over each item in your list, I called this item storage. (you can name those what ever you want to) Then we iterate over your object inventories with another lambda expression. This, the second lambda expression, returns either true if any of inventories's productIds match the productId of the current iterating object of mStorages or false if they don't match.
So you once the productIds match you can imagine the code like the following:
mStorages.Where(storage => true);
And once the result of the second lambda expression is true, storage will be added to the IEnumerable you will get as a result of the Where method.
Since we get an IEnumerable as return, but we want to add those Storage objects to mFilteredStorage, I convert the IEnumerable to a list, by:
/*(the return object we get from the `Where` method)*/.ToList();
You can use LINQ to accomplish your goal. Since Storage has no ProductId, the query will match by StorageId.
var filteredStoragesQry =
from storage in mStorages
where inventories.Any(inventory => inventory.StorageId == storage.StorageId)
select storage;
mFilteredStorages = filteredStoragesQry.ToList();
This query is for LINQ to objects, but it will also work in Entity Framework, when you replace mStorages and inventories by the respective DbSet objects from the context.
mStorages.Join(mInventories, x => x.StorageId, y => y.StorageId, (x, y) => new { Storage = x, ProductId = y.ProductId})
.Where(z => z.ProductId == "specificProductId").Select(z => z.Storage).ToList()
I ended with this code.
mFilteredStorage = tempStorage.GroupBy(s => s.Id).Select(group => group.First()).ToList()
This code is what I want to show.
I'm using DataTables.Mvc library for use with jQuery DataTables.
One of the methods is GetSortedColumns() which returns an array containing configurations for each column to be sorted.
Of interest in this object are the Name and SortDirection properties. Name is also the database table field name. SortDirection is either asc or desc.
At first ThenBy and ThenByDescending were undefined symbols, so I created ordered as IOrderedQueryable. This resolves the symbols, but I don't see any effect of these. Neither OrderBy, OrderByDescending, ThenBy nor ThenByDescending have any effect on the order of records in filteredRecords.
In Controller:
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult GetUserSelections([ModelBinder(typeof(DataTablesBinder))] IDataTablesRequest requestModel)
{
// Column Sort
var filteredRecords = db.AspNetSelectorInputs.Select(si => si);
var sortedColumns = requestModel.Columns.GetSortedColumns();
var count = 0;
foreach (var column in sortedColumns)
{
var ordered = filteredRecords as IOrderedQueryable<AspNetSelectorInput>;
filteredRecords =
column.SortDirection == DataTables.Mvc.Column.OrderDirection.Ascendant
? count == 0
? ordered.OrderBy(c => column.Name)
: ordered.ThenBy(c => column.Name)
: count == 0
? ordered.OrderByDescending(c => column.Name)
: ordered.ThenByDescending(c => column.Name);
count++;
}
filteredRecords = filteredRecords.Select(si => si).Skip(requestModel.Start).Take(requestModel.Length);
....
Can anyone see why this doesn't affect ordering of filteredRecords?
Is there a better way?
It is sorting, on exactly what you've asked it to. But the lambda expressions aren't doing what you think. For example, you're doing .OrderBy(c => column.Name), which is sorting using a literal value of the name of the column which has the same value for every item in the collection (notice how the thing it is sorting on is not affected by c), so it appears not to sort your collection. For example, you might as well be doing .OrderBy(c => "Hello").
You would need to do something like .OrderBy(c => c.YourChoiceOfPropertyName). Except you can't do that because (presumably) the name of the property is a string value in column.Name. So you'll need to use reflection within the lambda to get the value of that property using c as the instance. This will need fixing on all the lambdas. For example, inside the loop:
var propertyInfo=typeof(AspNetSelectorInput)
.GetProperty(column.Name);
And replacement lambda expressions:
c=>propertyInfo.GetValue(c)
P.S. the two instances of .Select(si => si) seem to be redundant, unless I am missing something.
I want to fetch value of field named "Gram" from the last record and put its value into a variable, without using any conditions.
First I tried
int value = int.Parse(Entity.TblGold.LastOrDefault().Gram.ToString());
Second I tried
int value = int.Parse(Entity.TblGold.Select(p => p.Gram).Last().ToString());
I just receive this exception:
LINQ to Entities does not recognize the method 'DataModel.TblGold LastOrDefault[TblGold](System.Linq.IQueryable``1[DataModel.TblGold])' method, and this method cannot be translated into a store expression.
Last or LastOrDefault are not supported in LINQ to Entities. You can either iterate your query using ToList or ToArray and then apply Last or you can order by descending and then use the First like:
int value = int.Parse(Entity.TblGold
.OrderByDescending(p => p.Gram)
.Select(r => r.Gram)
.First().ToString());
You can't do it in one query, but you can do it in two.
var countOfRows = tbl.Count();
var lastRow = tbl.Skip(countOfRows - 1).FirstOrDefault();
If you have an incremental id:
int idx = tb.Max(i => i.Id);
var row = tb.FirstOrDefault(i => i.Id == idx);
It is your answer and you don't need to convert.
int value = Entity.TblGold.OrderByDescending(p => p.Id).First().Gram;
You can use order by 1 == 1 and it works
var countOfRows = tbl.Count();
var lastRow = tbl.OrderBy(c => 1 == 1).Skip(countOfRows - 1).FirstOrDefault();
I want to run this LINQ simple code to have record number in LINQ but result is beneath error
var model = _db2.Persons.Select(
(x, index) => new
{
rn = index + 1,
col1 = x.Id
}).ToList();
Error:
LINQ to Entities does not recognize the method
'System.Linq.IQueryable1[<>f__AnonymousType22
[System.Int32,System.Int32]] Select[Person,<>f__AnonymousType22](System.Linq.IQueryable1
[MvcApplication27.Models.Person], System.Linq.Expressions.Expression1[System.Func3
[MvcApplication27.Models.Person,System.Int32,<>f__AnonymousType2`2
[System.Int32,System.Int32]]])' method, and this method cannot be translated into a store
expression.
The problem is that LINQ to Entities doesn't understand how to convert that Select overload (the one that gives you the index) into a SQL query. You can fix this by first selecting the portion from the DB you need (to avoid selecting every column unnecessarily), then doing AsEnumerable() to take it as an IEnumerable<T> instead of an IQueryable<T>, and then doing the Select purely in C# (in short, IQueryable<T>s are converted to SQL, while IEnumerable<T>s are run in code).
var model = _db2.Persons.Select(x => x.Id).AsEnumerable().Select(
(id, index) => new
{
rn = index + 1,
col1 = id
}).ToList();
Note that the query as you have it appears to be unordered, so the id/index pairings can change each time you call this. If you expected consistency, you should order by something (e.g. _db2.Persons.OrderBy(...)).
Edit
Adding comment from Scott:
As a nice reference here is the list of all Linq statements built in
to the framework and a listing if it is compatible or not.
You could just select the Id and after it create your own anonymous object using linq to objects, for sample:
var model = _db2.Persons.Select(x => x.Id)
.ToList() // return int[]
.Select((id, index) => new
{
rn = index + 1,
col1 = id
}) // return anonymous[] (with rn and col1)
.AsEnumerable(); // get an IEnumerable (readonly collection)
Problably this is happen because Entity Framework does not support this kind of query using linq as linq could do in memory, so, in this case, you could select just you need (id in your case) and execute it, using ToList() method to concretize your query and after that you will have a list on memory, so, you can use linq to objects and use the supported method as you want.
I have a generic List List[int, myClass], and I would like to find the smallest int value, and retrieve the items from the list that match this.
I am generating this from another LINQ statement
var traysWithExtraAisles = (from t in poolTrays
where t.TrayItems.Select(i=>i.Aisle)
.Any(a=> ! selectedAisles.Contains(a))
select new
{
count= t.TrayItems.Select(i=>i.Aisle)
.Count(a=> !selectedAisles.Contains(a)),
tray=t
}).ToList();
this gives me my anonymous List of [count, Tray], but now I want to figure out the smallest count, and return a sublist for all the counts that match this.
Can anyone help me out with this?
var smallestGroup = traysWithExtraAisles
.GroupBy(x => x.count)
.OrderBy(g => g.Key)
.First();
foreach(var x in smallestGroup)
{
var poolTray = x.tray;
}
You can use SelectMany to "flatten" your list. Meaning, combine all of the lists into one, then take the Min. So;
int minimum = poolTrays.SelectMany(x => x).Min(x => x.TheIntegerIWantMinOf);
Will give you the smallest value contained in the sub lists. I'm not entirely sure this is what you're asking for but if your goal is simply to find the smallest element in the collection then I would scrap the code you posted and use this instead.
Right, I now realise this is actually incredibly easy to do with a bit more fiddling around. I have gone with
int minCount = traysWithExtraAisles.Min(x=>x.count);
var minAislesList = (from t in trayswithExtraAisles
where t.count==mincount
select t).ToList()
I imagine it is probably possible to do this in one statement
You can use GroupBy as answered by Tim... or OrderBy as follow:
var result = traysWithExtraAisles.OrderBy(x=>x.count)
.TakeWhile((x,i)=> i == 0 || x.count == traysWithExtraAisles[i-1]).count;