How to configure LINQ request - linq

I have 2 tables:
'AllowedDates'
- DayID int PK
- Day datetime
'AllowedTimes'
- TimeID int PK
- DayID int FK
- Hour int
- Minute int
also I have table 'Users':
- ID int PK
- FirstName nvarchar(max)
...
and table 'UserDeniedTimes':
DayID int FK
UserID int FK
Hour int
Minute int
I need to select users, which don't have deny time (record in UserDeniedTimes) for concrete DayID/Hour/Minute
I try to do the following:
var result = from i in _dbContext.Users
where i.UserDeniedTimes.All(
p => (!p.AllowedDate.AllowedTimes.Any(
a1 => a1.DayID == aTime.DayID
&& a1.Hour == aTime.Hour
&& a1.Minute == aTime.Minute
))
)
select new ...
it works correctly, but with one exception. If user has record in UserDeniedTimes for some day, but another time, this user is not selected too. For example, UserDeniedTimes has record:
DayID = 10
UserID = 20
Hour = 14
Minute = 30
this user will be not selected if aTime has the following values:
DayID = 10
Hour = 9
Minute = 30
but will be selected if DayID = 11. Why?
[ADDED]
it works correctly when I limit only by day:
var result = from i in _dbContext.Users
where i.UserDeniedTimes.All(
p => (!p.AllowedDate.AllowedTimes.Any(
a1 => a1.DayID == aTime.DayID
))
)
select new ...
but not works when I write:
var result = from i in _dbContext.Users
where i.UserDeniedTimes.All(
p => (!p.AllowedDate.AllowedTimes.Any(
a1 => a1.Hour == 14
))
)
select new ...
why? What is difference between magic DayID and Hour ?
[ADDED #2]
((time == null) || i.UserDeniedTimes.All(p =>
//p.AllowedDate.AllowedTimes.Any(a1 => a1.DayID != 33) &&
(p.AllowedDate.AllowedTimes.Any(a2 => a2.Hour != 14)
))) &&
does not work
((time == null) || i.UserDeniedTimes.All(p =>
p.AllowedDate.AllowedTimes.Any(a1 => a1.DayID != 33) &&
//(p.AllowedDate.AllowedTimes.Any(a2 => a2.Hour != 14)
))) &&
works
why?

Sometimes it helps to rephrase the problem: if I read you well, you don't want users, that have a deny time with at least one specified DayID/Hour/Minute:
where !i.UserDeniedTimes.Any(
p => (p.AllowedDate.AllowedTimes.Any(
a1 => a1.DayID == aTime.DayID
&& a1.Hour == aTime.Hour
&& a1.Minute == aTime.Minute
))
)
This should select the users you want. If not, please tell in some more words what exactly you
are trying to achieve.

Related

How to group and count missing values using linq

How do I go about counting missing values using linq. Basically I am counting occurrences within a particular month and I want the count to show as zero if there were no entries for that particular month.
However, currently if there are no entries for that month the array skips a month as shown at index 5 below. The reason I don't want this to happen is because I am plotting the results on a chart so the skipped dates are out of sync from index 5 onwards with the actual count.
Below is my linq query
var veterans = _db.Records
.Where(j => j.Requestor == "Veterans" && EF.Functions.DateDiffMonth(j.Request_Date, DateTime.Now) >= 0 && EF.Functions.DateDiffMonth(j.Request_Date, DateTime.Now) <= 24)
.GroupBy(g => new { g.Request_Date.Value.Year, g.Request_Date.Value.Month }).OrderBy(d => d.Key.Year).ThenBy(d => d.Key.Month)
.Select(group => new
{
Dates = group.Key,
Count = group.Count()
});
var veteransCount = veterans.Select(n => n.Count).ToArray();
You can create a helper function to generate the month+year enumeration you need:
public static IEnumerable<(int Year,int Month)> MonthsInYears(int fromYear, int fromMonth, int toYear, int toMonth) {
for (int year = fromYear; year <= toYear; ++year)
for (int month = (year == fromYear ? fromMonth : 1); month <= (year == toYear ? toMonth : 12); ++month)
yield return (year, month);
}
Then using this, you can create an enumeration of the period:
var veterans = _db.Records
.Where(j => j.Requestor == "Veterans" && EF.Functions.DateDiffMonth(j.Request_Date, DateTime.Now) >= 0 && EF.Functions.DateDiffMonth(j.Request_Date, DateTime.Now) <= 24)
.GroupBy(g => new { g.Request_Date.Value.Year, g.Request_Date.Value.Month }).OrderBy(d => d.Key.Year).ThenBy(d => d.Key.Month)
.Select(group => new {
YearMonth = group.Key,
Count = group.Count()
});
var minYearMonth = veterans.Select(v => v.YearMonth).First();
var maxYearMonth = veterans.Select(v => v.YearMonth).Last();
var monthsInYears = MonthsInYears(minYearMonth.Year, minYearMonth.Month, maxYearMonth.Year, maxYearMonth.Month);
Then you can GroupJoin (as a left join) to your database data:
var veteransCount = monthsInYears.GroupJoin(
veterans,
ym => new { ym.Year, ym.Month },
v => v.YearMonth,
(ym, sj) => sj.FirstOrDefault()?.Count ?? 0)
.ToArray();
Alternatively, since this is a specific case, you could create a Dictionary for your source data and lookup each enumeration value:
var veteransMap = veterans.ToDictionary(v => v.YearMonth, v => v.Count);
var veteransCount2 = monthsInYears.Select(ym => veteransMap.TryGetValue(new { ym.Year, ym.Month }, out var count) ? count : 0)
.ToArray();
NOTE: If you want the full beginning and ending years, you could just call the MonthsInYears method with 1 and 12 for the from and to months.

