C# linq and sorting - linq

With linq I want to use order by with specific column but I need two switches because i don't know how to use desc or asc in one
public class CustomersRepository : RepositoryBase<Customers>
{
public List<Customers> GetAll(CustomersProperties property, SortEnum sortEnum, int page, int limit)
{
var query = _context.Set<Customers>();
switch (sortEnum)
{
case SortEnum.Ascending:
switch (property)
{
case CustomersProperties.Name:
query = query.OrderBy(x => x.Name);
break;
case CustomersProperties.Surname:
query = query.OrderBy(x => x.Lastname);
break;
default:
throw new ArgumentOutOfRangeException("property");
}
break;
case SortEnum.Descending:
break;
default:
throw new ArgumentOutOfRangeException("sortEnum");
}
return query.Skip(page * limit)
.Take(limit).ToList();
}
}
Is it possible to do without two switch cases?

Be aware that query.OrderBy(x => x.Name); does not do anything since the sorted collection is returned from OrderBy, and you're not capturing that return.
That said, there's not a way to "dynamically" choose the direction in Linq. However, a conditional switch would be a little cleaner. Another option would be to capture the sort expression in a variable:
Expreccion<Func<Customers, string>> propExp;
switch (property)
{
case CustomersProperties.Name:
propExp = ((Customers)x => x.Name)
break;
case CustomersProperties.Surname:
propExp = ((Customers)x => x.Lastname);
break;
default:
throw new ArgumentOutOfRangeException("property");
}
query = sortEnum == SortEnum.Ascending
? query.OrderBy(propExp);
: query.OrderByDescending(propExp);

You could make your own overload, something like this:
public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(
this IQueryable<TSource> source,
Expression<Func<TSource, TKey>> keySelector, SortEnum sort) {
switch (sort) {
case SortEnum.Ascending:
return source.OrderBy(keySelector);
case SortEnum.Descending:
return source.OrderByDescending(keySelector);
default:
throw new ArgumentOutOfRangeException("sort");
}
}
// later on..
query = query.OrderBy(x => x.LastName, sortEnum);

You can prepare sortProperty Expression first and then use it with either OrderBy or OrderByDescending:
public List<Customers> GetAll(CustomersProperties property, SortEnum sortEnum, int page, int limit)
{
var query = _context.Set<Customers>();
Expression<Func<Customers, string>> sortProperty;
switch (property)
{
case CustomersProperties.Name:
sortProperty = x => x.Name;
break;
case CustomersProperties.Surname:
sortProperty = x => x.Lastname;
break;
default:
throw new ArgumentOutOfRangeException("property");
}
switch (sortEnum)
{
case SortEnum.Ascending:
query = query.OrderBy(sortProperty);
break;
case SortEnum.Descending:
query = query.OrderByDescending(sortProperty);
break;
default:
throw new ArgumentOutOfRangeException("sortEnum");
}
return query.Skip(page * limit)
.Take(limit).ToList();
}

Related

Search for particular column and update it in linq EF 4.0

Basically table contain about 50 columns and I have to search for that particular column and update it.
lets say val == 27 then columnName will be Alarm27 and the "onOff" value I have to set in that Alarm27 column.
But question is how do I get this Alarm27 and just update it.
This is what I have tried so far.
public void UpdateAlarm(int val, bool onOff)
{
string alarmName = "Alarm" + val;
using (ESEntities context = new ESEntities())
{
var alarmid = context.OffShoreAlarms.Where(p => p.StationID == (int)TMStation.LQ).Select(p => p.OSAlarmID).FirstOrDefault();
var alarmMonitor = context.OfSAlarmMonitors.Where(p => p.OSAlarmID == alarmid).Select(p => p).FirstOrDefault();
switch (val)
{
case 1:
alarmMonitor.Alarm1 = onOff;
context.saveChanges();
break;
case 2:
alarmMonitor.Alarm2 = onOff;
context.SaveChanges();
break;
.
.
.
.
case 50:
alarmMonitor.alarm50 = onOff;
context.saveChanges();
break;
}
//TODO: context.SaveChanges(); do update operation..
}
}
I have not practically tried this but I think this may work for you.
string alarmName = "Alarm" + val;
context.OffShoreAlarms.Property(alarmName).CurrentValue = true;
You can change current Value of Your entity like this.
For more info check this Link

