Apply multiple order by in linq - c#

I am trying to sort my list by applying two order by. I just want to sort my list by the following orders:
Order 1: DateTime Descending Order
Order 2: Level Ascending Order
Following is my code:
First Method:
var FinalQuery = (from s in data
join p in context.A on s.Id equals p.PId
join PR in context.P on s.RP equals PR.PId
where PR.PId == s.PId
&& PR.IsActive == true
select new List
{
//columns
});
Model.TotalRecord = FinalQuery.Count();
if (Model.IsExportData == false)
{
Model.ListItems = FinalQuery
.Skip(Filter.PageNumber == 0 ? 0 : Model.RecordPerPage * (Filter.PageNumber - 1))
.Take(Model.RecordPerPage).ToList();
Model.ListItems.OrderByDescending(x => x.LoggedDateTime.Value.Date).ThenBy(x => x.Level);
}
else
{
Model.ListItems = FinalQuery.ToList();
}
Second Method:
var FinalQuery = (from s in data
join p in context.A on s.Id equals p.PId
join PR in context.P on s.RP equals PR.PId
where PR.PId == s.PId
&& PR.IsActive == true
select new List
{
//columns
});
Model.TotalRecord = FinalQuery.Count();
if (Model.IsExportData == false)
{
Model.ListItems = FinalQuery
.OrderByDescending(x => x.LoggedDateTime.Value.Date)
.ThenBy(x => x.Level)
.Skip(Filter.PageNumber == 0 ? 0 : Model.RecordPerPage * (Filter.PageNumber - 1))
.Take(Model.RecordPerPage).ToList();
}
else
{
Model.ListItems = FinalQuery.ToList();
}
I am not getting the actual result.

You don't assign the result of your OrderBy Linq-Chain in your first method.
Instead of
Model.ListItems.OrderByDescending(x => x.LoggedDateTime.Value.Date)
.ThenBy(x => x.Level);
you have to assign it to something like
var result = Model.ListItems.OrderByDescending(x => x.LoggedDateTime.Value.Date)
.ThenBy(x => x.Level);
As you can see from the sources:
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
return new OrderedEnumerable<TSource, TKey>(source, keySelector, null, false);
}
The OrderBy returns a new OrderedEnumerable, where the Sort()-Method of a list works on the list itself.
System.Collections.Generic.List.Sort
public void Sort()
{
Sort(0, Count, null);
}

Related

Linq to SQL C#: items between dates

