C# Linq Select two tables select MAX date from one - c#

I have two tables:
User
-----------------
ID int
Name varchar
UserStatus
-----------------
ID int
UserId int
Status varchar
ActiveDate datetime
EndDate datetime
Using Linq in C# how would I get all information from the User table and all the columns from the user status table by the max active date and the end date being null?
I know the following wont work but it is kind of a rudimentary example of what I am trying to do.
ctx.Users.Include("UserStatus").Where(s => s.UserStatus.Max(t => t.ActiveDate) & s.UserStatus.EndDate != null).OrderBy(s => s.Name)
Thanks
EDIT
Lets say the user table has the following:
1 Billy Bob
2 Bilbo baggins
and the user status table has:
1 1 Active 1/1/2011 null
2 1 Reserved 1/1/2011 3/5/2011
3 2 Active 2/22/2011 null
I would like the following results:
1 Billy Bob Active 1/1/2011
2 Bilbo Baggins Active 2/22/2011

Try doing this in two parts. First, group by and set the max value (using the let keyword), then compare using that value and finally select your object.
var query = (from user in ctx.Users
join userStatus in ctx.UserStatuses on user.ID equals userStatus.User.ID
where userStatus.EndDate == null
group userStatus by new
{
userStatus.User.ID,
userStatus.ActiveDate
} into userGroup
let maxActiveDate = userGroup.Max(x => x.ActiveDate)
from userStatus in userGroup
where userStatus.ActiveDate == maxActiveDate
select new
{
userStatus.User.ID,
userStatus.User.Name,
userStatus.Status,
userStatus.ActiveDate
});

You could use something like this:
public IEnumerable<UserStatus> GetUsers(string name)
{
var query = (from u in db.User
join us in db.UserStatus on u.ID equals us.ID
where u.Name == name
orderby us.ActiveDate descending
select us)
as IEnumerable<UserStatus>;
return query;
}
Or, if you only want the single record w/ the MAX ActiveDate, you would keep the same linq statement, but use .First() at the end, and you'd change your return type as well to be only DateTime, etc.

ctx.Users.Where(u => u.UserStatus.Max(us => us.ActiveDate) && u.UserStatus.EndDate.HasValue())
.OrderBy(u => u.Name)
that seems like what you want

If you're looking for every entry that does NOT have an End Date (thus is active), then you could just use
Where(x => x.UserStatus.EndDate == null)
I don't see the need for the max date in your examples.

Related

Linq - Filter a Result set based on another query

In my DB structure Each EmployeeId can have multiple BusinessId’s
In my 1st query I am getting the BusinessId’s of an employee based his EmployeeId
List<int> businessId = EMPDB.BusinessEmployees.where(a => a.EmployeeId == 5678 && a.Active == true).Select(S => s.BusinessId).ToList();
Out of the above BusinessIds I want to filter and get only those BusinessIds who have non compliance I.e there BusinessId is present in the BusinessCompliance table.
List<int> complianceBusinessIds = EMPDB.BusinessCompliances.where( m=> businessId.Contains(m.BusinessId)).ToList(); —- this is throwing error and I am not able to achieve this
Did you try a join?
from be in EMPDB.BusinessEmployees
join bc in EMPDB.BusinessCompliances on be.BusinessId equals bc.BusinessId
where be.EmployeeID == 5678
select bc;
It should be the better performance way to answer things like this

Complex Linq Query Update as DateTime

There are A and B tables that are related to each other. I want to create a linq query that will update the Status value in the A table if the entire row of relationship lines with the AID column in the B table is equal to or smaller than today's date in the Date field.
For example, according to the table below, the Status values of the rows with ID value 1 (AAA) and 2 (BBB) in Table A will be 1. Its Status value will not change because the line with ID value 3 (CCC) is not smaller than the current date of all the related rows in the B table.
How can I write the most stable and performance linq query?
Today : 2018-7-10
A Table
ID Name Status
1 AAA 0
2 BBB 0
3 CCC 0
B Table
ID AID Date
6 1 2018-5-3
7 2 2018-6-2
8 2 2018-6-4
9 3 2018-10-12
10 3 2018-7-7
Grouping TableB on AID
Selecting the "Max" date in each group.(Each unique AID)
Compares the selected dates with the corresponding Id in Table A.
Sets the Status value to true if the date is less or equal to the current date.
TableB.GroupBy(x => x.AId).Select(group => new { identifier = group.Key, MaxDate = group.Max(m => m.Date) }).ToList().ForEach(y =>
{
if (y.MaxDate <= DateTime.Now.Date)
{
TableA.Where(g => g.Id == y.identifier).First().Status = true;
}
});
This will select AIDs from Table B where Date is samller than now.
we select records from table A where its ID is in List from
previous step
Then we update Status value
A.Where ( a => B.Where( b => b.Date <= DateTime.Now).Select(b => b.AID).Contains(a.ID)).ForEach( a => a.Status = 1 )
/*Fetching those aS Who meet the condition. */
var aList1=(from b in dbset.Bs.Where(x=>x.Date<DateTime.Now)//TimeZone may vary
join a in dbSet.As
on b.AID equals a.ID
select a);
/*Fetching those aS Who don't meet the condition. */
var aList2=(from b in dbset.Bs.Where(x=>x.Date>=DateTime.Now)//TimeZone may vary
join a in dbSet.As
on b.AID equals a.ID
select a);
/*Removing those aS from list1 which occured in list2 */
var aFinalList=(aList1.Except(aList2)).ToList();
/*Updating status */
aFinalList.ForEach(x=>x.Status=1);
aFinalList.SaveChanges();
You can use GroupJoin extension in Lambda to Join the A and B tables then use All extension with your condition (date <= Today or any condition) then update the Status. Something like,
var lstResult = lstA.GroupJoin(lstB, a => new { a.Id }, b => new { Id = b.AId }, (a, b) => new { a, b })
.Select(x =>
{
if (x.b.All(y => y.Date <= DateTime.Now)) //Actual condition here.
{
x.a.Status = true;
return x.a;
}
else return x.a;
});
C# fiddle with sample data.