Update a single column to EF 6.0 using LINQ lambda ? MVC4

I want to save the data when i edit something in my data-table on enter .
On enter control goes to method in controller where actual update performs .
My code till this point:
public string UpdateData(int id, string value, int? rowId, int? columnPosition, int? columnId, string columnName)
{
var Leadsinfo = ser_obj1.Lead_List();
if (columnPosition == 0 && Leadsinfo.Any(c => c.Contact_Name.ToLower().Equals(value.ToLower())))
return "Lead with a name '" + value + "' already exists";
var Lead = Leadsinfo.FirstOrDefault(c => c.Lead_Id == id);
if (Lead == null)
{
return "Lead with an id = " + id + " does not exists";
}
switch (columnPosition)
{
case 0:
Lead.Contact_Name = value;
iWise_NeoEntities ooo = new iWise_NeoEntities();
break;
case 1:
Lead.Contact_Address = value;
break;
case 2:
Lead.Lead_Source = value;
break;
case 3:
Lead.Domain = value;
break;
default:
break;
}
return value;
}
In the above code i mentioned i need to write logic where it should save to DB upon any column editing . using lambda linq is easy i guess , But i dont know even how to start ?
Do i need to write save under each case?
You can create update method in database:
public void UpdateLead(Lead model)
{
var entity = db.Set<Lead>().Find(model.Id);
db.Entry<Lead>(entity).CurrentValues.SetValues(model);
db.SaveChanges();
}
And use it where you need:
switch (columnPosition)
{
case 0: Lead.Contact_Name = value; break;
case 1: Lead.Contact_Address = value; break;
case 2: Lead.Lead_Source = value; break;
case 3: Lead.Domain = value; break;
default: break;
}
iWise_NeoEntities ooo = new iWise_NeoEntities();
ooo.UpdateLead(Lead);

The method 'OrderBy' must be called before the method 'Skip' Exception

