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();
In the GetTransfers() method below I have to assign the result of GetAllocations() to a variable outside of my main query otherwise the query fails. Why do I have to do that? Is there a better way?
When the query fails I get this error:
{System.NotSupportedException: LINQ to Entities does not recognize the method 'System.Linq.IQueryable`1[XCBusinessLogic.Presentation.Allocation] GetAllocations()' method, and this method cannot be translated into a store expression.
This query works:
public IQueryable<Transfer> GetTransfers()
{
IQueryable<Allocation> wxyz = GetAllocations();
IQueryable<Transfer> query =
from transfer in Context.XC_TRANSFERS
//let wxyz = GetAllocations()
join trader in Context.MGRS on transfer.TRADER_ID equals trader.MGR_NO
join ssm in Context.SSM_CORES on transfer.SSM_ID equals ssm.SSM_ID
join desk in Context.XC_DESKS on transfer.DESK_ID equals desk.DESK_ID
select new Transfer
{
// snip
_AllocationList = wxyz.Where(x => x.TRANSFER_ID == transfer.TRANSFER_ID)
};
return query;
}
This query fails:
public IQueryable<Transfer> GetTransfers()
{
//IQueryable<Allocation> wxyz = GetAllocations();
IQueryable<Transfer> query =
from transfer in Context.XC_TRANSFERS
let wxyz = GetAllocations()
join trader in Context.MGRS on transfer.TRADER_ID equals trader.MGR_NO
join ssm in Context.SSM_CORES on transfer.SSM_ID equals ssm.SSM_ID
join desk in Context.XC_DESKS on transfer.DESK_ID equals desk.DESK_ID
select new Transfer
{
// snip
_AllocationList = wxyz.Where(x => x.TRANSFER_ID == transfer.TRANSFER_ID)
};
return query;
}
This query fails:
public IQueryable<Transfer> GetTransfers()
{
//IQueryable<Allocation> wxyz = GetAllocations();
IQueryable<Transfer> query =
from transfer in Context.XC_TRANSFERS
//let wxyz = GetAllocations()
join trader in Context.MGRS on transfer.TRADER_ID equals trader.MGR_NO
join ssm in Context.SSM_CORES on transfer.SSM_ID equals ssm.SSM_ID
join desk in Context.XC_DESKS on transfer.DESK_ID equals desk.DESK_ID
select new Transfer
{
// snip
_AllocationList = GetAllocations().Where(x => x.TRANSFER_ID == transfer.TRANSFER_ID)
};
return query;
}
GetAllocations Method:
public IQueryable<Allocation> GetAllocations()
{
IQueryable<Allocation> query =
from alloc in Context.XC_ALLOCATIONS
join acm in Context.ACMS on alloc.ACCT_NO equals acm.ACCT_NO
join b in Context.BUM_DETAILS.Where(x => x.FIRM_NO == 1 && x.CATEGORY_ID == 1937) on acm.ACCT_NO equals b.ACCT_NO into bumDetails
from bumDetail in bumDetails.DefaultIfEmpty()
where acm.FIRM_NO == 1
select new Allocation
{
AccountName = acm.ACCT_NAME
// snip
};
return query;
}
Linq to Entities translate everything in the query from transfer in Context.XC_TRANSFERS ... into SQL. So the only expressions that are allowed inside that query are ones that can easily be translated to SQL.
Linq to Entities cannot figure out how a .NET method like GetAllocations() works. How should it do that? There could be any form of crazy code inside a method. How could it turn that into SQL?
In your case the method actually contains another Linq to Entities query. Maybe you could copy-paste one query into the interior of the other. But I don't think that would improve your code!
So just keep the working solution you have.
You can get around the problem by using join with your method followed by an into
IQueryable<Transfer> query =
from transfer in Context.XC_TRANSFERS
join allocation in GetAllocations() on transfer.TRANSFER_ID equals allocation.TRANSFER_ID into allocationList
join trader in Context.MGRS on transfer.TRADER_ID equals trader.MGR_NO
join ssm in Context.SSM_CORES on transfer.SSM_ID equals ssm.SSM_ID
join desk in Context.XC_DESKS on transfer.DESK_ID equals desk.DESK_ID
select new Transfer
{
// snip
_AllocationList = allocationList
};
I was having a very similar issue and the answer from Aducci did it for me. This was what I was trying to do:
query = from x in query
where GetServicesQuery(db, options).Any(service => /*my criteria*/)
select x;
It was resolved by doing this as Aducci suggested:
query = from x in query
join service in GetServicesQuery(db, localOptions) on x.ID equals service.ID into services
where services.Any(service => /*my criteria*/)
select x;
I am posting this solution because my case was different from above (needing the subquery in the where not the select). If anyone ever stumbles onto this thread with the same issue as me, hopefully this will save them some searching around.
This one was stressing me out since GetServicesQuery has a lot of criteria that I don't want to keep repeating.
I am struggling with how to write the below equivalent as LINQ. Truly I guess I am only struggling with how I represent the INNER JOIN () portion. Is that called a Nested Join? Anonymous Join? I am not even sure. Anyway, big thanks to anyone who can point me true. Even if it is just what this is called so I can BING it properly.
SELECT p.PersonID, p.FirstName, p.MiddleName, p.LastName, cp.EnrollmentID, cp.EnrollmentDate, cp.DisenrollmentDate
FROM vwPersonInfo AS p
INNER JOIN (
SELECT c.ClientID, c.EnrollmentID, c.EnrollmentDate, c.DisenrollmentDate
FROM tblCMOEnrollment AS c
LEFT OUTER JOIN tblWorkerHistory AS wh
ON c.EnrollmentID = wh.EnrollmentID
INNER JOIN tblStaffExtended AS se
ON wh.Worker = se.StaffID
WHERE (wh.EndDate IS NULL OR wh.EndDate >= getdate())
AND wh.Worker = --WorkerID Param Here
) AS cp
ON p.PersonID = cp.ClientID
ORDER BY p.PersonID
just put the inner query in its own variable. (It will be translated into one single SQL expression)
var innerQuery = from x in db.tblCMOEnrollment
where ...
select ...;
var query = from a in vwPersonInfo
join b innerQuery on p.PersonID equals cp.ClientID
select ...;
I think you can do this by writing a second method and joining on that method:
private static IEnumerable<Table> GetData(int joinKey)
{
return (from x in context.TableB.Where(id => id.Key == joinKey select x).AsQueryable();
}
Then you can do your normal query:
var query = from c in context.TableA
join GetData(c.PrimaryKeyValue)
I have a simple query where I want to get the attributes distinct using the value stored in the "Attribute" property. For some reason I always get Distinct operation not supported for this overload error.
var nounTypes = from c in query
join cna in ics.CatalogNounAttributes on c.CatalogId equals cna.CatalogId
join nta in ics.NounTypeAttributes on cna.NounTypeAttributeId equals nta.NounTypeAttributeId
join nt in ics.NounTypes on nta.NounTypeId equals nt.NounTypeId
select new { NounTypeName = nt.Name, Attributes = nt.NounTypeAttributes.Distinct() };
I would also like to get the Count of each Attribute grouped by "Attribute" value where Attribute is a property on NounTypeAttributes table.
I believe you should simply say nt.Distinct.
var nounTypes = from c in query
join cna in ics.CatalogNounAttributes on c.CatalogId equals cna.CatalogId
join nta in ics.NounTypeAttributes on cna.NounTypeAttributeId equals nta.NounTypeAttributeId
join nt in ics.NounTypes on nta.NounTypeId equals nt.NounTypeId
select new { NounTypeName = nt.Name, Attributes = nt.Distinct() };
You do not need to use the Distinct.
Take a look at :
http://msdn.microsoft.com/en-us/vcsharp/aa336761.aspx#distinct2
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}