Linq to SQL One to Many query databind to Gridview - c#

I've a LTSQL query which returns all users and userroles from SQL Server DB.
For example one user can have many user roles. So in the returned query if you look at a single user row it will have EntitySet(UserRole) for each userRole
The C# LTSQL query which returns all the the relational data is simply
var results = from u in db.Users
Select u;
Gridview.DataSource = results;
Gridview.Databind;
This displays all the data from the user table but not the userroles which is an entity in the results from the query.
If I want to display the user info from users table and all the userroles on a single gridView row, what is the most efficient approach?
The gridView should look something like this:
UserId | UserName | UserRoles |
1 | John Smith | Admin, Accountant | Edit
2 | Dave Jones | Sales, Admin | Edit

Something like this:
var results = from u in db.Users
Select new {
UserID = u.UsrID,
UserName = u.UserName,
UserRoles = string.Join(", ", u.UserRoles.Select(r => r.Name))
};

Related

Return data from linked table

I have 3 tables
table 1 = user table
table 2 = staff table
table 3 = userStaff table
userStaff table is the join table that connects the user and staff table.
user Table
id | firstName | password
---------------------------
2 | UserOne | hashed
staff Table
id | firstName | lastName
---------------------------
3 | sally | jones
userStaff Table
userID | staffID
---------------------------
2 | 3
I have created a method that inputs the user id and it should return back the staff table corresponding to the staffId linked to the userID. In this case, I want to insert the user id of 2 and get back the information of staff id 3, therefore receiving back " id = 3, firstName = Sally, lastName = jones".
This is the code I tried to use
public async Task<UserStaff> GetUserLinkStaff(int id)
{
var staff = await _context.UserStaffs
.FirstOrDefaultAsync(u => u.UserId == id)
.Include(s => s.staffs);
return staff;
}
However, I get an error message which says "Task' does not contain a definition for 'Include' and no accessible extension method 'Include' accepting a first argument of type 'Task' could be found (are you missing a using directive or an assembly reference?) [Schedular.API]csharp(CS1061)"
What is the correct LINQ query I should use?
What is the correct LINQ query I should use?
To solve it, just change your code as follow:
var staff = await _context.UserStuffs.Include(o => o.Stuff)
.FirstOrDefaultAsync(u => u.UserId == id);
When using Include to get the associated data, you should first perform the Include operation after the DbSet, then return the associated data collection and get the specific data by FirstOrDefaultAsync method after Include.
I suggest you read this article to understand the usage of Include in ef core.
Here is the test result:

How to flatten an SQL join with collection using LINQ query?

Let's say I have two tables: Users and UserRoles:
Users:
+----+
| Id |
+----+
| 1 |
| 2 |
| 3 |
+----+
UserRoles:
+--------+----------+
| UserId | Name |
+--------+----------+
| 1 | A |
| 1 | B |
| 2 | A |
| 3 | C |
+--------+----------+
I need to be able to select above data into a list of User objects that looks like this:
class User
{
public int Id { get; set; }
public List<string> Roles { get; set; }
}
The best I could come up with is the following, but it ends up generating a nested select statement, and I don't know if that's going to hurt performance:
List<Device> usersWithRoles = from user in Users
select new Device
{
Id = user.Id,
Roles = (from role in UserRoles where role.UserId == user.Id select role.Name).ToList()
}.ToList();
Is there a better (more performant way) to accomplish this? For example with join, I was thinking something like this, but don't know how to populate roles from the join:
List<Device> usersWithRoles = from user in Users
join user_role in UserRoles on user.Id equals user_role.UserId
select new Device
{
Id = user.Id,
Roles = ??? // how do I populate this
}.ToList();
Thanks.
Use the GroupJoin LINQ to SQL method:
List<Device> usersWithRoles = from user in Users
join user_role in UserRoles on user.Id equals user_role.UserId into roles
select new Device
{
Id = user.Id,
Roles = roles.ToList()
}.ToList();

Making a new DataTable out of two others with same ID using LINQ in C#

I have two data tables that have results from queries made on two different servers. The items from the queries are related by a product ID, I am trying to combine the results into a new data table by joining on the product ids.
The basic structure of the tables are
//Product
product_id | field1 | field2 | field3...
//Inventory
id | fieldA | fieldB | fieldC...
and I want
product_id | field1 | field2 |field3 | fieldA | fieldB |fieldC
when product_id == id
This is pretty trivial to do in SQL but since the two data sets come from different databases on different servers I need to merge the results in my app for users.
The only semi-working solution I have is to get a left and right join from the tables, but I cannot figure out how to merge those results into a new data table. Here is some rough code of how I think it should be done. Any help on this would be appreciated as I am sure there is a much better way to do this.
var query = from S in Product.AsEnumerable()
join I in Inventory.AsEnumerable()
on S.Field<string>("product_id") equals I.Field<string>("id") into COLS
from entry in COLS
select entry;
var query2 = from I in Inventory.AsEnumerable()
join S in Product.AsEnumerable()
on I.Field<string>("id") equals S.Field<string>("product_id") into COLS2
from entry in COLS2
select entry;
var FullJoin = query.Union(query2);
if( FullJoin.Any())
{
Results = FullJoin.CopyToDataTable<DataRow>();
}
return Results;
Try the following code:
var query = from p1 in P1
join p2 in P2
on p1.ID equals p2.ProductID
select new { p1.ID, p1.Field1, p1.Field2, p1.Field3, p2.FieldA, p2.FieldB, p2.FieldC};