I was trying to implement the jQgrid using MvcjQgrid and i got this exception.
System.NotSupportedException was unhandled by user code
Message=The method 'Skip' is only supported for sorted input in LINQ to Entities. The method 'OrderBy' must be called before the method 'Skip'.
Though OrdeyBy is used before Skip method why it is generating the exception? How can it be solved?
I encountered the exception in the controller:
public ActionResult GridDataBasic(GridSettings gridSettings)
{
var jobdescription = sm.GetJobDescription(gridSettings);
var totalJobDescription = sm.CountJobDescription(gridSettings);
var jsonData = new
{
total = totalJobDescription / gridSettings.PageSize + 1,
page = gridSettings.PageIndex,
records = totalJobDescription,
rows = (
from j in jobdescription
select new
{
id = j.JobDescriptionID,
cell = new[]
{
j.JobDescriptionID.ToString(),
j.JobTitle,
j.JobType.JobTypeName,
j.JobPriority.JobPriorityName,
j.JobType.Rate.ToString(),
j.CreationDate.ToShortDateString(),
j.JobDeadline.ToShortDateString(),
}
}).ToArray()
};
return Json(jsonData, JsonRequestBehavior.AllowGet);
}
GetJobDescription Method and CountJobDescription Method
public int CountJobDescription(GridSettings gridSettings)
{
var jobdescription = _dataContext.JobDescriptions.AsQueryable();
if (gridSettings.IsSearch)
{
jobdescription = gridSettings.Where.rules.Aggregate(jobdescription, FilterJobDescription);
}
return jobdescription.Count();
}
public IQueryable<JobDescription> GetJobDescription(GridSettings gridSettings)
{
var jobdescription = orderJobDescription(_dataContext.JobDescriptions.AsQueryable(), gridSettings.SortColumn, gridSettings.SortOrder);
if (gridSettings.IsSearch)
{
jobdescription = gridSettings.Where.rules.Aggregate(jobdescription, FilterJobDescription);
}
return jobdescription.Skip((gridSettings.PageIndex - 1) * gridSettings.PageSize).Take(gridSettings.PageSize);
}
And Finally FilterJobDescription and OrderJobDescription
private static IQueryable<JobDescription> FilterJobDescription(IQueryable<JobDescription> jobdescriptions, Rule rule)
{
if (rule.field == "JobDescriptionID")
{
int result;
if (!int.TryParse(rule.data, out result))
return jobdescriptions;
return jobdescriptions.Where(j => j.JobDescriptionID == Convert.ToInt32(rule.data));
}
// Similar Statements
return jobdescriptions;
}
private IQueryable<JobDescription> orderJobDescription(IQueryable<JobDescription> jobdescriptions, string sortColumn, string sortOrder)
{
if (sortColumn == "JobDescriptionID")
return (sortOrder == "desc") ? jobdescriptions.OrderByDescending(j => j.JobDescriptionID) : jobdescriptions.OrderBy(j => j.JobDescriptionID);
return jobdescriptions;
}
The exception means that you always need a sorted input if you apply Skip, also in the case that the user doesn't click on a column to sort by. I could imagine that no sort column is specified when you open the grid view for the first time before the user can even click on a column header. To catch this case I would suggest to define some default sorting that you want when no other sorting criterion is given, for example:
switch (sortColumn)
{
case "JobDescriptionID":
return (sortOrder == "desc")
? jobdescriptions.OrderByDescending(j => j.JobDescriptionID)
: jobdescriptions.OrderBy(j => j.JobDescriptionID);
case "JobDescriptionTitle":
return (sortOrder == "desc")
? jobdescriptions.OrderByDescending(j => j.JobDescriptionTitle)
: jobdescriptions.OrderBy(j => j.JobDescriptionTitle);
// etc.
default:
return jobdescriptions.OrderBy(j => j.JobDescriptionID);
}
Edit
About your follow-up problems according to your comment: You cannot use ToString() in a LINQ to Entities query. And the next problem would be that you cannot create a string array in a query. I would suggest to load the data from the DB with their native types and then convert afterwards to strings (and to the string array) in memory:
rows = (from j in jobdescription
select new
{
JobDescriptionID = j.JobDescriptionID,
JobTitle = j.JobTitle,
JobTypeName = j.JobType.JobTypeName,
JobPriorityName = j.JobPriority.JobPriorityName,
Rate = j.JobType.Rate,
CreationDate = j.CreationDate,
JobDeadline = j.JobDeadline
})
.AsEnumerable() // DB query runs here, the rest is in memory
.Select(a => new
{
id = a.JobDescriptionID,
cell = new[]
{
a.JobDescriptionID.ToString(),
a.JobTitle,
a.JobTypeName,
a.JobPriorityName,
a.Rate.ToString(),
a.CreationDate.ToShortDateString(),
a.JobDeadline.ToShortDateString()
}
})
.ToArray()
I had the same type of problem after sorting using some code from Adam Anderson that accepted a generic sort string in OrderBy.
After getting this excpetion, i did lots of research and found that very clever fix:
var query = SelectOrders(companyNo, sortExpression);
return Queryable.Skip(query, iStartRow).Take(iPageSize).ToList();
Hope that helps !
SP

foreach or switch messing up linq query?

