Entity Framework - .Include doesn't load all records - c#

I have this a database with these entities:
public class User
{
public GUID UserId { get; set;}
public IEnumerable<Item> items { get; set;}
}
public class Item
{
public GUID ItemId { get; set;}
public GUID ownerId { get; set;}
public int boughtCount{ get; set;}
}
I need to return the list of users that have items ordered by the items bought the most.
So for example if we have this users:
A: userId: ..
items: 1. itemId: .. | boughtCount: 2
2. itemId: .. | boughtCount: 1
B: userId: ..
items: 1. itemId: .. | boughtCount: 7
C: userId: ..
items: 1. itemId: .. | boughtCount: 3
D: userId: ..
items: none
The query needs to return the users in the following order: B,C,A (D is not returned as he doesn't have any items)
I am using the following query:
users = await _context.Items.OrderByDescending(c => c.BoughtCount)
.Join(_context.Users,
i => i.OwnerId,
u => u.Id,
(i, u) => new { i, u })
.OrderByDescending(x => x.i.BoughtCount)
.Select(x => x.u)
.Distinct()
.Skip(skip)
.Take(take)
.Include(u => u.Items)
.ToListAsync();
This query returns the users in the correct order, but my problem is that for each user it returns maximum of 15 of the items he has, so if for example user A would have 30 items, I will only get his first 15.
What is this 15 items limit?
Am I doing something that cause this limit to come or it's just "hard coded" somewhere?
If so, how do I remove/change the limit?
Note: My sql database is hosted in Azure.
UPDATE:
This is the generated sql query from my linq query:
SELECT [I].[ItemId], [I].[ownerId], [I].[boughtCount]
FROM [Items] AS [I]
INNER JOIN (
SELECT DISTINCT [t0].*
FROM (
SELECT DISTINCT [u].[UserId]
FROM [Items] AS [I]
INNER JOIN [User] AS [u] ON [c].[ownerId] = [u].[UserId]
ORDER BY [u].[UserId]
OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY
) AS [t0]
) AS [u] ON [I].[ownerId] = [u].[UserId]
Thanks!

Related

Merging 2 rows from 2 different lists with same UserId?

If I have 2 different lists,
list1 contains:
UserId: fcec4d6c-c971-4690-90da-be8411dcf251 Col1: 32 Col2: 2 Col3: 0 Col4: 0
UserId: 783ffaa5-03ef-4883-80d2-0500ef489832 Col1: 50 Col2: 4 Col3: 0 Col4: 0
and list2 contains:
UserId: fcec4d6c-c971-4690-90da-be8411dcf251 Col1: 0, Col2: 0, Col3: 45, Col4: 50
If I want to merge these to lists, so that the result would end up being a UserDto list which contains:
UserId: fcec4d6c-c971-4690-90da-be8411dcf251 Col1: 32 Col2: 2 Col3: 34 Col4: 50
UserId: 783ffaa5-03ef-4883-80d2-0500ef489832 Col1: 50 Col2: 4 Col3: 0 Col4: 0
How would one go about doing that?
UserDto just contains something like
[JsonSchema(JsonObjectType.String, Format = "uuid")]
public Guid UserId { get; set; }
public int Col1 { get; set; }
public int Col2 { get; set; }
public int Col3 { get; set; }
public int Col4 { get; set; }
I've tried
list1.AddRange(list2);
list1.GroupBy(e => e.UserId, (key, g) => new { User = key, Columns = g.ToList() }).ToList();
return list1;
list1 returns 2 UserIds, where fcec4d6c-c971-4690-90da-be8411dcf251 now has a Columns list that contains 2 columns, one with col1 + col2 filled and col3 + col4 filled. Please note that these lists will contain a lot of these instances.
Edit 1: I should've made it more clear that I want the sum of these instances in the end. I have now received a proper solution.
Select proper aggregate function:
var result = list1.Concat(list2)
.GroupBy(e => e.UserId)
.Select(g => new User
{
UserId = g.Key,
Col1 = g.Max(x => x.Col1),
Col2 = g.Max(x => x.Col2),
Col3 = g.Max(x => x.Col3),
Col4 = g.Max(x => x.Col4),
})
.ToList();
You didn't specify why you didn't change Col1 and Col2, but changed Col3 and Col4. Do you always want to replace these two columns? Or do you only want to replace them if they have value zero? Or maybe you want to replace all columns with a zero value?
Anyway, first you need to get every list1Element with all zero or more list2Elements that have the same UserId.
Whenever you want to fetch "items with their sub-items", like Schools with their Students, Customers with their Orders, or list1Elements with their list2Elements, consider to use one of the overloads of Enumerable.GroupJoin.
As parameter keySelector use the properties that makes it "its list2Element"
IEnumerable<User> list1 = ...
IEnumerable<User> list2 = ...
var result = list1.GroupJoin(list2
list1User => list1User.UserId, // from every user in list1 take the UserId
list2User => list2User.UserId, // from every user in list2 take the UserId
// parameter resultSelector: from every user in list1, with the zero or more
// users from list2 that have the same UserId, make one new
(list1User, list2UsersWithSameId) => new
{
// decide what you want.
// Replace all 0 properties with the corresponding list2 column?
Col3 = (list1User.Col3 != 0) ? list1User.Col3 :
list2UsersWithSameId.Select(list2User => list2User.Col3)
.FirstOrDefault(),
So if listUser.Col3 not zero, use this Col3 value,
otherwise, from the zero or more list2UsersWithSameId take the Col3 and use the first or default. If there is a list2 user with same Id, you have got its Col3, if not, you get the value zero.
So the value is only replaced if Col3 is zero, and there is at least one list2 with the same Id. If there is none, Col3 remains zero.
Do the same for the other columns that you want to replace.
TODO: you didn't specify that UserId in list2 is unique. If not, it can be that a list item has more than one corresponding list2 item with the same userId. You have to decide which value to use: the first one? the largest one?

How to get related data with linq?

I have a Personnel table and PersonnelDrivingLicense table. There is more than one record in the PersonnelDrivingLicense table related to one Personnel.
I tried to get Personnel and PersonnelDrivingLicense data with linq but I am getting 2 record instead 1.
Here is my linq query:
from p in Personnel
join pdl in PersonnelDrivingLicense on p.Id equals pdl.PersonnelId
select new Personnel
{
Id = p.Id,
PersonnelDrivingLicense = new List<PersonnelDrivingLicense>
{
new PersonnelDrivinLicense
{
Id = pdl.Id,
DrivingLicenseClass = pdl.DrivingLicenseClass
}
}
}
This linq returns below result:
Id: 1,
PersonnelDrivingLicense:
Id: 1,
DrivingLicenseClass: B
Id: 1,
PersonnelDrivingLicense:
Id: 2,
DrivingLicenseClass: C
The correct result should be below:
Id: 1,
PersonnelDrivingLicense:
Id: 1,
DrivingLicenseClass: B
Id: 2,
DrivingLicenseClass: C
How can I get desired result above?
How should I write correct linq query?
Thanks.
You should group the returned Personnel objects by Id. Try this:
from p in Personnel
join pdl in PersonnelDrivingLicense on p.Id equals pdl.PersonnelId
group p by p.Id into g
select new Personnel
{
Id = g.Key,
PersonnelDrivingLicense = g.Select(x => x.PersonnelDrivingLicense).ToList()
}
If you have a one-to-many or a many-to-many relationship, and you want an item with their sub-items, you should use GroupJoin instead of Join.
In you case, you have Personnel and PersonnelDrivingLicenses, probably a one-to-many relation: every Personnel object has zero or more PersonnelDrivingLicences. Every PersonnelDrivingLicence belongs to exactly one Personnel object, namely the Personnel that the foreign key PersonnelId refers to.
Apparently you want a sequence of Personnel objects, each Personnel object with a list of his/her PersonnelDrivingLicenses.
// GroupJoin Personnel with PersonnelDrivingLicenses
var personnelWithTheirDrivingLicenses = myDbContext.Personnel
.GroupJoin(myDbDcontext.PersonnelDrivingLicenses,
// from every personnel object take the Id,
personnel => personnel.Id,
// from every driving license take the PersonnelId
personnelDrivingLicence => personnelDrivingLicence.PersonnelId,
// Take the Personnel, with all his matching driving licenses to make a new:
(personnel, drivingLicenses) => new
{
// for optimal efficiency, Select only the properties you plan to use:
Id = personnel.Id,
Name = personnel.Name,
...
DrivingLicenses = drivingLicenses
.Where(drivingLicense => ...) // only if you don't want all his DrivingLicenses
.Select(drivingLicense => new
{
// again, select only the properties you plan to use:
Id = drivingLicense.Id,
Type = drivingLicense.Type,
...
// not needed, you already know the value:
// PersonnelId = drivingLicense.PersonnelId,
})
.ToList(),
});

How do I use Group By and MAX in Linq to return multiple rows?

Clarified example
I have a database of users that is created by a script that scans through Active Directory. One of the fields it applies is a "ScanDate" field, which indicates when the scan took place. The script scans through multiple Active Directory domains.
GOAL: Obtain an IList from the database that contains the list of users for ALL domains, but where the ScanDate is the MAX(ScanDate) for each domain.
This ensures I get the freshest data for each domain.
A SQL query that appears to work for me:
SELECT *
FROM ADScans a
WHERE a.ScanDate = (SELECT MAX(b.ScanDate) FROM ADScans b WHERE a.Domain = b.Domain) AND Enabled = 1
However, having trouble getting that expressed in LINQ
e.g.:
Category | Date
Cat1 4/4/16
Cat2 | 4/4/16
Cat3 | 4/4/16
Cat1 | 4/3/16
I would expect a list:
Cat1 | 4/4/16
Cat2 | 4/4/16
Cat3 | 4/4/16
Some clarification
I would expect to have multiple rows returned per category - the MAX(Date) will not just give me one. I am looking to obtain ALL of the rows for the MAX(Date) of each category.
Something like this should work:
var result =
list
//Group by Category
.GroupBy(x => x.Category)
//For each Category, select Category and max Date within the Category
//This would create an anonymous object, you could do a "new Entity" instead if you want
.Select(g => new {Category = g.Key, Date = g.Max(x => x.Date)})
.ToList();
I'm an idiot.
from u in this.db.ADScans
where u.ScanDate ==
(from s in this.db.ADScans where u.Domain == s.Domain select s.ScanDate).Max()
&& u.Enabled
select u;
Rather than using Max(), just order the items in the groups and take the top item: since you ordered the items, it's guaranteed to be the highest one:
var mostRecentScanFromEachDomain =
from u in this.db.ADScans
where u.Enabled
group u by u.Domain into g
select g.OrderByDescending(u => u.ScanDate)
.FirstOrDefault();
You can GroupBy by Domain to get the max ScanDate, then keep only the rows with that Date.
For a model like this:
class ADScan
{
public int Domain { get; set; }
public DateTime ScanDate { get; set; }
}
You can get the scans doing this:
var result = scans.GroupBy(s => s.Domain)
.SelectMany(g => g.Where(s => s.ScanDate == g.Max(d => d.ScanDate)));
This produces a collection containing the scans with the max ScanDate for its Domain.

Sorting a list based on another attribute in another list

I have the objects as below:
public class CustomerSequence
{
public string CustomerName { get; set; }
public int Sequence { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Component { get; set; }
}
Let's say I have 2 Lists Object
Customer: CustomerSequence:
Id Name Component CustomerName Sequence
1 AAA AAAAAA AAA 2
2 BBB BBBBBBB BBB 4
3 CCC CCCCC CCC 1
DDD 3
As you can see there is no DDD in List.
I want to sort the List Customer based on the List CustomerSequence
Result is:
Customer:
Id Name Component
3 CCC CCCCC
1 AAA AAAAAA
2 BBB BBBBBBB
Anyone can help me please.
Join both sequences on customer name, then order by sequence value:
from c in customers
join cs in customerSequences
on c.Name equals cs.CustomerName
orderby cs.Sequence
select c;
Lambda syntax is not that beautiful, and it will look like
customers.Join(customerSequences,
c => c.Name, cs => cs.CustomerName, (c,cs) => new { c, cs })
.OrderBy(x => x.cs.Sequence)
.Select(x => x.c)
Internally join uses lookup for second sequence, which is much more effective then linear search with Where.
If it is possible that there is no CustomerSequencs matching customer, or there is more than one match, then use group join:
from c in customers
join cs in customerSequences
on c.Name equals cs.CustomerName into g
orderby g.Select(cs => cs.Sequence).FirstOrDefault()
select c
This query uses 0 form missing sequences, and first matched value if there is more than one sequence for customer.
Try this
Customer.OrderBy(x => CustomerSequence.Where(y => y.CustomerName == x.Name)
.Select(y => y.Sequence)
.FirstOrDefault())
Alternatively you can use a join which would be better if the source was a database
var sorted =
from c in customer
join csj in customerSequence on c.Name equals csj.CustomerName into customerSequenceJoined
from cs in customerSequenceJoined.DefaultIfEmpty()
orderby cs == null ? 0 : cs.Sequence
select c;
The cs == null ? 0 : cs.Sequence deals with the case when there is no matching record in the sequence collection. You could use int.MaxValue if you want these items to appear last.
Use Join
var customers = from cust in Customer
join cust_seq in CustomerSequence
on cust.Name equals cust_seq.CustomerName
orderby cust_seq.Sequence
select cust;
I tend to use a dictionary for this sort of thing.
var customerSequence =
customerSequences
.ToDictionary(x => x.CustomerName, x => x.Sequence);
var sortedCustomers =
customers
.OrderBy(x => customerSequence[x.Name])
.ToList();

Ranking. Linq to sql question

I have a table of orders made by persons:
Orders
{
Guid PersonId,
int Priority,
Guid GoodId
}
Priority is some integer number. For example:
AlenId 1 CarId
DianaId 0 HouseId
AlenId 3 FoodId
DianaId 2 FlowerId
I want to retrieve highest priority orders for each person:
AlenId 1 CarId
DianaId 0 HouseId
In T-SQL I'll use ranking, how can I get the same result in Linq-2-Sql ?
Thank you in advance!
Something like this:
var query = from order in context.Orders
orderby order.Priority
group order by order.PersonId into personOrders
select personOrders.First();
I believe that should work, but I don't know how well-defined it is, in terms of the ordering post-grouping. This would also work, although it's slightly uglier:
var query = from order in context.Orders
group order by order.PersonId into personOrders
select personOrders.OrderBy(order => order.Priority).First();
Or using just dot notation:
var query = context.Orders
.GroupBy(order => order.PersonId)
.Select(group => group.OrderBy(order => order.Priority)
.First());

Categories

Resources