Linq with Lambda equivalent of SQL - c#

I am relatively new to entity framework and I've been trying to write a Linq statement with Lambda that includes a simple join. I have three tables: Staff - StaffRole - Role.
I want a staff member in a certain role that satisfies a certain condition. Its very simple to write this in regular SQL:
SELECT *
FROM Staff s
INNER JOIN StaffRole sr ON s.StaffId = sr.StaffId
INNER JOIN Role r ON sr.RoleId = r.RoleId
WHERE r.Description = 'SpecialisedHealthManager'
AND s.PrimaryShm = 0
Now, writing it in a Linq statement has not given me much luck. I'm thinking it would be something like this:
var actingShm = db.Staff.Join(db.StaffRole,
inner => inner.StaffId,
outer => outer.Role,
(outer, inner) => new
{
StaffId = inner.StaffId,
FirstName = inner.Staff.FirstName,
Surname = inner.Staff.Surname,
SamAccountName = inner.Staff.SamAccountName,
RoleId = outer.Description
});
Needless to say, this is not working..

Try using it this way:
var list = from s in Staff
join sr in StaffRole on s.StaffId equals sr.StaffId
join r in Role on sr.RoleId equals r.RoleId
where r.Description == 'SpecialisedHealthManager' && s.PrimaryShm == 0
select new
{
StaffId = s.StaffId,
FirstName = s.Staff.FirstName,
Surname = s.Staff.Surname,
SamAccountName = s.Staff.SamAccountName,
RoleId = r.Description
});

Look here if you realy want to do this with method syntax LINQ:
SO Multiple tables join with lambdas
Also look here:
http://msdn.microsoft.com/pl-pl/library/bb534675(v=vs.110).aspx
for Join extension method syntax. Usage presented in your code is wrong.

You should have your associations setup so you can do this...
var actingShm = from s in db.Staff
from r in s.Roles
where r.Description == "SpecialisedHealthManager"
select new
{
StaffId = s.StaffId,
FirstName = s.FirstName,
Surname = s.Surname,
SamAccountName = s.SamAccountName,
RoleId = r.Description
});
Are you using Entity Framework or Linq2SQL?

Related

Entity Framework Core 2.2 orderby evaluated locally