I have 2 tables in my SQL server 2012:
Errors (id, cityID, centerID, date)
InspectionVisits (id, cityID, centerID, datePerformed)
I am trying to get the number of errors between inspection visits to see if there is an improvement in the center with the specific centerID and build a chart.
This is my code so far but I can't find out how I can write the where clause to get the number of errors between these inspection visits:
var errorsPerIV = from e in dc.Errors
where e.cityID == ctid && e.centerID == centerid
group e by e.date.Date into g
join iv in dc.InspectionVisits on g.FirstOrDefault().cityID equals iv.cityID
where iv.centerID == g.FirstOrDefault().centerID
select new
{
Day = g.Key.Day + "/" +
g.Key.Month + "/" +
g.Key.Year,
Errors = g.Count()
};
Sample Case
Something like: 5 errors between Inspection_Visit_1 and Inspection_Visit_2, 2 errors between Inspection_Visit_2 and Inspection_Visit_3 and 1 error between Inspection_Visit_3 and today.
EDIT
Maybe it could work if I show queries per day and mark only the inspection visits in the chart's x axis.
I'm not sure if there is a better way, but you could do something like the following
Suppose you have a class like
public class Summary
{
public DateTime? PreviousInspection;
public DateTime? NextInspection;
public int Errors;
}
then you can get most of the information by having a query like
var errorsPerIV = (from e in dc.Errors
where e.cityID == ctid && e.centreID == centreid
// Find the date of the previous inspection (if any)
let previousInspection = (from i in dc.InspectionVisits where i.cityID == e.cityID && i.centreID == e.centreID && i.datePerformed <= e.date orderby i.datePerformed descending select i.datePerformed).FirstOrDefault()
// Find the date of the next inspection (if any)
let nextInspection = (from i in dc.InspectionVisits where i.cityID == e.cityID && i.centreID == e.centreID && i.datePerformed > e.date orderby i.datePerformed ascending select i.datePerformed).FirstOrDefault()
group e by new { previousInspection , nextInspection } into results
orderby results.Key.previousInspection
select new Summary
{
PreviousInspection = results.Key.previousInspection,
NextInspection = results.Key.nextInspection ,
Errors = results.Count()
})
.ToList();
However if there are no errors between two visits, then these visits will not appear in your list, so you need to find all the visits and see if there missing, ie something like
var inspectionsDates = (from i in InspectionVisits where i.cityID == ctid && i.centreID == centreid orderby i.datePerformed select i.datePerformed).ToList();
for(int i=0; i< inspectionsDates.Count-1; i++)
{
if (!errorsPerIV.Any(a=>a.PreviousInspection == inspectionsDates[i]))
{
errorsPerIV.Add(new Summary() { PreviousInspection = inspectionsDates[i], NextInspection = inspectionsDates[i + 1], Errors = 0});
}
}
I think this would be easiest processed on the client side.
First you want to get the interesting InspectionVisits and order them by date, then convert to an Enumerable to pull them to the client:
var orderedIVs = InspectionVisits.Where(iv => iv.cityID == ctid && iv.centerID == centerid).Select(iv => iv.dateperformed).OrderBy(ivdp => ivdp).AsEnumerable();
Now using an extension method that processes along the Enumerable to compute a running value (it is called Scan because it is modeled after the APL Scan operator, which is like an Aggregate that returns all the intermediate values):
// TKey combineFn(T prev, T cur)
// TKey lastKeyFn(T cur)
public static IEnumerable<TResult> Scan<T, TResult>(this IEnumerable<T> src, Func<T, T, TResult> combineFn, Func<T, TResult> lastKeyFn) {
using (var srce = src.GetEnumerator()) {
if (srce.MoveNext()) {
var prev = srce.Current;
while (srce.MoveNext()) {
yield return combineFn(prev, srce.Current);
prev = srce.Current;
}
yield return lastKeyFn(prev);
}
}
}
You can compute the periods for the inspection visits:
var IVPeriods = orderedIVs.Scan((prev, cur) => new { Begin = prev, End = cur }, cur => new { Begin = cur, End = DateTime.Now });
Finally, with the periods you can count the Errors that occurred between each period:
var errorsPerIV = IVPeriods.Select(ivp => new { Day = ivp.Begin.Date, Count = Errors.Where(e => ivp.Begin <= e.date && e.date <= ivp.End).Count() });
If you want to process this on the server side, you must join the InspectionVisits table to itself so you can create the periods. I have not tested this with SQL server:
var orderedIVs = InspectionVisits.Where(iv => iv.cityID == ctid && iv.centerID == centerid).Select(iv => iv.dateperformed).OrderBy(ivdp => ivdp);
var IVPeriods = (from ivb in orderedIVs
from ive in orderedIVs
where ivb < ive
group ive by ivb into iveg
select new { Begin = iveg.Key, End = iveg.OrderBy(iv => iv).First() })
.Concat((from ivb in orderedIVs orderby ivb descending select new { Begin = ivb, End = DateTime.Now }).Take(1));
var errorsPerIV = IVPeriods.Select(ivp => new { Day = ivp.Begin.Date, Count = Errors.Where(e => ivp.Begin <= e.date && e.date <= ivp.End).Count() });

joining two from with an expression<func<>>

