How to keep GridView's sorting state? (Ascending & Descending) - linq

I have a problem that has been bugging me for quite some time and I'm in desperate need of help due to I am a beginner in .NET.
I am using a GridView to display the query results. However, instead of injecting it directly to the entity/LINQ data source, I manually code the events such as Load, Sorting and Paging. The problem is in the Sorting, I can't keep the ascending/descending state. One of the possible solutions that I can think of is by Caching the state, however, I feel like there is another way that is more neat. Thus, can you guys suggest me any other ideas that suit better?
Thanks a lot in advance!
Below is the code that I use for the sorting. Apparently, e.SortDirection will always equals to ascending no matter how many times I have clicked the Column's Header.
switch (e.SortExpression)
{
case "Album":
if (e.SortDirection == SortDirection.Ascending)
_orderedResult = from doc in _result
orderby doc.DocumentAlbum.Name ascending
select doc;
else
_orderedResult = from doc in _result
orderby doc.DocumentAlbum.Name descending
select doc;
break;
case "Category":
if (e.SortDirection == SortDirection.Ascending)
_orderedResult = from doc in _result
orderby doc.DocumentCategory.Name ascending
select doc;
else
_orderedResult = from doc in _result
orderby doc.DocumentCategory.Name descending
select doc;
break;
case "Title":
if (e.SortDirection == SortDirection.Ascending)
_orderedResult = from doc in _result
orderby doc.Title ascending
select doc;
else
_orderedResult = from doc in _result
orderby doc.Title descending
select doc;
break;
case "Description":
if (e.SortDirection == SortDirection.Ascending)
_orderedResult = from doc in _result
orderby doc.Description ascending
select doc;
else
_orderedResult = from doc in _result
orderby doc.Description descending
select doc;
break;
case "DateCreated":
if (e.SortDirection == SortDirection.Ascending)
_orderedResult = from doc in _result
orderby doc.DateCreated ascending
select doc;
else
_orderedResult = from doc in _result
orderby doc.DateCreated descending
select doc;
break;
case "DateUpdated":
if (e.SortDirection == SortDirection.Ascending)
_orderedResult = from doc in _result
orderby doc.DateUpdated ascending
select doc;
else
_orderedResult = from doc in _result
orderby doc.DateUpdated descending
select doc;
break;
}

Actually, I just found the answer. I used the ViewState function to keep track of the state.
This is the function that I used:
private SortDirection GetSortDirection(string column)
{
// By default, set the sort direction to ascending
SortDirection _sortDirection = SortDirection.Ascending;
// Retrieve the last column that was sorted
string _sortExpression = ViewState["SortExpression"] as string;
if (_sortExpression != null)
{
// Check if the same column is being sorted.
// Otherwise, the default value can be returned.
if (_sortExpression == column)
{
string _lastDirection = ViewState["SortDirection"] as string;
if ((_lastDirection != null) && (_lastDirection == "ASC"))
{
_sortDirection = SortDirection.Descending;
}
}
}
// Save new values in ViewState.
ViewState["SortDirection"] = _sortDirection.ToString();
ViewState["SortExpression"] = column;
return _sortDirection;
}

protected void gvOfflineSub_Sorting(object sender, GridViewSortEventArgs e)
{
IList<SellerDetails> Ilist = gvOfflineSub.DataSource as IList<SellerDetails>;
DataTable dt = ToDataTable<SellerDetails>(Ilist);
if (dt != null)
{
DataView dataView = new DataView(dt);
dataView.Sort = e.SortExpression + " " + ConvertSortDirectionToSql(e.SortDirection);
gvOfflineSub.DataSource = dataView;
gvOfflineSub.DataBind();
}
}
/// <summary>
/// Description:Following Method is for Sorting Gridview.
/// </summary>
/// <param name="sortDirection"></param>
/// <returns></returns>
private string ConvertSortDirectionToSql(SortDirection sortDirection)
{
string newSortDirection = String.Empty;
switch (sortDirection)
{
case SortDirection.Ascending:
newSortDirection = "ASC";
break;
case SortDirection.Descending:
newSortDirection = "DESC";
break;
}
return newSortDirection;
}

If You are using LINQ then it is very simple to sort
just bind grid like this
var data = (from d in edc.TableName
select new
{
d.Address,
d.InsertedDate,
d.ID
}).OrderByDescending(d => d.ID);
if (data != null)
{
grdList.DataSource = data;
grdList.DataBind();
}