I have this semi-complex query that counts the most voted user post of the last 7 days:
var fromDate = dateTimeService.Now.Add(-interval);
var votedPosts =
from vote in dbContext.Votes
join post in dbContext.Posts on vote.PostId equals post.PostId
group new {vote.Sign, post.PostId} by post.PostId
into postVotes
select new {
PostId = postVotes.Key,
TotalRating = postVotes.Sum(pv => pv.Sign)
};
var bestPost = (
from post in dbContext.Posts
join votedPost in votedPosts on post.PostId equals votedPost.PostId
join room in dbContext.Rooms on post.RoomId equals room.RoomId
join game in dbContext.Modules on room.ModuleId equals game.ModuleId
where room.RoomAccess == RoomAccessType.Open && post.CreateDate > fromDate
orderby votedPost.TotalRating descending,
post.CreateDate descending
select new BestPost
{
UserId = post.UserId,
ModuleId = game.ModuleId,
ModuleTitle = game.Title,
PostId = post.PostId,
PostText = post.Text,
PostCommentary = post.Commentary,
PostCreateDate = post.CreateDate,
TotalRating = bestPost.TotalRating
}).FirstOrDefault();
What I try to do here is to group user votes by PostId, sum the evaluations of their votes by field Sign (can be -1, 0 or 1), then join it with some additional data like game Id/Title and post texts, filter non-public or too old posts, then order it by rank and then by create date, then map it onto DTO and return the very first result if present.
All the fields here are simple basic types: the Vote.Sign is int, Post.CreateDate is DateTime, all the *Id are Guid and Text/Title/Commentary are string.
I get the warning:
warn: Microsoft.EntityFrameworkCore.Query[20500]
The LINQ expression 'orderby [bestPost].TotalRating desc' could not be translated and will be evaluated locally.
warn: Microsoft.EntityFrameworkCore.Query[20500]
The LINQ expression 'FirstOrDefault()' could not be translated and will be evaluated locally.
If I remove the sort by TotalRating and only leave the CreateDate sorting, it works fine, creates proper LIMIT request. But with TotalRating the query looks like this:
SELECT
t."PostId", t."TotalRating", post."CreateDate" AS "PostCreateDate",
post."UserId", game."ModuleId", game."Title" AS "ModuleTitle",
post."PostId" AS "PostId0", post."Text" AS "PostText",
post."Commentary" AS "PostCommentary"
FROM
"Posts" AS post
INNER JOIN
(SELECT
post0."PostId", SUM(vote."Sign")::INT AS "TotalRating"
FROM
"Votes" AS vote
INNER JOIN
"Posts" AS post0 ON vote."PostId" = post0."PostId"
GROUP BY
post0."PostId") AS t ON post."PostId" = t."PostId"
INNER JOIN
"Rooms" AS room ON post."RoomId" = room."RoomId"
INNER JOIN
"Modules" AS game ON room."ModuleId" = game."ModuleId"
WHERE
(room."RoomAccess" = 0) AND (post."CreateDate" > #__fromDate_0)
And it looks pretty bad to be calculated in dotnet runtime.
I tried to wrap the result in another select from, but it didn't help. I also cannot do the group by on all the columns because then I won't be able to aggregate things like ModuleId because EF Core 2.2 does not support the group.FirstOrDefault things and PostgreSQL does not support max(uuid) (otherwise I could use group.Max(g => g.ModuleId)).
What am I doing wrong?
What happens if you combine the gist of votedPosts into the query so you don't duplicate the join on posts?
var bestPost = (
from post in dbContext.Posts
join vote in dbContext.Votes on post.PostId equals vote.PostId into votej
let voteTotalRating = votej.Sum(pv => pv.Sign)
join room in dbContext.Rooms on post.RoomId equals room.RoomId
join game in dbContext.Modules on room.ModuleId equals game.ModuleId
where room.RoomAccess == RoomAccessType.Open && post.CreateDate > fromDate
orderby voteTotalRating descending,
post.CreateDate descending
select new BestPost {
UserId = post.UserId,
ModuleId = game.ModuleId,
ModuleTitle = game.Title,
PostId = post.PostId,
PostText = post.Text,
PostCommentary = post.Commentary,
PostCreateDate = post.CreateDate,
TotalRating = voteTotalRating
}).FirstOrDefault();

MVC 5 select from 2 model linq expression

For example i have 2 model.
ASpnetUserRoles and ASPnetRoles
i want to select ASPnetRoles.Name,ASPnetROles.ID where ASPnetRoles.ID in ASPnetUserRoles.
i only know how to write in SQL
select * from modalA where modelA.id in(select modelB.id from modelB)
if you enable "Lazy Loading" , you can use this linq query :
using(var db = ..."context"..)
{
var q = db.AspNetUserRoles.where(c=>c.UserID = userIdVal)
.select(z=> new { RoleId = z.RoleId
,userId = z.UserId
,RoleName = z.AspNetRole.RoleName
})
.toList();
}
This is what i did eventually.
IEnumerable<IdentityRole> ro;
ro = (from p in haha join ur in aspNetUser.AspNetRoles on p.Id equals ur.Id select p);
Here a generic sample you must create a class (no EF), and store the result to it.
IQueryable<ResultClass> result=from t1 in db.Table1
join t2 in db.Table2
//Here the relation fields
on t1.IdTable1 equals t2.IdTable2
//Here where conditios and/or orderby
select new ResultClass()
{
Field1=t1.SomeField,
Field2=t2.SomeField,
//all need fields
}
Use the result
result.ToList()

Left outer join off of group by dataset using linq

I am attempting to write the following SQL as a linq query.
SELECT grp.OrganisationId,
grp.OrderCount,
organisations.Name
FROM (select OrganisationId,
count(*) as OrderCount
from orders
where 1 = 1
group by OrganisationId) grp
LEFT OUTER JOIN organisations on grp.OrganisationId = organisations.OrganisationId
WHERE 1 = 1
The where clauses are simplified for the benefit of the example.
I need to do this without the use of navigational properties.
This is my attempt:
var organisationQuery = ClientDBContext.Organisations.Where(x => true);
var orderGrouped = from order in ClientDBContext.Orders.Where(x => true)
group order by order.OrganisationId into grouping
select new { Id = grouping.Key.Value, OrderCount = grouping.Count() };
var orders = from og in orderGrouped
join org in organisationQuery on og.Id equals org.Id
select(x => new OrganisationOrdersReportPoco()
{
OrganisationNameThenCode = org.Name,
TotalOrders = og.OrderCount
});
But I am getting an error of...
Type inference failed in the call to 'Join'
From previous threads, I believe this is because I have "lost the join with order" (but I don't understand why that matters when I am creating a new recordset of Organisation, Count).
Thanks!
I understand you may believe navigation properties are the solution here, but if possible, please can we keep the discussion to the join off of the group by as this is the question I am trying to resolve.
You are mixing lambda and LINQ expressions. Change select to:
select new OrganisationOrdersReportPoco()
{
OrganisationNameThenCode = org.Name,
TotalOrders = og.OrderCount
};
If i understood your model correctly you could try this instead:
var orders = ClientDBContext.Organisations.Select(org => new OrganisationOrdersReportPoco
{
OrganisationNameThenCode = org.Name,
TotalOrders = org.Orders.Count()
}).ToList();

Why does my linq to sql query fail?

I am new to .Net (and stackoverflow) so I am running into a few problems. One vexing problem is this linq query below. Some information about the code. CustomerCart is a separate Model class in my project where the products member is a list of products. If I remove the products from the select new CustomerCart portion it runs fine and the data is present. So I figure it is my syntax. Can I not put a linq statement inside an assignment constructor? Any help would be appreciative. Thank you in advance.
var k = from s in store.Customers
join p in store.ShoppingCarts on s.custId equals p.customerId
select new CustomerCart()
{
FirstName = s.firstName,
LastName = s.lastName,
products = (from j in store.Products
where p.productId == j.productId
select j).ToList(),
CartID = p.cartId,
CustomerID = s.custId,
};
**Edit
Error I receive: The model item passed into the dictionary is of type 'System.Data.Objects.ObjectQuery`1[productShop.Models.CustomerCart]', but this dictionary requires a model item of type 'productShop.Models.CustomerCart'.
Sorry for not placing the error message with my question.
Try this query instead:
var k =
from s in store.Customers
join p in store.ShoppingCarts on s.custId equals p.customerId
join j in store.Products on p.productId equals j.productId into products
select new CustomerCart()
{
FirstName = s.firstName,
LastName = s.lastName,
products,
CartID = p.cartId,
CustomerID = s.custId,
};

Including objects from join in query result

Imagine a simple Entity Framework query with a context generated from the database such as:
var q = from a in context.Accounts
join c in context.Contracts
on a.Id equals c.AccountId
select new CustomAccount {
Id = a.Id,
Name = a.Name,
...
Contracts = //How do I easily populate the related contracts?
};
The query looks for accounts and joins to contracts. The relationship is not enforced in the database (I can't change the schema) so I can't use navigational properties. Is there an easy way that I can populate the related objects?
Just use a group by clause. Something like this (untested):
var q = from a in context.Accounts
join c in context.Contracts on a.Id equals c.AccountId
group a by new { a.Id, a.Name, a.Etc } into g
select new CustomAccount
{
Id = g.Key.Id,
Name = g.Key.Name,
Etc = g.Key.Etc,
Contracts = g.SelectMany(x => x.Contracts)
};
I'm not sure if I understand correctly but you can execute a query that return anonymous type objects
EDIT : you can create a custom class to hold the data member of your result and return in linq result.
EDIT : using group by on account name (e.g)
var q = from a in context.Accounts
join c in context.Contracts
on a.Id equals c.AccountId
group a by new { a.Name } into g
select new AccountContracts
{
AccountName = g.Key.Id, // Account name
Contracts = g.SelectMany(x => x.Contracts)
};

Categories

Resources