Is possible to get dictionary of values with linq? - c#

I have solution in my mind, but I am convinced that there must be better solution for the problem.
Lets think I have two tables - User (n..n) Group. In my app I wish to get list of Users, each with all his Groups. In SQL I can achieve similar result with:
SELECT TOP (1000) u.Name, g.Name
FROM [User] u
join [GroupUser] gu on u.Id = gu.UserId
join [Group] g on gu.GroupId = g.Id
In the code I am able to write something like this (please, don't judge the correctness, it's just for illustration):
var result = new Dictionary<Model.User, List<Model.Group>>();
List<string> names = request.Names;
var query = this.dbContext.Set<Model.User>().AsQueryable();
query = from user in query
where names.Contains(user.Name, StringComparer.OrdinalIgnoreCase)
select user;
foreach(var user in await query.ToListAsync())
{
var groupQuery =
from g in this.dbContext.Set<Model.Group>()
join gu in this.dbContext.Set<Model.GroupUser>() on g.Id equals gu.GroupId
join u in this.dbContext.Set<Model.User>() on gu.UserId equals u.Id
where u.Name.Equals(user.Name, StringComparison.OrdinalIgnoreCase)
select g;
result.Add(user, await groupQuery.ToListAsync())
}
My question is -- is possible to achieve something like this with single linq query? Or do I really need to enumerate all the Users and fire new query for each of them? This code looks very resource demanding. It is quite simple, only one cycle, but it contains Users.Count+1 query evaluations.
Thanks in advance for hints.

Assuming you are using LINQ to SQL, you can combine the queries:
var requestedUsers = from user in query
where names.Contains(user.Name, StringComparer.OrdinalIgnoreCase)
select user;
var result = (from u in requestedUsers
join gu in this.dbContext.Set<Model.User>() on u.UserId equals gu.Id
join g in this.dbContext.Set<Model.Group>() on gu.UserId equals g.Id into gj
select new { u, gj })
.ToDictionary(ugj => ugj.u, ugj => ugj.gj.ToList());

If you have an entity like the following:
public class User
{
// other properties
public virtual List<Group> Groups { get; set; }
}
You can query both with an Include:
var users = dbContext.Users.Include(u => u.Groups)
.Where(u => names.Contains(u.Name, StringComparer.OrdinalIgnoreCase));

Related

SQL to Linq Lambda

Does anybody know how to convert this on outerjoin on LINQ Lambda?
I wan to achieve this using lambda linq
SELECT * FROM Posts as A LEFT JOIN Reactions as B on A.Id = B.PostId AND #userId = b.userid
Here is my current linq code
return await _dbContext.Posts
.GroupJoin(_dbContext.Reactions,
post => post.Id, reaction => reaction.PostId,
(post, reactions) => new { post, reactions })
.SelectMany(x => x.reactions.DefaultIfEmpty(),
(post, reaction) => new { post.post, reaction })
What you want to accomplish can be done in two different ways in SQL, and those ways can be translated to Linq.
Depending on your scenario (volume of data, indexes, etc) you may want to need one or another
Option A: Join the filtered data
SELECT a.Name, b.*
FROM
tableA
LEFT JOIN tableB on
b.Action='delete' AND a.Id = b.Id
would be translated in LINQ to something similar to:
var query =
from a in db.TableA
join pet in db.TableB.Where(x => x.Action=="delete") on a equals b.TableA into gj
from leftJoined in gj.DefaultIfEmpty()
and using method syntax:
var query = tableA
.GroupJoin(
tableB.Where(x => x.Action == "delete"),
tableA => tableA,
tableB => tableB.tableA,
(tableA, tableBs) => new {tableA, tableBs}
).SelectMany(x => x.tableBs.DefaultIfEmpty())
Option B: Do the join and later filter the data
SELECT a.Name, b.*
FROM
tableA
LEFT JOIN tableB on a.Id = b.Id
WHERE
b.Id = NULL OR b.Action='delete'
would be translated to:
var query =
from a in db.TableA
join pet in db.TableB on a equals b.TableA into gj
from leftJoined in gj.DefaultIfEmpty()
where lefjoined == null || leftjoined.Action == "delete"
A left outer join is a join in which each element of the first collection is returned, regardless of whether it has any correlated elements in the second collection. You can use LINQ to perform a left outer join by calling the DefaultIfEmpty method on the results of a group join.
You can use this approach
Query Syntax:
var query = (from post in Posts
join reaction in Reactions
on post.Id equals reaction.PostId
into reaction
from reaction in reaction.DefaultIfEmpty()
select new
{
post.Id,
//prod.Foo1,
//post.Foo2,
//reaction.Foo3,
//reaction.Foo4,
//you can select other fields too
}).OrderBy(ps => ps.Id);
For more information visit Perform left outer joins
Normally you don't. Flattening out related data like that is simply not necessary in LINQ. Just fetch the data with its natural shape:
_dbContext.Posts.Include(p => p.Reactions)
This returns the Posts and any reactions, without having to repeat the Post data for each Reaction, or having nulls for Posts without Reactions.

