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

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();

Related

How to Join with a column having Comma Separated Values in Linq

I have one master table Category.
ID | Name
----------
1 | Category1
2 | Category2
3 | Category3
4 | Category4
And Another Table Details have field like
ID | CategoryId | Detail
--------------------
1 | 1,2,3 | Test1
2 | 3,4 | Test2
Here the Category Id stored as comma separated values.
Now i want the result as
ID | CategoryName
----------------
1 | Category1,Category2,Category3
2 | Category3,Category4
AnyOne Have idea ..??
You can use link this:
private static void commaSeperate(List<classname> obj)
{
string delimeter = ",";
Console.WriteLine(obj.Aggregate((i, j) => new classname { Name = (i.Name + delimeter + j.Name) }).Name);
Console.ReadKey();
}
This is just a sample, please modify according to your conditions.
Hope this will help you.
This could be the solution if our retrieve the data into memory first.
var q = from d in Details
from m in Master
select new {Id = d.Id, CategoryName = String.Join(m.Where(i=> d.CategoryId.Split(',').Cast<int32>().Contains(i.Id).Select(i => i.Name).ToArray(), ',')}
You can't join these two tables but I think below query would work for your result:
(from de in datacontextobj.Details
from ca in datacontextobj.Category
where de.CategoryId.Contains(ca.ID)
select de.ID, ca.Name).ToList();

Selecting parents recursively (for ACL/Permissions system)

Important things
Model Category:
[Table("Category")]
public class Category
{
public Category()
{
Children = new HashSet<Category>();
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Column("Parent")]
public int? ParentId { get; set; }
[ForeignKey("ParentId")]
public virtual Category Parent { get; set; }
public virtual ICollection<Category> Children { get; set; }
public string Name { get; set; }
}
Database entries:
---------------------------------
| Id | Parent | Name |
---------------------------------
| 1 | NULL | Top Category |
---------------------------------
| 2 | 1 | Second Category |
---------------------------------
| 3 | 2 | Third Category |
---------------------------------
| 4 | 3 | Fifth Category |
---------------------------------
And now the problem...
How i use a query like...
SELECT "Name" FROM "Category" WHERE "Parent" IS NULL;
with a query like...
SELECT "c"."Id", "c"."Parent", "p"."Name" FROM "Category" "c" LEFT JOIN "Category" "p" ON "p"."Id" = "c"."Parent" WHERE "c"."Name" LIKE '%d%';
I want to select a category with all their parents recursively.
It would be really nice to have a LINQ query for that.
Expected result is something like this:
----------------------------------------------
| "c"."Id" | "c"."Parent" | "p"."Name" |
----------------------------------------------
| 2 | 1 | Top Category |
----------------------------------------------
| 3 | 2 | Second Category |
----------------------------------------------
Or with a group?
-----------------------------------------------------------
| "c"."Id" | "c"."Parent" | GROUP_CONCAT(...) |
-----------------------------------------------------------
| 2, 3 | 1, 2 | Top Category, Second Category |
-----------------------------------------------------------
The category "system" above is a example for a bigger plan...:
( Is there a framework / nuget plugin which allow to use ACL/Permissions/Rights on objects which stored in database?
I have users and groups. Then i have folders and files. Each user or group can have rights for specific folder or file. You can enable heredity for file oder folder-permissions. And with this query i try to read all permissions for a user and a specific folder with all their parents... )
Try this query
var result =
from first in db.Category
join second in db.Category on first.Id equals second.ParentId.GetValueOrDefault(0)
where first.name.Contains(parameter)
select new
{
Id = first.Id ,
ParentId= second.ParentId ?? 0,
ParentName= second.Name
};
I have used second.ParentId.GetValueOrDefault(0) because the join is made on two differents types int and nullable of int

Linq to SQL One to Many query databind to Gridview

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))
};

Get first row of join

I have a linq query that joins two tables (no relation in the actual db)
the relation is:
Companies 1 - n Phones
var miniCompanies =
(from companies in db.Companies
join phones in db.Phones on companies.Id equals phones.CompanyId
select new
{
companies.Name,
phones.Phone,
}).ToList().Distinct();
this returns something like:
----------------------------
company1 | 12345 |
----------------------------
company1 | 23456 |
----------------------------
company2 | 43242 |
----------------------------
company2 | 34234 |
----------------------------
company2 | 65442 |
----------------------------
i need to get only the fisrt in Phones table not everything
how to do that?
Edit:maybe i wasn't clear about what want sorry for that.
i ment:
----------------------------
company1 | 12345 |
----------------------------
company2 | 43242 |
----------------------------
i want the first phone for each company
You can use GroupBy:
var miniCompanies =
(from companies in db.Companies
join phones in db.Phones on companies.Id equals phones.CompanyId
select new
{
companies.Name,
phones.Phone,
}).GroupBy(c=>c.Name).Select(c=>c.FirstOrDefault()).ToArray();
1 You can try with First operator
(from companies in db.Companies
join phones in db.Phones on companies.Id equals phones.CompanyId
select new
{
phones.Phone,
}).First();
Link : http://msdn.microsoft.com/fr-fr/library/vstudio/system.linq.queryable.first.aspx
2 You can also use FirstOrDefault
Link : http://msdn.microsoft.com/fr-fr/library/vstudio/system.linq.queryable.firstordefault.aspx
3 You can also use Take(1);
Link : http://msdn.microsoft.com/fr-fr/library/vstudio/bb300906.aspx
I would use the following:
var miniCompanies =
(from companies in db.Companies
join phones in db.Phones on companies.Id equals phones.CompanyId
select new
{
companies.Name,
phones.Phone,
}).ToList().Distinct().FirstOrDefault();
The 'FirstOrDefault()' method will return the first item in the collection. If the collection contains no elements, the default object will be returned. For nullable types, this will be a 'null' object. This is a good way to prevent your application from failing due to an exception related to an empty collection.

Get distinct columns with order by date

I have these following two tables:
Job Title | PostDate | CompanyId
Assitant | 12/15/10 | 10
Manager | 12/1/10 | 11
Developer | 12/31/10 | 10
Assitant | 12/1/10 | 13
PM | 11/29/10 | 12
CompanyId | Name
10 | Google
11 | Yahoo
12 | Microsoft
13 | Oracle
Now i would like to get 3 different companies with the jobs sorted by post date. The result table would be following:
Job Title | PostDate | CompanyName
Developer | 12/31/10 | Google
Manager | 12/1/10 | Yahoo
Assitant | 12/1/10 | Oracle
How can I achieve that using a linq query? Any help will be appreciated...
I think that would be something like:
var query = from company in db.Companies
join job in db.Jobs on company.CompanyId equals job.CompanyId
group job by company into jobsByCompany
let lastJob = jobsByCompany.OrderByDescending(x => x.PostDate)
.First()
orderby lastJob.PostDate descending
select new
{
JobTitle = lastJob.JobTitle,
PostDate = lastJob.PostDate,
CompanyName = jobsByCompany.Key.Name
};
It's a little odd to do a join and then a group - we could do a GroupJoin instead, but then discard empty options:
var query = from company in db.Companies
join job in db.Jobs on company.CompanyId equals job.CompanyId
into jobsByCompany // Make this a group join
let lastJob = jobsByCompany.OrderByDescending(x => x.PostDate)
.FirstOrDefault()
where lastJob != null
orderby lastJob.PostDate descending
select new
{
JobTitle = lastJob.JobTitle,
PostDate = lastJob.PostDate,
CompanyName = company.Name
};
EDIT: Note that doesn't just take the top three results. Use query = query.Take(3); to just get the first three results.

Categories