Trying to build up a query to filter on data in the following manner works OK, returning users filtered by whatever filters are in the FilterNamesAndValues parameter.
GetAllUsersFiltered(..., Dictionary<string,string> FilterNamesAndValues)
{
....
List<DataContracts.IUser> lstUsers = new List<DataContracts.IUser>();
....
var query = from u in lstUsers select u;
string firstName = string.Empty;
FilterNamesAndValues.TryGetValue("FirstName", out firstName);
query = query.Where(u => u.FirstName == firstName);
string company = string.Empty;
FilterNamesAndValues.TryGetValue("Company", out company);
query = query.Where(u => u.CompanyName == company);
....
return query.ToList();
}
The example below however doesn't work and I can't see why:
GetAllUsersFiltered(..., Dictionary<string,string> FilterNamesAndValues)
{
....
List<DataContracts.IUser> lstUsers = new List<DataContracts.IUser>();
....
var query = from u in lstUsers select u;
foreach (KeyValuePair<string, string> kv in FilterNamesAndValues)
{
if (kv.Value != null)
{
switch (kv.Key)
{
case "FirstName":
query = query.Where(u => u.FirstName == kv.Value);
break;
case "Company":
query = query.Where(u => u.CompanyName == kv.Value);
break;
}
}
}
return query.ToList();
}
After the application has hit the first switch case, I can do a query.ToList() and see a row in there. But by the time the execution has gone around the loop to hit the second filter, query.ToList() returns nothing. The query is not filtered successively the way it was in the first example and worse than that, the filter conditions have effectively been lost. There's probably an obvious explanation for this, but right now I can't see it.
The problem is that you're closing over kv in the foreach, but the query is executed using deferred execution. This causes it to close over the wrong value. For details on what's happening, I'd recommend Eric Lippert's post titled "Closing over the loop variable considered harmful".
You can solve this via a temporary:
foreach (KeyValuePair<string, string> kvOriginal in FilterNamesAndValues)
{
// Make a temporary in the correct scope!
KeyValuePair<string, string> kv = kvOriginal;
if (kv.Value != null)
{
switch (kv.Key)
{
case "FirstName":
query = query.Where(u => u.FirstName == kv.Value);
break;
case "Company":
query = query.Where(u => u.CompanyName == kv.Value);
break;
}
}
}

Linq / lambda expressions

Trying something with Linq / Lambda's, but have no idea where to search.
I'm working on some simple sorting in an ASP.net GridView. Here's some sample code:
IQueryable<User> query = (from c in users select c).AsQueryable<User>();
if (isAscending)
{
switch (e.SortExpression)
{
case "Name":
query.OrderBy(c => c.Name);
break;
default:
break;
}
}
else
{
switch (e.SortExpression)
{
case "Name":
query.OrderByDescending(c => c.Name);
break;
default:
break;
}
}
grid.DataSource = query.ToList();
grid.DataBind();
I am, however, unsatisfied with the code as it is very sensitive to errors and requires lots of duplicated code. I'm hoping to solve this using Lambda expressions instead, but I have no idea how. Here's what I would like to go to:
IQueryable<User> query = (from c in users select c).AsQueryable<User>();
var param = null;
switch (e.SortExpression)
{
case "Name":
param = (c => c.Name);
break;
default:
break;
}
if (isAscending)
{
query.OrderBy(param);
}
else
{
query.OrderByDescending(param);
}
grid.DataSource = query.ToList();
grid.DataBind();
Could anyone please help me?
Thanks!
Your code is broken at the moment - you're calling OrderBy/OrderByDescending but not using the result. You need
query = query.OrderBy(param);
etc.
As for conditionally ordering - the problem is that you can't declare the type of param because the type of the ordering key may well vary depending on the sort expression.
As a workaround, you can write your own extension method:
public static IOrderedQueryable<TSource> OrderByWithDirection<TSource, TKey>
(this IQueryable<TSource> source,
Expression<Func<TSource, TKey>> keySelector,
bool ascending)
{
return ascending ? source.OrderBy(keySelector)
: source.OrderByDescending(keySelector);
}
Your code would then become:
IQueryable<User> query = (from c in users select c).AsQueryable<User>();
switch (e.SortExpression)
{
case "Name":
query = query.OrderByWithDirection(c => c.Name, isAscending);
break;
// etc
default:
break;
}
(Why are you calling AsQueryable, by the way?)

Resources