I am using linq to get data and insert the data into a IEnumerable>. But sometimes i get duplicate keyvaluepairs and i dont want that because i am doing a ToDictionary(pair => pair.Key, pair => pair.Value) on the IEnumerable.
this is my code:
public Dictionary<Guid, string> GetCitizensWithUnwarrentedAbsence(Guid counselorId, DateTime date)
{
var now = DateTime.Now;
var startInterval = Convert.ToDateTime(date.Date.ToShortDateString());
var endInterval = Convert.ToDateTime(date.Date.ToShortDateString()).AddHours(23).AddMinutes(59);
var list = (from c in _context.CitizenCounselorSet
join p in _context.ActivityLessonParticipantSet on c.Citizen.Id equals p.UserId
where c.CounselorId == counselorId
&& c.StartDate < now
&& (c.EndDate == null || (c.EndDate.HasValue && c.EndDate.Value > now))
&& p.WasUnwarrantedAbsent
&& !p.ActivityLesson.IsDeleted
&& !p.ActivityLesson.IsCancelled
&& p.ActivityLesson.From >= startInterval
&& p.ActivityLesson.From <= endInterval
select new
{
UserId = p.UserId,
UserName = p.User.FullName,
CPR = p.User.UserName
}).ToList().Select(a => new KeyValuePair<Guid, string>(a.UserId, a.UserName + " (" + EncryptionUtility.DecryptString(a.CPR).Insert(6, "-") + ")"));
return list.ToDictionary(pair => pair.Key, pair => pair.Value);
}
How would i make sure not to get duplicates or remove the duplicates after i get the data ??
I would make couple of changes at the end of the query. Let's save space and start in your }).ToList() when your main query logic gets executed and redefine the rest to get your dictionary:
var yourExistingQueryLogic = ...
}).ToList();
var yourUserDictionary = yourExistingQueryLogic
.Select(x=>new {x.UserId, UserName = x.UserName+ " (" + EncryptionUtility.DecryptString(a.CPR).Insert(6, "-") + ")"}) //you can simply build an anonymous object here
.Distinct() //this will eliminate duplicates
.ToDictionary(x=>x.UserId, x=>x.UserName); // DONE!
Related
I want to create query dynamically based on entered number and make it to array to get data from view, so I can create condition to filter it, but my query only works for 3 entered numbers.
var query = from bs in dc.VwResourceAssignments select bs;
var listReqNumber = new[] {123, 456, 789};
My current query is:
if (listReqNumber.Length == 1)
{
query = query.Where(p => p.RequisitionNumber.Contains(listReqNumber[0]));
}
else if (listReqNumber.Length == 2)
{
query = query.Where(p => p.RequisitionNumber.Contains(listReqNumber[0]) ||
p.RequisitionNumber.Contains(listReqNumber[1]));
}
else if (listReqNumber.Length == 3)
{
query = query.Where(p => p.RequisitionNumber.Contains(listReqNumber[0]) ||
p.RequisitionNumber.Contains(listReqNumber[1]) ||
p.RequisitionNumber.Contains(listReqNumber[2]));
}
Is there any way to make it dynamically so I can input requisition number as many as I want?
Let's generalize the problem: if we have an arbitrary listReqNumber array we want to implement
query = query.Where(
p => p.RequisitionNumber.Contains(listReqNumber[0]) ||
p.RequisitionNumber.Contains(listReqNumber[1]) ||
...
p.RequisitionNumber.Contains(listReqNumber[listReqNumber.Length - 1])
);
or - let's get rid of || - we want any item req withing listReqNumber be contained in p.RequisitionNumber
// doesn't compile - just the idea
query = query.Where(p => p.RequisitionNumber.Contains(any req in listReqNumber));
Pity, we can't put any req in listReqNumber but we can swap listReqNumber and p.RequisitionNumber and finally have a valid query:
query = query.Where(p => listReqNumber.Any(req => p.RequisitionNumber.Contains(req)));
How about if you use directly like this:
var result = from p in query where listReqNumber.Contains(p.RequisitionNumber) select p;
var data = query.Where(q =>
q.RequisitionNumber.Any(w => listReqNumber.Contains(w))
);
You can use dynamic linq. Link is here:
https://weblogs.asp.net/scottgu/dynamic-linq-part-1-using-the-linq-dynamic-query-library
then have a function like below:
string GetQuery(int num)
{
return $"p.RequisitionNumber.Contains(listReqNumber[{num}])";
}
Create a query like below in Main:
string testQuery = string.Empty; // or use StringBuilder then convert to string.
int length = 4;
for (int temp = 1; temp <= length; temp ++)
testQuery += testQuery.Length == 0 ? GetQuery(temp - 1) : " || " + GetQuery(temp);
then use dynamic linq like:
var query = northwind.Products
.Where(testQuery).OrderBy("SupplierID");
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() });
I am sorting on multiple criteria using dynamic sorting, here's my code:
public ActionResult WebGrid(int page = 1, int rowsPerPage = 10, string sortCol = "OrderID", string sortDir = "ASC", string sortSecCol = "OrderID", string sortSecDir = "ASC")
{
List<Orders> res;
using (var nwd = new NorthwindEntities())
{
var _res = nwd.Orders
.AsQueryable()
.OrderBy(sortCol + " " + sortDir, sortSecCol + " " + sortSecDir)
.Skip((page - 1) * rowsPerPage)
.Take(rowsPerPage)
.Select(o => new Orders
{
What I am trying to do in here is I want the column OrderID be the secondary sort whenever it is not a primary sort, but it didn't work when I actually selected other column as a primary sort.
In order words, when other column is select as primary sort in descending order, OrderID should also be in descending order, I am not sure what did I missed in my code.
The OrderBy method I used is come from here (MSDN).
The method you are using has the following signature:
public static IQueryable<T> OrderBy<T>(
this IQueryable<T> source,
string ordering,
params object[] values
)
so the sortSecCol + " " + sortSecDir goes to values argument, while the whole ordering is supposed to be provided as comma separated list in the ordering argument.
You can use something like this instead:
var ordering = sortCol + " " + sortDir;
if (sortSecCol != sortCol)
ordering += ", " + sortSecCol + " " + sortSecDir;
...
.OrderBy(ordering)
...
try this:
if (sortCol == "OrderID" && sortDir == "ASC")
{
_res = l.OrderBy(x => x.OrderId).ThenBy(x => x.AnotherField);
}
if (sortCol == "OrderID" && sortDir == "DESC")
{
_res = l.OrderByDescending(x => x.OrderId).ThenByDescending(x => x.AnotherField);
}
if (sortCol == "AnotherField" && sortDir == "ASC")
{
_res = l.OrderBy(x => x.AnotherField).ThenBy(x => x.OrderId);
}
if (sortCol == "AnotherField" && sortDir == "DESC")
{
_res = l.OrderByDescending(x => x.AnotherField).ThenByDescending(x => x.OrderId);
}
_res.Skip((page - 1) * rowsPerPage)
.Take(rowsPerPage)
.Select(o => new Orders
{
I want to write following query in Linq
INSERT INTO INOUTNEW (CODE,INDATE,TIME_DATE1,INOUTFLAG,TIME_FLD1,TIME_FLD2,TIME_FLD3)
SELECT CODE,MIN(INDATE),TIME_DATE1,'I',TIME_FLD1,TIME_FLD2,'31/05/2015' FROM INOUT
WHERE TIME_FLD1='T0003' AND INDATE >= '31/05/2015' AND INDATE <= '31/05/2015'
AND TIME_DATE1='31/05/2015'
GROUP BY CODE,TIME_DATE1,TIME_FLD1,TIME_FLD2
SO I am trying this :-
var data = ctx.tblInOut.Where(m => m.CompanyId == companyId && m.Time_Field1 == item.ShiftCode && m.InDate == StrInStart && m.InDate <= StrInEnd && m.Time_Date1 == InputDate).Select(m =>
new
{
EmployeeId = m.EmployeeId,
InDate = Min(m.InDate),
Time_Date1 = m.Time_Date1,
InOutFlag = m.InOutFlag
}).ToList();
I am stuck in Min Part. How to get Min in Select? And How to add multiple GroupBy in Linq?
Try something like this:
var data = ctx.tblInOut
.Where(m =>
m.CompanyId == companyId &&
m.Time_Field1 == item.ShiftCode &&
m.InDate == StrInStart &&
m.InDate <= StrInEnd &&
m.Time_Date1 == InputDate
)
.GroupBy(m =>
new {
m.Code,
m.Time_Date1,
m.Time_FLD1,
m.Time_FLD2
})
.Select(g =>
new
{
m.Key.Code,
InDate = m.Min(gg => gg.InDate),
m.Key.Time_Date1,
Something = "I",
m.Key.Time_FLD1,
m.Key.Time_FLD2,
SomeDate = "31/05/2015"
}).ToList();
To get Min, you must group first - otherwise it's trying to call Min on a single element.
.Key simply references the key of the group (in this case, a tuple of Code, Date1, Time_FLD1, Time_FLD2)
Can't figure out the logic for Get all records if variable is null else get where officername = officer.
var res = (from h in db.BalanceHistories
where temp.Contains(h.LoanType ?? 0)
&& ((officer != null && h.OfficerName.ToLower() == officer.ToLower()) || ("Get all records"))
group h by new { h.Date.Value.Month, h.Date.Value.Year } into p
select new
{
Month = p.Key.Month,
Year = p.Key.Year,
Count = p.Count(),
Balance = p.Sum(x => x.Balance),
Delinquent = p.Sum(x => x.Delinquent)
}).ToList();
While you can make a compound if statement, this will (usually -- currently always) pass this compound statement on to the database. In many cases, this can cause index misses which if done correctly wouldn't be missed. In non-Microsoft SQL servers, these types of queries are also known to just plain not work (MySQL, DB2). It's better to just write the query correctly in the first place:
var query = db.BalanceHistories
.Where(h=>temp.Contains(h.LoanType ?? 0));
if (officer!=null)
{
// Depending on your database, the ToLower()s here may not be needed.
query=query.Where(h=>h.OfficerName.ToLower() == officer.ToLower()))
}
var res=(from h in query
group h by new { h.Date.Value.Month, h.Date.Value.Year } into p
select new
{
Month = p.Key.Month,
Year = p.Key.Year,
Count = p.Count(),
Balance = p.Sum(x => x.Balance),
Delinquent = p.Sum(x => x.Delinquent)
});
Just swap ("Get all records") with (officer == null), Although i would suggest you to put this condition at first.
Explanation:
If officer == null than respect to short circut law we will return true and the rest of our condition won't matter.
Else means officer != null and thus we want to check if it matches our h.OfficerName data.
End result:
res = (from h in db.BalanceHistories
where temp.Contains(h.LoanType ?? 0)
&& (
officer == null
|| h.OfficerName.ToLower() == officer.ToLower())
)
group h by new { h.Date.Value.Month, h.Date.Value.Year } into p
select new
{
Month = p.Key.Month,
Year = p.Key.Year,
Count = p.Count(),
Balance = p.Sum(x => x.Balance),
Delinquent = p.Sum(x => x.Delinquent)
}).ToList();
Have you tried:
var res = (from h in db.BalanceHistories
where temp.Contains(h.LoanType ?? 0)
&& ((officer == null) || (h.OfficerName.ToLower() == officer.ToLower()))
group h by new { h.Date.Value.Month, h.Date.Value.Year } into p
select new
{
Month = p.Key.Month,
Year = p.Key.Year,
Count = p.Count(),
Balance = p.Sum(x => x.Balance),
Delinquent = p.Sum(x => x.Delinquent)
}).ToList();