Get the next and previous sql row by Id and Name, EF?

Assume we have the following data in a SQL Server table (sorted by name) :
Id Name LName
-------------------------
5 Abbas Dayyan
3 Mohammad KD
4 Nima Ahmad
1 Omid Zangene
2 Pedram Ahmadi
we have an Id query string and we wanna get the next and previous row (if exists) from Id.
e.g :
the Id query string is 4, so we wanna get Mohammad KD as previous row and Omid Zangene as next row.
Could you please guide me how can do it with LINQ to Entity Framework.
Edit:
In practice the number of table rows is around 1 million.
Table rows didn't sort by Name by default, wa need to sort them by Name for the result.
How about this?
var result = (from person in db.People
where person.Id == id
let ordered = db.People.OrderBy(p => p.Name)
let reversed = db.People.OrderByDescending(p => p.Name)
let previous = reversed.SkipWhile(p => p.Id != id).Skip(1).FirstOrDefault()
let next = ordered.SkipWhile(p => p.Id != id).Skip(1).FirstOrDefault()
select new { previous, next }).First();
Edit: Took the new specifications into account.
Edit 2: Modified the code to not use LastOrDefault, which doesn't work with LINQ to Entities.
Try this:
int id = 4;
var list = (from x in ctx.Table
where x.id >= id - 1 && x.id <= id + 1
select x).OrderBy(o -> o.name).ToList();
edit: this will return elements with ID 3,4 and 5. I dont know if you are actually ordering the table in the database by name, but if you are i think it would be easier to just use a loop instead of using linq to get the previous and next element.
update: sorted the result by name

LINQ to SQL Grouping and Counting

I have searched for solution, but nothing similar.
My problem is that I want to select data from database, group it by UserID and Count it by Status id
Users
UserID
Name
Appointments
UserID
ClientID
Status
StartDate
Status can be active=1, canceled=2, done=3
This is how I will display results.
Thanks in advance.
In you question you say you want to group on UserId, but in the output you show Name. This query will group on both. You might want to adjust it to your needs.
from u in tblUsers
join a in tblAppointments on u.UserID equals a.UserID
group a by new { u.UserID, u.Name } into g
select new
{
Name = g.Key.Name,
Active = g.Count (x => x.Status == 1),
Canceled = g.Count (x => x.Status == 2),
Done = g.Count (x => x.Status == 3)
}
(this will handle the case if two users have the same name though)
This covers grouping operators. This covers the count.

Simple left join in LINQ

I have a Private-Message Table and a User-Table.
I connect them here:
var messageUsers = (from pm in dc.PrivateMessages
join user in dc.Users
on pm.SenderID equals user.UserID
where !pm.IsDeletedRecipient && pm.RecipientID == id
select new PMInbox
{
SenderUsername = user.Username,
PrivateMessageID = pm.PrivateMessageID,
SenderID = pm.SenderID,
Subject = pm.Subject,
Text = pm.Text,
SenderType = pm.SenderType,
IsDeletedRecipient = pm.IsDeletedRecipient,
IsDeletedSender = pm.IsDeletedSender,
IsRead = pm.IsRead,
Timestamp = pm.TimestampSend
}).ToList();
How to make this in a left-join, so if the userID is not the SenderID (e.g. The Sender is deleted allready), the Username should be empty?
var messageUsers = (
from pm in dc.PrivateMessages
where !pm.IsDeletedRecipient && pm.RecipientID == id
select new PMInbox {
SenderUsername = (
from user in dc.Users
where user.UserID == pm.SenderID
select user.Username
).SingleOrDefault(),
PrivateMessageID = pm.PrivateMessageID,
//...
}
).ToList();
In general, to achieve a Left outer join in LINQ, you should be using SingleOrDefault or possibly FirstOrDefault when you wish to include 1 or 0 rows of the joined "table", and DefaultIfEmpty when you may need to include several lines (in my experience, that's a less common occasion, however). In your specific case, it sounds like UserID is a unique identifier for Users - as such, I translated your left join into a SingleOrDefault call rather than a DefaultIfEmpty call.
Unlike SQL, .NET cannot resolve properties on null values. So if you do something like user.Username and user is null, you'll get a NullReferenceException. You can then either check for null every single time you access user, or you can project (i.e. select) before calling SingleOrDefault or DefaultIfEmpty. After all, an empty sequence of users selects an empty sequence of usernames - which, after SingleOrDefault creates a name or null without the need for any manual null-checking.
Do a group join, then unpack the group by querying it. The DefaultIfEmpty generates a null element when the collection is empty.
var messageUsers = (
from pm in dc.PrivateMessages
join u in dc.Users
on pm.SenderID equals u.UserID
into users
from user in users.DefaultIfEmpty()
where !pm.IsDeletedRecipient && pm.RecipientID == id
...
Also, if you have Associations set up between the tables in the dbml, there should be a relational property from PrivateMessage to User that can be used to express the query more simply. (I'm naming that property Senders, but it might be auto-named Users or Users1. You can rename the property in the dbml.)
var messageUsers = (
from pm in dc.PrivateMessages
where !pm.IsDeletedRecipient && pm.RecipientID == id
from user in pm.Senders.DefaultIfEmpty()
...

Categories