Is it possible to join two from based on a local expression variable?
ex;
var query = from t in context.table1
from a in context.anothertable1.Where(x => t.id == a.id)
select new {a,t};
on line 2, the Where clause .Where(x => t.id == a.id) how would you move it into an expression?
I know i can do this;
Expression<Func<anothertable1, bool>> test = x => x.field1 == 1;
and It would work here;
var query = from t in context.table1
from a in context.anothertable1
.Where(x => t.id == a.id)
.Where(test)
select new {a,t};
and everything work and the sql query generated is as expected.
I can't figure out how to do the same with the other where.
EDIT
a more complex example, i anonymized it so it might not compile
var listOfMinMaxtable1 = (from n in context.table1.Where(table1Filter)
group n by n.table1_Number into grp
select new MinMaxtable1()
{
table1_Id_Max = grp.Max(x => x.table1_Id),
table1_Id_Min = grp.Min(x => x.table1_Id),
table1_Number = grp.Key
});
var listtable2 = (from t in context.table2
group t by t.table2_Id into grp
select new table2()
{
table2 = grp,
table2_Id = grp.Key
});
var query = from MinMax in listOfMinMaxtable1
//inner join **reference 1**
from table3 in context.table3
.Where(x => x.table_Number == MinMax.table_Number)
.Where(noticeMasterFilter) //a working expression<func<>>
//inner join **reference 2**
from Lasttable1 in context.table1
.Where(x => x.table_Id == MinMax.table_Id_Max)
//left join **reference 3**
from Firsttable1 in context.table1
.Where(x => x.table_Id == MinMax.table_Id_Min)
.Where(firstNoticeFilter) //a working expression<func<>>
.DefaultIfEmpty()
//left join **reference 4**
from Lasttable2 in listtable2
.Where(x => x.table_Id == MinMax.table_Id_Max)
.SelectMany(x => x.table2)
.Where(x => x.table2_Id == 123)
.OrderByDescending(x => x.table_Id)
.Take(1)
.DefaultIfEmpty()
if you find //left join reference 3 in the code above
that where clause; .Where(x => x.table_Id == MinMax.table_Id_Min)
might be sometime; .Where(x => x.table_Id == MinMax.table_Id_Max)
I could just copy/paste the whole from and change the where clause while adding noop pattern (an expression that return false and this make entity framework remove the whole thing so it doesn't affect the generated sql/result) with an expression on both from
for reference(this is noise to the question), the noop expression that i'm talking about is;
Expression<Func<table1, bool>> includeFrom= x => false;
and would be used like
//left join **reference 3**
from Firsttable1 in context.table1
.Where(x => x.table_Id == MinMax.table_Id_Min)
.Where(firstNoticeFilter) //a working expression<func<>>
.Where(includeFrom) //<--- this line make it a noop if the expression stay false
.DefaultIfEmpty()
but I don't want to do this if it's possible to make a custom expression that would go into the .Where()
Instead of creating an expression based on one type, you can create a combined type and use that for your where expression.
Two Table Combined Type
public class TwoTableDto
{
public Table1 t { get; set; }
public Table2 a { get; set; }
}
Query without expression
var query = (from t in context.table1
from a in context.anothertable1
select new TwoTableDto { t = t, a = a })
.Where(x => x.t.id == x.a.id);
Expression
Expression<Func<TwoTableDto, bool>> expr = x => x.t.id == x.a.id;
Query with expression
var query = (from t in context.table1
from a in context.anothertable1
select new TwoTableDto { t = t, a = a })
.Where(expr);

I need Linq to sql queries with join and count

i need to count how many singles of one matchmaker in tbluserregistration table.
i use following code,in this code if codition for filtering grid record.
please replay me fastly.
private IQueryable chanims
{
get
{
EntitiesModelMX dbContext = new EntitiesModelMX();
//var query = from p in dbContext.TblShadChanims
// where p.Confirmed == true || p.Status != null
// orderby p.ShadChanimID descending
// select p;
var query = from p in dbContext.TblShadChanims
join u in dbContext.TblUserRegistrations
on p.ShadChanimID equals u.ShadChanID into usercount
orderby p.ShadChanimID descending
select new
{
p,
Singles = usercount.Where(usr => usr.ShadChanID !=0).Count()
};
if (txtFirstNameFilter.Text.Trim().Length > 0)
{
query = (IOrderedQueryable<MainDataMX.TblShadChanim>)query.Where(p => p.p.FirstName.Contains(txtFirstNameFilter.Text.Trim()));
}
if (txtLastNameFilter.Text.Trim().Length > 0)
{
query = (IOrderedQueryable<MainDataMX.TblShadChanim>)query.Where(p => p.LastName.Contains(txtLastNameFilter.Text.Trim()));
}
if (txtPhoneFilter.Text.Trim().Length > 0)
{
query = (IOrderedQueryable<MainDataMX.TblShadChanim>)query.Where(p => p.Phone1.Contains(txtPhoneFilter.Text.Trim()));
}
if (!ddlStatus.SelectedValue.Equals("ALL"))
{
query = (IOrderedQueryable<MainDataMX.TblShadChanim>)query.Where(p => p.Status == (int?)int.Parse(ddlStatus.SelectedValue));
}
if (txtEmailFilter.Text.Trim().Length > 0)
{
query = (IOrderedQueryable<MainDataMX.TblShadChanim>)query.Where(p => p.Email.Contains(txtEmailFilter.Text.Trim()));
}
if (!drpGender.SelectedValue.Equals("ALL"))
{
query = (IOrderedQueryable<MainDataMX.TblShadChanim>)query.Where(p => p.IsMale == (bool?)bool.Parse(drpGender.SelectedValue));
}
return query;
}
}
above code is not working.please reply back.
just use Count with Grouping, please check the following links:
Group and count items
linq with groupby and count
Hope to be in use
:)