How Do I perform an outer Join in C# using three tables

I want to get the list of roles and the total number of users in each of the roles. So far, this works:
var result = (from r in _context.UserRoles
join u in _context.Users on r.UserId equals u.Id
group new { r, u } by new { r.RoleId } into grp
select new UserRoleModel { RoleId = (int)grp.FirstOrDefault().r.RoleId, NoOfUsers = grp.Count() }).ToList();
But it does not display roles that don't have any users in it. I have 12 roles, 7 of them have been assigned to at least one user, while the remaining 5 have not. I want to display all the roles with the number of users assigned to them, but if the roles have no users assigned to them (like those 5) I want it to return 0 as the number of users. Thanks
You want to apply left join and you should join them using DefaultIfEmpty.
Also, the entities which is joined in the query is wrong. Because, you should join junction entity (UserRole) with Role entity instead of User if you want to retrieve the grouped roles and count of opposite users. Otherwise, you never know which role doesn't have user.
var result = (from r in _context.Roles
join ur in _context.UserRoles on r.Id equals ur.RoleId into ps
from ur in ps.DefaultIfEmpty()
group new { r,ur } by new { r.Id } into grp
select new UserRoleModel { RoleId = (int)grp.Key.Id,
NoOfUsers = grp.Count(t => t.ur != null) }).ToList();
Also, I suggest you to change
(int)grp.FirstOrDefault().r.RoleId
to
(int)grp.Key.RoleId
The query is grouped by RoleId already.
Also, I have additional note. I strongly suggest you to define navigation properties and use them. If you would have them in the entities, the query would be simplier;
var result = _context.Roles.Select(x => new UserRoleModel
{
RoleId = x.Id,
NoOfUsers = x.UserRoles?.Count() ?? 0
});
Try this,
var result = (from r in _context.UserRoles
join u in _context.Users on r.UserId equals u.Id into temp
from uu in temp.DefaultIfEmpty()
group new { r, u } by new { r.RoleId } into grp
select new UserRoleModel { RoleId = (int)grp.FirstOrDefault().r.RoleId, NoOfUsers = grp.Count() }).ToList();

C# - Join Syntax with two tables [duplicate]