Related

Code Rewite for tuple and if else statements by using LINQ

In my C# application i am using linq. I need a help what is the syntax for if-elseif- using linq in single line. Data, RangeDate are the inputs. Here is the code:
var Date1 = RangeData.ToList();
int record =0;
foreach (var tr in Date1)
{
int id =0;
if (tr.Item1 != null && tr.Item1.port != null)
{
id = tr.Item1.port.id;
}
else if (tr.Item2 != null && tr.Item2.port != null)
{
id = tr.Item2.port.id;
}
if (id >0)
{
if(Data.Trygetvalue(id, out cdat)
{
// Do some operation. (var cdata = SumData(id, tr.item2.port.Date)
record ++;
}
}
}
I think your code example is false, your record variable is initialized to 0 on each loop so increment it is useless .
I suppose that you want to count records in your list which have an id, you can achieve this with one single Count() :
var record = Date1.Count(o => (o.Item1?.port?.id ?? o.Item2?.port?.id) > 0);
You can use following code:
var count = RangeData.Select(x => new { Id = x.Item1?.port?.id ?? x.Item2?.port?.id ?? 0, Item = x })
.Count(x =>
{
int? cdate = null; // change int to your desired type over here
if (x.Id > 0 && Data.Trygetvalue(x.Id, out cdat))
{
// Do some operation. (var cdata = SumData(x.Id, x.Item.Item2.port.Date)
return true;
}
return false;
});
Edit:
#D Stanley is completely right, LINQ is wrong tool over here. You can refactor few bits of your code though:
var Date1 = RangeData.ToList();
int record =0;
foreach (var tr in Date1)
{
int? cdat = null; // change int to your desired type over here
int id = tr.Item1?.port?.id ?? tr.Item2?.port?.id ?? 0;
if (id >0 && Data.Trygetvalue(id, out cdat))
{
// Do some operation. (var cdata = SumData(id, tr.Item2.port.Date)
record ++;
}
}
Linq is not the right tool here. Linq is for converting or querying a collection. You are looping over a collection and "doing some operation". Depending on what that operation is, trying to shoehorn it into a Linq statement will be harder to understand to an outside reader, difficult to debug, and hard to maintain.
There is absolutely nothing wrong with the loop that you have. As you can tell from the other answers, it's difficult to wedge all of the information you have into a "single-line" statement just to use Linq.

Dynamically Set Column Name In LINQ Query

Im trying to write a method which will allow me to search different DataTables, over different columns.
So far i have the following:
string selectedValue;
string searchColumn;
string targetColumn;
var results = (from a in dt.AsEnumerable()
where a.Field<string>(searchColumn) == selectedValue
select new
{
targetColumn = a.Field<string>(targetColumn)
}).Distinct();
Which kind of gets the job done, but I'm left with the column name as targetColumn rather than the actual column name I want.
Is there any way to resolve this?
Thanks in advance
CM
I make a LINQ to Datatables
public List<DataRow> Where(this DataTable dt, Func<DataRow, bool> pred)
{
List<DataRow> res = new List<DataRow>();
try {
if (dt != null && dt.Rows.Count > 0) {
for (i = 0; i <= dt.Rows.Count - 1; i++) {
if (pred(dt(i))) {
res.Add(dt(i));
}
}
}
} catch (Exception ex) {
PromptMsg(ex);
}
return res;
}
Usage :
var RowsList = dt.Where(f => f("SomeField").toString() == "SomeValue" ||
f("OtherField") > 5);

How to optimize code for Sorting?

I would like to optimize following lines of code for Sorting.
public ViewResult Index(string sortorder, int? pagesize, int? page)
{
int pageSize = pagesize ?? 10;
if (Request.HttpMethod != "GET")
{
page = 1;
pageSize = 10;
}
ViewBag.SelectedPageSize = pageSize;
ViewBag.CurrentSort = sortorder;
ViewBag.FirstNameSortParm = String.IsNullOrEmpty(sortorder) ? "FirstName desc" : "";
ViewBag.LastNameSortParm = sortorder == "LastName" ? "LastName desc" : "LastName";
ViewBag.DepNameSortParm = sortorder == "depName" ? "depName desc" : "depName";
var joined = from tm in db.TabMasters select tm;
switch (sortorder)
{
case "FirstName":
joined = joined.OrderBy(m => m.FirstName);
break;
case "FirstName desc":
joined = joined.OrderByDescending(m => m.FirstName);
break;
case "LastName":
joined = joined.OrderBy(m => m.LastName);
break;
case "LastName desc":
joined = joined.OrderByDescending(m => m.LastName);
break;
case "depName":
joined = joined.OrderBy(m => m.depName);
break;
case "depName desc":
joined = joined.OrderByDescending(m => m.depName);
break;
default:
joined = joined.OrderBy(m => m.FirstName);
break;
}
int pageIndex = (page ?? 1) - 1;
int start = (pageIndex * pageSize);
ViewBag.TotalRecord = joined.Count();
ViewBag.StartRecord = start + 1;
ViewBag.EndRecord = ((start + pageSize) >= ViewBag.TotalRecord) ? ViewBag.TotalRecord : (start + pageSize);
return View(joined.ToPagedList(pageIndex, pageSize));
}
Because this is very tedious way if i have more the 10 fields to perform sort.
Thanks,
Imdadhusen
It's a bit vague to me what your actual goal is but for the switch part you could use an extension method as the below.
public static class SortExtensions
{
public static IEnumerable<T> SortByField<T>(this IEnumerable<T> sequence, string sortOrder)
{
var tokens = sortOrder.Trim().Split(' ');
var field = tokens[0];
var direction = tokens.Skip(1).Single().ToLower();
var prop = typeof(T).GetProperty(field);
return direction == "desc"
? sequence.OrderByDescending(m => prop.GetValue(m, null))
: sequence.OrderBy(m => prop.GetValue(m, null));
}
}
It will make a very simplified parsing of the sort order. It puts the responsibility on the calling party which is generally not what you want to do, so you might want some error handling in case the sortorder string does not fulfill the requirements.
from the sortorder string it fetches a name used to identify a property which can be used to fetch the value used for sorting.
you can use it like this:
db.TabMasters.SortByField(sortOrder)
EDIT based on comment:
The line typeof(T).GetProperty(field) is fragile in the absence of any error handling. It relies on the first token to be a name of a public property of the type T. It will return null if the name doesn't match a property. Including if it matches a Field name. A similar function exist for getting a FieldInfo
prop.GetField(field) will return a fieldinfo object of there's a public field with the given name otherwise null. To get the value of a field simply omit the last parameter to the GetValue call.
You should take a look at Linq.DynamicQuery.
There's more info in this blogpost http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
The library lets you write following code:
var query = northwind.Products
.Where("CategoryID = 3 AND UnitPrice > 3")
.OrderBy("SupplierID");
instead of
var query = from p in northwind.Products
where p.CategoryID == 3 && p.UnitPrice > 3
orderby p.SupplierID
select p;
If you want to add the sortdirection:
var query = northwind.Products.OrderBy("SupplierID Descending");

Good practice for using "NOT IN" in a LINQ query

I have converted the following SQL query to LINQ with the exception of the "NOT IN" subquery.
What would be the most effective way to implement this using LINQ? Should I use a join instead?
If anybody is able to provide an example or some guidance, I'd appreciate it.
New LINQ Query:
return from objOpenCalls in db.OpenItemss
from objTasks in db.Tasks
.Where(t => (t.Task_ID == objOpenCalls.Parent_Task_ID))
where ((objTasks.Item_ID > 0) && (objTasks.Type_ID > 0) && (objTasks.OwnerTypeItem_ID == user) && (objOpenCalls.CallEnd < DateTime.Now))
orderby objOpenCalls.CallStart descending
select new CallMiniViewModel
{
ID = objOpenCalls.ID,
CallStart = objOpenCalls.CallStart,
Name = objTasks.Task_Title
};
Old SQL Query:
SELECT TOP (100) ta.ID, t.Task_Title, ta.CallStart
FROM OpenItems AS ta INNER JOIN
Tasks AS t ON ta.Parent_Task_ID = t.Task_ID
WHERE
(t.Item_ID > 0) AND (t.[Type_ID] > 0) AND (ta.CallStart > DATEADD(m, -6, GETDATE()))
AND (ta.ID NOT IN (SELECT CallId FROM CallFeedback)) AND (t.OwnerTypeItem_ID = #Username) AND (ta.CallEnd < GETDATE())
ORDER BY ta.CallStart DESC
There are a couple of ways of doing the not in. Below is just a quick sample put in LinqPad as a test.
class MyClass {
public int Id {get;set;}
}
void Main()
{
int[] myItems = new[] { 1, 2, 3, 4, 5, 6 };
IEnumerable<MyClass> classes = new []{
new MyClass { Id = 3 },
new MyClass { Id = 6 },
new MyClass { Id = 8 }
};
var results = from cl in classes
where !myItems.Contains( cl.Id )
select cl;
foreach(var result in results) {
Console.WriteLine( "Class {0}", result.Id);
}
var results2 = from cl in classes
where (
from i in myItems
where i == cl.Id
select i ).Count( ) == 0
select cl;
foreach(var result in results2) {
Console.WriteLine( "Class {0}", result.Id);
}
}
I normally play with code first in LinqPad as it helps me understand any problems, and it will (if you're working with SQL) then show you what SQL the query will generate and you can fine tune a little. Sometimes it takes a little time to get your stuff able to be run in there, but it's worth it with the more complex queries.

How To Project a Line Number Into Linq Query Results

How can I project the row number onto the linq query result set.
Instead of say:
field1, field2, field3
field1, field2, field3
I would like:
1, field1, field2, field3
2, field1, field2, field3
Here is my attempt at this:
public List<ScoreWithRank> GetHighScoresWithRank(string gameId, int count)
{
Guid guid = new Guid(gameId);
using (PPGEntities entities = new PPGEntities())
{
int i = 1;
var query = from s in entities.Scores
where s.Game.Id == guid
orderby s.PlayerScore descending
select new ScoreWithRank()
{
Rank=i++,
PlayerName = s.PlayerName,
PlayerScore = s.PlayerScore
};
return query.ToList<ScoreWithRank>();
}
}
Unfortunately, the "Rank=i++" line throws the following compile-time exception:
"An expression tree may not contain an assignment operator"
Well, the easiest way would be to do it at the client side rather than the database side, and use the overload of Select which provides an index as well:
public List<ScoreWithRank> GetHighScoresWithRank(string gameId, int count)
{
Guid guid = new Guid(gameId);
using (PPGEntities entities = new PPGEntities())
{
var query = from s in entities.Scores
where s.Game.Id == guid
orderby s.PlayerScore descending
select new
{
PlayerName = s.PlayerName,
PlayerScore = s.PlayerScore
};
return query.AsEnumerable() // Client-side from here on
.Select((player, index) => new ScoreWithRank()
{
PlayerName = player.PlayerName,
PlayerScore = player.PlayerScore,
Rank = index + 1;
})
.ToList();
}
}
Ok, that did the trick. Thanks.
Here is my final code...
Server:
public List<Score> GetHighScores(string gameId, int count)
{
Guid guid = new Guid(gameId);
using (PPGEntities entities = new PPGEntities())
{
var query = from s in entities.Scores
where s.Game.Id == guid
orderby s.PlayerScore descending
select s;
return query.ToList<Score>();
}
}
Client:
void hsc_LoadHighScoreCompleted(object sender, GetHighScoreCompletedEventArgs e)
{
ObservableCollection<Score> list = e.Result;
_listBox.ItemsSource = list.Select((player, index) => new ScoreWithRank()
{
PlayerName = player.PlayerName,
PlayerScore = player.PlayerScore,
Rank = index+=1
}).ToList();
}
You could also make just a slight adjustment to your original code to get it working. Word of caution, if you databind or access the object again, the Rank will increment each time. In those cases the top answer is better.
let Rank = i++
and
Rank.ToString()
Full code:
public List<ScoreWithRank> GetHighScoresWithRank(string gameId, int count)
{
Guid guid = new Guid(gameId);
using (PPGEntities entities = new PPGEntities())
{
int i = 1;
var query = from s in entities.Scores
let Rank = i++
where s.Game.Id == guid
orderby s.PlayerScore descending
select new ScoreWithRank()
{
Rank.ToString(),
PlayerName = s.PlayerName,
PlayerScore = s.PlayerScore
};
return query.ToList<ScoreWithRank>();
}
}
This solution worked for me.
http://www.dotnetfunda.com/articles/article1995-rownumber-simulation-in-linq.aspx
.Select((x, index) => new
{
SequentialNumber = index + 1
,FieldFoo = x.FieldFoo
}).ToList();
List<Emp> Lstemp = GetEmpList();
int Srno = 0;
var columns = from t in Lstemp
orderby t.Name
select new {
Row_number=++Srno,
EmpID = t.ID,
Name = t.Name,
City = t.City
};

Resources