How to get a an object in list with min field value using linq? [duplicate]

This question already has answers here:
How to use LINQ to select object with minimum or maximum property value
(20 answers)
Closed 9 years ago.
I want to get an object from the list that has minimum price value.
I did it following way but not getting the item. I do not want to do ordering.
cOrderItem oItem = this.OrderItems
.Where(p => p.CategoryID == catID && p.n_IsOfferApplied == false)
.Min(p => p.OrderItemPrice).FirstOrDefault();
you can try order by and fetch first element
cOrderItem oItem = this.OrderItems.
Where(p => p.CategoryID == catID && p.n_IsOfferApplied == false).
OrderBy(p => p.OrderItemPrice).FirstOrDefault()
this is kind of work around i dont wnat you to use but as you requested you dont want order by than
var data = (from r in OrderItems
where r.CategoryID == catID && r.n_IsOfferApplied == false
group r by r.CategoryID into g
select new { id = g.Key, data= g.Min(a=>a.OrderItemPrice) }).
FirstOrDefault();
var cat = from r in OrderItems
join d in data data on d.id == r.CategoryID
select r;
Note : solution is not tried by me and not recommended to use
You have to match each element against the minimum value that is occuring in the collection.
cOrderItem oItem = this.OrderItems
.Where(p =>
p.CategoryID == catID
&& p.n_IsOfferApplied == false
//Read as: "where the OrderItemPrice value is equal to the lowest occuring value in this.OrderItems
&& p.OrderItemPrice == this.OrderItems.Min(q => q.OrderItemPrice))
.FirstOrDefault();
In case you need multiple items (if they have the same OrderItemPrice), you can do the exact same query, but drop the .FirstorDefault() at the end.
Min doesn't return the item.It returns an integer or double result etc.If you want the item first get the min value then use it with where clause
var minValue = this.OrderItems.Min(p => p.OrderItemPrice);
cOrderItem oItem = this.OrderItems.Where(p => p.CategoryID == catID
&& p.n_IsOfferApplied == false
&& p.OrderItemPrice == minValue)
.FirstOrDefault();
When you use OrderBy it creates new, ordered copy of source sequence in memory (and enumerates source one more time). If you want to avoid this, you can use MoreLINQ MinBy (available from NuGet)
cOrderItem oItem = OrderItems
.Where(p => p.CategoryID == catID && !p.n_IsOfferApplied)
.MinBy(p => p.OrderItemPrice);
NOTE: it will throw exception if there is no matching items, so you can use your own extension to return default value if it is possible that no matches will be found:
public static T MinBy<T, TKey>(
this IEnumerable<T> source, Func<T, TKey> selector)
{
// throw if source or selector is null
using (var iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
return default(T);
var min = iterator.Current;
var minKey = selector(min);
IComparer<TKey> comparer = Comparer<TKey>.Default;
while (iterator.MoveNext())
{
var current = iterator.Current;
var currentKey = selector(current);
if (comparer.Compare(currentKey, minKey) < 0)
{
min = current;
minKey = currentKey;
}
}
return min;
}
}

How to use SingleOrDefault/where in IEnumerable Session property