I'm writing a LINQ to SQL statement, and I'm after the standard syntax for a normal inner join with an ON clause in C#.
How do you represent the following in LINQ to SQL:
select DealerContact.*
from Dealer
inner join DealerContact on Dealer.DealerID = DealerContact.DealerID
It goes something like:
from t1 in db.Table1
join t2 in db.Table2 on t1.field equals t2.field
select new { t1.field2, t2.field3}
It would be nice to have sensible names and fields for your tables for a better example. :)
Update
I think for your query this might be more appropriate:
var dealercontacts = from contact in DealerContact
join dealer in Dealer on contact.DealerId equals dealer.ID
select contact;
Since you are looking for the contacts, not the dealers.
And because I prefer the expression chain syntax, here is how you do it with that:
var dealerContracts = DealerContact.Join(Dealer,
contact => contact.DealerId,
dealer => dealer.DealerId,
(contact, dealer) => contact);
To extend the expression chain syntax answer by Clever Human:
If you wanted to do things (like filter or select) on fields from both tables being joined together -- instead on just one of those two tables -- you could create a new object in the lambda expression of the final parameter to the Join method incorporating both of those tables, for example:
var dealerInfo = DealerContact.Join(Dealer,
dc => dc.DealerId,
d => d.DealerId,
(dc, d) => new { DealerContact = dc, Dealer = d })
.Where(dc_d => dc_d.Dealer.FirstName == "Glenn"
&& dc_d.DealerContact.City == "Chicago")
.Select(dc_d => new {
dc_d.Dealer.DealerID,
dc_d.Dealer.FirstName,
dc_d.Dealer.LastName,
dc_d.DealerContact.City,
dc_d.DealerContact.State });
The interesting part is the lambda expression in line 4 of that example:
(dc, d) => new { DealerContact = dc, Dealer = d }
...where we construct a new anonymous-type object which has as properties the DealerContact and Dealer records, along with all of their fields.
We can then use fields from those records as we filter and select the results, as demonstrated by the remainder of the example, which uses dc_d as a name for the anonymous object we built which has both the DealerContact and Dealer records as its properties.
var results = from c in db.Companies
join cn in db.Countries on c.CountryID equals cn.ID
join ct in db.Cities on c.CityID equals ct.ID
join sect in db.Sectors on c.SectorID equals sect.ID
where (c.CountryID == cn.ID) && (c.CityID == ct.ID) && (c.SectorID == company.SectorID) && (company.SectorID == sect.ID)
select new { country = cn.Name, city = ct.Name, c.ID, c.Name, c.Address1, c.Address2, c.Address3, c.CountryID, c.CityID, c.Region, c.PostCode, c.Telephone, c.Website, c.SectorID, Status = (ContactStatus)c.StatusID, sector = sect.Name };
return results.ToList();
You create a foreign key, and LINQ-to-SQL creates navigation properties for you. Each Dealer will then have a collection of DealerContacts which you can select, filter, and manipulate.
from contact in dealer.DealerContacts select contact
or
context.Dealers.Select(d => d.DealerContacts)
If you're not using navigation properties, you're missing out one of the main benefits on LINQ-to-SQL - the part that maps the object graph.
Use Linq Join operator:
var q = from d in Dealer
join dc in DealerConact on d.DealerID equals dc.DealerID
select dc;
basically LINQ join operator provides no benefit for SQL. I.e. the following query
var r = from dealer in db.Dealers
from contact in db.DealerContact
where dealer.DealerID == contact.DealerID
select dealerContact;
will result in INNER JOIN in SQL
join is useful for IEnumerable<> because it is more efficient:
from contact in db.DealerContact
clause would be re-executed for every dealer
But for IQueryable<> it is not the case. Also join is less flexible.
Actually, often it is better not to join, in linq that is. When there are navigation properties a very succinct way to write your linq statement is:
from dealer in db.Dealers
from contact in dealer.DealerContacts
select new { whatever you need from dealer or contact }
It translates to a where clause:
SELECT <columns>
FROM Dealer, DealerContact
WHERE Dealer.DealerID = DealerContact.DealerID
Inner join two tables in linq C#
var result = from q1 in table1
join q2 in table2
on q1.Customer_Id equals q2.Customer_Id
select new { q1.Name, q1.Mobile, q2.Purchase, q2.Dates }
Use LINQ joins to perform Inner Join.
var employeeInfo = from emp in db.Employees
join dept in db.Departments
on emp.Eid equals dept.Eid
select new
{
emp.Ename,
dept.Dname,
emp.Elocation
};
Try this :
var data =(from t1 in dataContext.Table1 join
t2 in dataContext.Table2 on
t1.field equals t2.field
orderby t1.Id select t1).ToList();
OperationDataContext odDataContext = new OperationDataContext();
var studentInfo = from student in odDataContext.STUDENTs
join course in odDataContext.COURSEs
on student.course_id equals course.course_id
select new { student.student_name, student.student_city, course.course_name, course.course_desc };
Where student and course tables have primary key and foreign key relationship
try instead this,
var dealer = from d in Dealer
join dc in DealerContact on d.DealerID equals dc.DealerID
select d;
var Data= (from dealer in Dealer join dealercontact in DealerContact on dealer.ID equals dealercontact.DealerID
select new{
dealer.Id,
dealercontact.ContactName
}).ToList();
var data=(from t in db.your tableName(t1)
join s in db.yourothertablename(t2) on t1.fieldname equals t2.feldname
(where condtion)).tolist();
var list = (from u in db.Users join c in db.Customers on u.CustomerId equals c.CustomerId where u.Username == username
select new {u.UserId, u.CustomerId, u.ClientId, u.RoleId, u.Username, u.Email, u.Password, u.Salt, u.Hint1, u.Hint2, u.Hint3, u.Locked, u.Active,c.ProfilePic}).First();
Write table names you want, and initialize the select to get the result of fields.
from d1 in DealerContrac join d2 in DealerContrac on d1.dealearid equals d2.dealerid select new {dealercontract.*}
One Best example
Table Names : TBL_Emp and TBL_Dep
var result = from emp in TBL_Emp join dep in TBL_Dep on emp.id=dep.id
select new
{
emp.Name;
emp.Address
dep.Department_Name
}
foreach(char item in result)
{ // to do}

Convert SQL to LINQ, with multiple joins and a predicate

I am struggling with trying to write a LINQ statement in LINQ or method syntax, based on the following SQL.
SELECT vr.*
FROM VisualReport vr
JOIN VisualReportRoleList vrl ON vr.VisualReportSK = vrl.VisualReportSK
JOIN Role r ON vrl.RoleSK = r.RoleID
JOIN UserRoleList url ON r.RoleID = url.RoleID
JOIN Users u ON url.UserID = url.UserID
WHERE u.ID = 1
I have tried many things, but without luck. I was going to post some of it here, but it just made this post really messy to read.
Can anyone here help me with this?
Join pattern works like this
Param 1. is the list to join on,
Param 2. key on the left (what your calling join on),
Param 3. key on the right (what your joining to),
Param 4. is the results selector how you want to select the results.
db.VisualReposts.Join(db.VisualRepostRoleLists,
vr => vr.VisualReportSK,
vrl => vrl.VisualReportSK
(vr, vrl) => new { vr, vrl}).Join(db.Roles,
vrj => vrj.vrl.RoleSK,
r => r.RoleID,
vrj, r => new {vr = vrj.vr,
vrl = vrj.vrl,
role = r} )
//follow the same patter for the rest of the joins.
// Also add your where clause, it might be a better idea to start
//from the users table so you can filter first
But if everything is properly foreign keyed, you don't need to do a join you can just access the ICollections. If you can or have them foreign keyed you can access them like this
var user = db.Users.Where(u => u.id == 1);
//everything should have an icollection now so you can access via
//user.UserRoleList.Roles ect or what ever you need
A simple one to one conversion. LinqPad should convert this to equivalent of your query.
var result = (from vr in VisualReport
join vrl in VisualReportRoleList on vr.VisualReportSK equals vrl.VisualReportSK
join r in Role on vrl.RoleSK equals r.RoleID
join url in UserRoleList on vrl.RoleSK equals url.RoleID
join u in Users on url.UserID equals u.UserID
where u.ID == 1
select vr).ToList();

Linq to SQL - How to compare against a collection in the where clause?

I'd like to compare against an IEnumerable collection in my where clause. Do I need to manually loop through the collection to pull out the column I want to compare against, or is there a generic way to handle this?
I want something like this:
public IEnumerable<Cookie> GetCookiesForUsers(IEnumerable<User> Users)
{
var cookies = from c in db.Cookies
join uc in db.UserCookies on c.CookieID equals uc.CookieID
join u in db.Users on uc.UserID equals u.UserID
where u.UserID.Equals(Users.UserID)
select c;
return cookies.ToList();
}
I'm used to using the lambda Linq to SQL syntax, but I decided to try the SQLesque syntax since I was using joins this time.
What is a good way to do this?
Try this: http://blog.wekeroad.com/2008/02/27/creating-in-queries-with-linq-to-sql/
public IEnumerable<Cookie> GetCookiesForUsers(IEnumerable<User> Users)
{
var cookies = from c in db.Cookies
join uc in db.UserCookies on c.CookieID equals uc.CookieID
join u in db.Users on uc.UserID equals u.UserID
where Users.Contains(u.UserID)
select c;
return cookies.ToList();
}
Or perhaps:
public IEnumerable<Cookie> GetCookiesForUsers(IEnumerable<User> Users)
{
var cookies = from c in db.Cookies
join uc in db.UserCookies on c.CookieID equals uc.CookieID
join u in db.Users on uc.UserID equals u.UserID
where Users.Select(x => x.UserID).Contains(u.UserID)
select c;
return cookies.ToList();
}

Categories