MySQL getting followers from a certain user in c#

My database currently exists of 2 tables. One is a user table, and the other is a followers table. Users can follow each other, which means the followers table will have a row that looks like this:
Table: followers
Row: user_id | follower_id
So if I have 3 users:
User 1 | id = 1
User 2 | id = 2
User 3 | id = 3
And user 1 follows user 2 and user 2 follows user 3, the rows in the followers table will look like this:
user_id = 2 | follower_id = 1
user_id = 3 | follower_id = 2
All of this works fine, but how should I create a query when I want to retrieve all followers from this user in c#? My class has a list of ids of people who follow this user. Like this: (This could be a list of user objects, I just need the ids for now)
public class User
{
public int id { get; set; }
public List<int> followers { get; set; }
}
I can retrieve the user from the database, I just can't figure out how to get the followers of this user all in one query. Any help would be appreciated!
The query could look like this:
SELECT u.id, u.name, GROUP_CONCAT(f.follower_id) AS list_of_followers
FROM user u
JOIN followers f ON u.id = f.user_id
WHERE u.id = 2
GROUP BY u.id, u.name
list_of_followers is just a comma-separated list of all followers of user with id=2. You can easily parse the string and populate a List<int> out of it.
Edit:
If you want to also get users with no followers, then you can modify the above query as follows:
SELECT u.id, u.name,
GROUP_CONCAT(f.follower_id) AS list_of_followers,
COUNT(f.follower_id) AS count_of_followers
FROM user u
LEFT JOIN followers f ON u.id = f.user_id
WHERE u.id = 2
GROUP BY u.id, u.name
For users with no followers count_of_followers = 0.

How to write SQL query where one column returns multiple results C#

I have two tables:
users:
ID Name
----------------
1 Bob
2 Dave
3 Mike
Books:
ID UserId BookName
------------------------
1 1 Cat in Hat
2 1 Happy Birthday
3 1 One Fish
4 2 Goldilocks
5 2 Three Crows
6 3 Hitchhikers
UserId is a key showing which books each user owns
In my html I want to create output that looks like this:
Name Books Owned
-------------------------
Bob Cat in Hat, Happy Birthday, One Fish
Dave Goldilocks, Three Crows
Mike Hitchhikers
Currently I am doing this with a loop and nested query:
var queryusers = db.Query("SELECT * FROM users");
foreach (var user in queryusers) {
<span>#user.name</span>
var querybooks = db.Query("SELECT * FROM books WHERE userid = #0", user.id);
foreach (book in querybooks ) {
<span>#book.bookname</span>
}
}
I know that's not good, but I don't know how to do this with JOIN. If I try:
var queryusersandbooks = db.Query("SELECT * FROM users INNER JOIN books ON users.id = books.userid");
My query returns 6 rows, but I only want 3. Is there a way to do this without having a SQL query in the loop?
Thanks
You are looking for GROUP_CONCAT. Try something like this:
SELECT name AS Name, GROUP_CONCAT(books.bookname SEPARATOR ', ') AS Books
FROM books INNER JOIN users ON users.id=books.userid
GROUP BY books.userid;
Keep in mind that GROUP-CONCAT has a 1024 character limit, so if you need more space, set a bigger threshold before executing the SQL, like this:
SET SESSION group_concat_max_len = 1000000;
SELECT name AS Name, GROUP_CONCAT(books.bookname SEPARATOR ', ') AS Books
FROM books INNER JOIN users ON users.id=books.userid
GROUP BY books.userid;
Here is a working fiddle.
Try something like this
SELECT u.name,CONCAT_WS(',',b.bookname) AS user_books FROM users u INNER JOIN books b ON u.id = b.userid GROUP BY u.id
If I were going to do something like this in mysql, I'd group by user and use group_concat to combine the results:
select users.id, users.name, group_concat(bookname) as books
from users inner join books on books.userid = user.id
group by users.id, users.name
You can order and specify the separator if you like, read about group_concat for more details. This breaks down quickly if there might be a lot of books, or if you want each book to be its own column or anything like that, but it's fine in simple cases.
You are on the right track. In your original code the inner foreach would have executed 6 times as well, once for each of the books.
Using the code below (basing off your own code), the query would still return 6 rows, as you have found, but only prints the user name once.
var queryusersandbooks = db.Query("SELECT * FROM users INNER JOIN books ON users.id = books.userid ORDER BY users.name");
int curruser = 0;
foreach (var userandbook in queryusersandbooks)
{
if (userandbook.userid != curruser)
{
<span>#userandbook.name</span>
curruser = userandbook.userid;
}
<span>#userandbook.bookname</span>
}

Categories