I'm using VS2010 Entity Framework to store a value in a session. I the use syntax below:
private IEnumerable _TransactionItem2
{
get
{
var msg = HttpContext.Current.Session["SESSION_MESSAGES_NAME"] as IEnumerable;
if (msg == null)
{
msg = new BOMDetailController().GetSearchData(0, 0);
HttpContext.Current.Session["SESSION_MESSAGES_NAME"] = msg;
}
return msg;
}
set
{
Session[_MenuName + "TransactionItem"] = value;
}
}
Session initialization method below:
public IEnumerable GetSearchData(int companyID = 0, long bOMID = 0)
{
var itemBrand = (from p in this.controllerCMN.Context.CmnItemBrands where p.IsDeleted == false select p).AsEnumerable();
var itemColor = (from p in this.controllerCMN.Context.CmnItemColors where p.IsDeleted == false select p).AsEnumerable();
var itemMaster = (from p in this.controllerCMN.Context.CmnItemMasters where p.IsDeleted == false select p).AsEnumerable();
var itemSpecificationMaster = (from p in this.controllerCMN.Context.CmnItemSpecificationMasters where p.IsDeleted == false select p).AsEnumerable();
var itemStyleMaster = (from p in this.controllerCMN.Context.CmnItemStyleMasters where p.IsDeleted == false select p).AsEnumerable();
var uOM = (from p in this.controllerCMN.Context.CmnUOMs where p.IsDeleted == false select p).AsEnumerable();
var bnOMMaster = (from p in this.Context.PlnBOMMasters where p.IsDeleted == false && p.CompanyID == companyID select p).AsEnumerable();
var prdPhase = (from p in this.Context.PlnPrdPhases where p.IsDeleted == false && p.ComapnyID == companyID select p).AsEnumerable();
var prdTask = (from p in this.Context.PlnPrdTasks where p.IsDeleted == false && p.ComapnyID == companyID select p).AsEnumerable();
var bOMDetail = (from p in this.Context.PlnBOMDetails where p.IsDeleted == false && p.CompanyID == companyID select p).AsEnumerable();
var query = from p in bOMDetail
select new
{
BOMDetailRecordID = p.BOMDetailRecordID,
CustomCode = p.CustomCode,
BOMID = p.BOMID,
BOMName = (from q in bnOMMaster where q.BOMID == p.BOMID select q.Description).FirstOrDefault(),
PhaseID = p.PhaseID,
PhaseName = (from q in prdPhase where q.PhaseID == p.PhaseID select q.PhaseName).FirstOrDefault(),
ItemID = p.ItemID,
ItemName = (from q in itemMaster where q.ItemID == p.ItemID select q.ItemName).FirstOrDefault(),
ColorID = p.ColorID,
ColorName = (from q in itemColor where q.ColorID == p.ColorID select q.ColorName).FirstOrDefault(),
StyleID = p.StyleID,
StyleName = (from q in itemStyleMaster where q.StyleID == p.StyleID select q.StyleName).FirstOrDefault(),
ItemSpecificationID = p.ItemSpecificationID,
ItemSpecificationName = (from q in itemSpecificationMaster where q.ItemSpecificationID == p.ItemSpecificationID select q.Description).FirstOrDefault(),
ItemBrandID = p.ItemBrandID,
BrandName = (from q in itemBrand where q.ItemBrandID == p.ItemBrandID select q.ItemBrandName).FirstOrDefault(),
UOMID = p.UOMID,
UOMName = (from q in uOM where q.UOMID == p.UOMID select q.UOMName).FirstOrDefault(),
ItemQty = p.ItemQty,
UnitPrice = p.UnitPrice,
StatusID = p.StatusID,
};
return query.WhereIf(bOMID != 0, p => p.BOMID == bOMID).OrderByDescending(w => w.BOMDetailRecordID);
}
I want to query on the above property TransactionItem2.
1) I want to count how many items on the property TransactionItem2
`TransactionItem2.AsQueryable().Count()`
2) I want to query on this like:
var entity = _ TransactionItem2.SingleOrDefault(item => item.BOMDetailRecordID == childRecordID);
3) I want to use a query
var entity = _ TransactionItem2.Where(item => item.BOMDetailRecordID == childRecordID);
My Extension method is
public static TSource Single<TSource>(this IEnumerable source)
{
return source.Cast<TSource>().Single();
}
public static TSource Single<TSource>(this IEnumerable source, Func<TSource, bool> predicate)
{
return source.Cast<TSource>().Single(predicate);
}
public static TSource SingleOrDefault<TSource>(this IEnumerable source)
{
return source.Cast<TSource>().SingleOrDefault();
}
public static TSource SingleOrDefault<TSource>(this IEnumerable source, Func<TSource, bool> predicate)
{
return source.Cast<TSource>().SingleOrDefault(predicate);
}
Working syntax:
var entity = _TransactionItem2.SingleOrDefault(item => item.BOMDetailRecordID == childRecordID);
The above syntax shows an error message:
“cannot be inferred from the usage. Try specifying the type arguments
explicitly”
The above syntax is not working for the property TransactionItem2. Why is 1,2,3 not working?
You have to specify TSource explicitly when calling your methods:
var entity = _TransactionItem2.SingleOrDefault<EntityClass>(item => item.BOMDetailRecordID == childRecordID);

Categories