Is there anyway to return int and null with nested conditions?

I want to return a customer which is in this city and buy ItemID = 1, 2 or NULL
List<int?> AcceptableValues = new List<int?>
{
1,2
};
List<Customer> Customers = ListOfCustomer.Where(x => x.CountryTbl.City == 1
&& x.ItemTbl.Any(p => p.ItemID == 1 || p.ItemID ==2 || p.Item is null)
&& x.SellTbl.SellType == 10).ToList();
Is there any way we can use LINQ to filter the customer who only buys itemID in the AcceptableValues ?
I've tried with ItemTbl.SingleOrDefault, or ItemTbl.Where, or AppeptableValues.Contains(m => m.ItemID) but didn't work.
List<Customer> Customers = ListOfCustomer
.Where(c => c.CountryTbl.City == 1
&& c.ItemTbl.Any(i => i.ItemID == null || AcceptableValues.Contains(i.ItemID))
&& c.SellTbl.SellType == 10).ToList();

LINQ - Previous Record

All:
Lets say I have the following table:
RevisionID, Project_ID, Count, Changed_Date
1 2 4 01/01/2016: 01:02:01
2 2 7 01/01/2016: 01:03:01
3 2 8 01/01/2016: 01:04:01
4 2 3 01/01/2016: 01:05:01
5 2 15 01/01/2016: 01:06:01
I am ordering the records based on Updated_Date. A user comes into my site and edits record (RevisionID = 3). For various reasons, using LINQ (with entity framework), I need to get the previous record in the table, which would be RevisionID = 2 so I can perform calculations on "Count". If user went to edit record (RevisionID = 4), I would need to select RevisionID = 3.
I currently have the following:
var x = _db.RevisionHistory
.Where(t => t.Project_ID == input.Project_ID)
.OrderBy(t => t.Changed_Date);
This works in finding the records based on the Project_ID, but how then do I select the record before?
I am trying to do the following, but in one LINQ statement, if possible.
var itemList = from t in _db.RevisionHistory
where t.Project_ID == input.Project_ID
orderby t.Changed_Date
select t;
int h = 0;
foreach (var entry in itemList)
{
if (entry.Revision_ID == input.Revision_ID)
{
break;
}
h = entry.Revision_ID;
}
var previousEntry = _db.RevisionHistory.Find(h);
Here is the correct single query equivalent of your code:
var previousEntry = (
from r1 in db.RevisionHistory
where r1.Project_ID == input.Project_ID && r1.Revision_ID == input.Revision_ID
from r2 in db.RevisionHistory
where r2.Project_ID == r1.Project_ID && r2.Changed_Date < r1.Changed_Date
orderby r2.Changed_Date descending
select r2
).FirstOrDefault();
which generates the following SQL query:
SELECT TOP (1)
[Project1].[Revision_ID] AS [Revision_ID],
[Project1].[Project_ID] AS [Project_ID],
[Project1].[Count] AS [Count],
[Project1].[Changed_Date] AS [Changed_Date]
FROM ( SELECT
[Extent2].[Revision_ID] AS [Revision_ID],
[Extent2].[Project_ID] AS [Project_ID],
[Extent2].[Count] AS [Count],
[Extent2].[Changed_Date] AS [Changed_Date]
FROM [dbo].[RevisionHistories] AS [Extent1]
INNER JOIN [dbo].[RevisionHistories] AS [Extent2] ON [Extent2].[Project_ID] = [Extent1].[Project_ID]
WHERE ([Extent1].[Project_ID] = #p__linq__0) AND ([Extent1].[Revision_ID] = #p__linq__1) AND ([Extent2].[Changed_Date] < [Extent1].[Changed_Date])
) AS [Project1]
ORDER BY [Project1].[Changed_Date] DESC
hope I understood what you want.
Try:
var x = _db.RevisionHistory
.FirstOrDefault(t => t.Project_ID == input.Project_ID && t.Revision_ID == input.Revision_ID -1)
Or, based on what you wrote, but edited:
_db.RevisionHistory
.Where(t => t.Project_ID == input.Project_ID)
.OrderBy(t => t.Changed_Date)
.TakeWhile(t => t.Revision_ID != input.Revision_ID)
.Last()

Linq All with a condition

DB:
I'm trying to bring back data only when All of the ReviewItems meet the condition of ReviewItemStatus==3. This works.
Problem: But then I want to narrow the scope of All to all ReviewItems where ReviewerID==1000
// I want ALL groupAccountLinks only for ReviewerID==1000 and AccountID
// 0) (and thus ReviewItems) for Account Charlie have ReviewItemStatusID==3
var xx = Accounts.Where(acc => acc.GroupAccountLinks.All(gal =>
// do ANY of the (1) associated reviewItems contain ri.ReviewItemStatusID == 3
gal.ReviewItems.Any(ri => ri.ReviewItemStatusID == 3)
// This doesn't work
//&& ri.Review.ReviewerID == 1000
)
&& acc.AccountID == 1002 // Charlie
);
Will be going against EF4.1 Currently testing using Linqpad and LinqToSQL test db.
You have && Condition .So, I think your table not have all AccountId=1002 and All ReviewItemStatusID =3 . So Change && condition to || condition.
The first thing I would do would be to take the && acc.AccountID == 1002 off and make sure that you're getting the entire unfiltered set of Accounts that have a ReviewItem in status 3. If that looks good try doing your filter this way:
var xx = Accounts
.Where(acc => acc.GroupAccountLinks
.All(gal => gal.ReviewItems
.Any(ri => ri.ReviewItemStatusID == 3))
.FirstOrDefault(acc => acc.AccountID == 1002);
Thanks to Jesse, Slauma and RemeshRams which lead me to this:
var xxx = Accounts.Where(
// do All Accounts satisfy condition
u => u.Accounts.All(
// do All GroupAccountLinks (and thus ReviewItems) for Account meet the condition of ReviewItemStatusID==3
acc => acc.GroupAccountLinks.All(
// for ReviewItems where ReviewerID==1000, do they All have ReviewItemStatusID==3
gal => gal.ReviewItems.Where(
ri => ri.Review.ReviewerID == 1000)
.All(
ri => ri.ReviewItemStatusID == 3
)
// Make sure there are some ReviewItems
&& gal.ReviewItems.Any()
)
)
);

Linq join with exists

I'm busy rewriting a system and are using Linq queries to extract data from the database. I am used to plain old TSQL and stored procedures so my Linq skills are not the best.
I have a sql query that I try to rewrite in Linq that contains a join, where clause and IN statements. I do get it right but when I run the sql query I get a different value as from the Linq query. Somewhere I'm missing something and can't find the reason.
Here is the SQL:
select
isnull(Sum(QtyCC) + Sum(QtyEmployee), 0) *
isnull(Sum(UnitPrice), 0)[TotalRValue]
from
tbl_app_KGCWIssueLines a
inner join tbl_app_KGCWIssue b on b.IssueNrLnk = a.IssueNrLnk
where
b.CreationDate >= '2011-02-01' and
a.IssueNrLnk IN (
select
IssueNrLnk
from
tbl_app_KGCWIssue
where
CustomerCode = 'PRO002' and
ISNULL(Tier1,'') = 'PRO002' and
ISNULL(Tier2,'') = 'HAMD01' and
ISNULL(Tier3,'') = '02' and
ISNULL(Tier4,'') = '02001' and
ISNULL(Tier5,'') = 'PTAHQ001' and
ISNULL(Tier6,'') = '035' and
ISNULL(Tier7,'') = '' and
ISNULL(Tier8,'') = '' and
ISNULL(Tier9,'') = '' and
ISNULL(Tier10,'') = ''
)
And here is the Linq:
ctx.ObjectContext.tbl_app_KGCWIssue
.Join(ctx.ObjectContext.tbl_app_KGCWIssueLines,
i => i.IssueNrLnk, l => l.IssueNrLnk, (i, l) => new { i, l })
.Where(o => o.i.CreationDate >= IntervalStartDate)
.Where(p => ctx.ObjectContext.tbl_app_KGCWIssue
.Where(a =>
a.CustomerCode == CustomerCode &&
a.Tier1 == employee.Tier1 &&
a.Tier2 == employee.Tier2 &&
a.Tier3 == employee.Tier3 &&
a.Tier4 == employee.Tier4 &&
a.Tier5 == employee.Tier5 &&
a.Tier6 == employee.Tier6 &&
a.Tier7 == employee.Tier7 &&
a.Tier8 == employee.Tier8 &&
a.Tier9 == employee.Tier9 &&
a.Tier10 == employee.Tier10)
.Select(i => i.IssueNrLnk)
.Contains(p.l.IssueNrLnk))
.Sum(p => p.l.UnitPrice * (p.l.QtyEmployee + p.l.QtyCC));

Resources