After running the query below and hovering over "usersToWork" when debugging, I can view all of the properties of the single entry that I get returned to me in addition to the other tables that have relations to this value. What I need to display to the user is the "Lines.Id" (Lines being the table and Id being the column in the Lines table) value, however that value gets lost from the SelectMany() statements. Is there anyway to select that "Lines.Id" value to include in the final value that I get from all of my joins? In the code below, I commented out what I want but I can't place that there otherwise I get error on the first SelectMany statement saying 'int' does not contain a definition for 'Shifts' and no extension method 'Shifts' accepting a first argument of type 'int' could be found.'
Correct me if I'm wrong but SelectMany() selects all of the columns from what you want to join on. In this case, in the first SelectMany() I get only values from the "Shifts" table and in the second SelectMany() I get only values from the "Users" table. Why is this different from the SQL join? When joining in SQL you can get every column as you join them together, SelectMany() yields only the values of the second table that you are joining on. Is it even possible to get that value in the "Lines" table or will I have to do another query? Any help would be great.
int idEnteredByUser = 123;
var usersToWork = entityDataModel.Lines
//....NOT IN MY CODE NOW....
// .Select(line => line.Id)//THIS IS WHAT I NEED.
// .Select(line => line.Description, line.Id//OR THIS TO RETURN TWO VALUES IF POSSIBLE
//This is my current code, I need to include on of the select lines above.
.SelectMany(line => line.Shifts) //Join lines on shifts.
.Where(shift => shift.EndTime >= DateTime.Now) //Join restricted times.
.SelectMany(user => user.Users) //Join the restricted shift times on users.
.Where(user => user.UserId == idEnteredByUser ); //Only look for the specific user
This works much easier using LINQ query syntax.
I'm assuming that you made a typo in your posted code and that user is a property of shift.
var idEnteredByUser = 123;
var usersToWork =
from line in entityDataModel.Lines
from shift in line.Shifts
where shift.EndTime >= DateTime.Now
from user in shift.Users
where user.UserId == idEnteredByUser
select new
{
Description = line.Description,
Id = line.Id
};
Related
I am trying to learn how to use LINQ to perform a query that yields the same result as this:
SELECT (
SELECT SUM(point)
FROM communitymemberpointfeature
WHERE communitymemberpointfeature.communitymemberid = communitymember.id
) AS points, communitymember.*
FROM communitymember
After browsing around the Internet, I constructed the following statement:
var list = (from pointFeature in communityMemberPointFeatureList
join member in communityMemberList on pointFeature.CommunityMemberId equals member.Id
group pointFeature by new { pointFeature.CommunityMemberId }
into grouping
select new
{
grouping,
points = grouping.Sum(row => row.Point)
}).ToList();
But this yielded a result like
[
{
points:7200,
grouping:[
{Id:1,Point:5000,FeatureId:1,CommunityMemberId:1},
{Id:2,Point:2200,FeatureId:1,CommunityMemberId:1},
],
}
...
]
What I really want is a result set like:
[
{points:7200,CommunityMemberId:1,firstname:'john',lastname:'blah' ....},
...
]
Can someone tell me what I did wrong?
Edit after comment added to the end
I can imagine you have problems translating your SQL into LINQ. When trying to write LINQ statements it is usually a lot easier to start from your requirements, instead of starting from a SQL statement.
It seems to me that you have a table with CommunityMembers. Every CommunityMember has a primary key in property Id.
Furthermore, every CommunityMember has zero or more CommunityMemberPointFeatures, namely those CommunityMemberPointFeatures with a foreign key CommunityMemberId that equals the primary key of the CommunityMember that it belongs to.
For example: CommunityMember [14] has all CommunityMemberPointFeatures that have a value CommunityMemberId equal to 14.
Requirement
If I look at your SQL, it seems to me that you want to query all CommunityMembers, each with the sum of property Point of all CommunityMemberPointFeatures of this CommunityMember.
Whenever you want to query "items with their zero or more subitems", like "Schools with their Students", "Customers with their Orders", "CommunityMembers with their PointFeatures", consider using GroupJoin.
A GroupJoin is in fact a Left Outer Join, followed by a GroupBy to make Groups of the Left item with all its Right items.
var result = dbContext.CommunityMembers // GroupJoin CommunityMembers
.GroupJoin(CommunityMemberPointFeatures, // With CommunityMemberPointFeatures
communityMember => communityMember.Id, // from every CommunityMember take the Id
pointFeature => pointFeature.CommunityMemberId, // from every CommunityMemberPointFeature
// take the CommunityMemberId
// Parameter ResultSelector: take every CommunityMember, with all its matching
// CommunityMemberPointFeatures to make one new object:
(communityMember, pointFeaturesOfThisCommunityMember) => new
{
// Select the communityMember properties that you plan to use:
Id = communityMember.Id,
Name = communityMember.Name,
...
// From the point features of this CommunityMember you only want the sum
// or property Point:
Points = pointFeaturesOfThisCommunityMember
.Select(pointFeature => pointFeature.Point)
.Sum(),
// However, if you want more fields, you can use:
PointFeatures = pointFeaturesOfThisCommunityMember.Select(pointFeature => new
{
Id = pointFeature.Id,
Name = pointFeature.Name,
...
// not needed, you know the value:
// CommunityMemberId = pointFeature.CommunityMemberId,
})
.ToList(),
});
Edit after comment
If you want, you can omit Selecting the values that you plan to use.
// Parameter ResultSelector:
(communityMember, pointFeaturesOfThisCommunityMember) => new
{
CommunityMember = communityMember,
PointFeatures = pointFeaturesOfThisCommunityMember.ToList(),
),
However, I would strongly advise against this. If CommunityMember [14] has a thousand PointFeatures, then every PointFeature will have a foreign key with a value 14. So you are transporting this value 14 1001 times. What a waste of processing power, not to mention all the other fields you plan not to use.
Besides: if you do this you violate against information hiding: whenever your tables changes internally, the result of this function changes. Is that what you want?
I am new to linq so I apologize in advance if it a dumb question. I inherited the following query and it is not producing correct results in order to fix it I have to understand what it is doing.
Here is the query, if someone can explain it to me what it is doing (also possibly fix it) that will be greatly appreciated.
**using (var dbCtx = new TLMDbContext())
{
var dvps = dbCtx.tblDVPTests.Where(x => x.DVPID == 2176);
// these 2 following if conditions doesnt bring correct result sets
if (dvpMasterPhaseId.HasValue)
{
dvps = dvps.Where(x => x.tblDVPPhases.All(p => p.DVPMasterPhaseID ==255));
}
if (dvpMasterVariantId.HasValue)
{
dvps = dvps.Where(x => x.tblDVPPhases.All(p => p.tblDVPVariants.All(v=>v.DVPMasterVariantID==681)));
}
}**
UPDATE
I want this following query to be written in LINQ and if one of you guys tell me what was wrong with the LINQ query above that will be great.
select * from tblDVPTest
inner join tblDVPPhase on tblDVPTest.DVPTestID=tblDVPPhase.DVPTestID
where dvpid=2176 and tblDVPPhase.DVPMasterPhaseID=255
Here is with both condition
select * from tblDVPTest
inner join tblDVPPhase on tblDVPTest.DVPTestID=tblDVPPhase.DVPTestID
inner join tblDVPVariant on tblDVPPhase.DVPPhaseID=tblDVPVariant.DVPPhaseID
where dvpid=2176 and tblDVPPhase.DVPMasterPhaseID=255 and tblDVPVariant.DVPMasterVariantID=681
As I can understand from code, you are using entity framework to interact with database. Below is a high level explanation:
Create DbContext object.
Select everything from table tblDVPTests into variable dvps where column DVPID of table tblDVPTests matches value of variable dvpNum.
If nullable variable dvpMasterPhaseId is not null then select only those values from dvps where referenced table tblDVPPhases has all rows with column DVPMasterPhaseID value matching with variable dvpMasterPhaseId value. Store result in dvps. If variable dvpMasterPhaseId is null skip this step.
If nullable variable dvpMasterVariantId is not null then select only those values from dvps where referenced table tblDVPPhases has all rows with column DVPMasterVariantID value matching with variable dvpMasterVariantId value. Store result in dvps. If variable dvpMasterVariantId is null skip this step.
Dispose DbCOntext object.
Note: There is a foreign key relationship between table tblDVPTests and tblDVPPhases.
To fix your issue first of all you should let us know what exactly you are expecting in the result.
Can anybody tell me how I would get the records in the first statement that are not in the second statement (see below)?
from or in TblOrganisations
where or.OrgType == 2
select or.PkOrgID
Second query:
from o in TblOrganisations
join m in LuMetricSites
on o.PkOrgID equals m.FkSiteID
orderby m.SiteOrder
select o.PkOrgID
If you only need the IDs then Except should do the trick:
var inFirstButNotInSecond = first.Except(second);
Note that Except treats the two sequences as sets. This means that any duplicate elements in first won't be included in the results. I suspect that this won't be a problem since the name PkOrgID suggests a unique ID of some kind.
(See the documentation for Enumerable.Except and Queryable.Except for more info.)
Do you need the whole records, or just the IDs? The IDs are easy...
var ids = firstQuery.Except(secondQuery);
EDIT: Okay, if you can't do that, you'll need something like:
var secondQuery = ...; // As you've already got it
var query = from or in TblOrganisations
where or.OrgType == 2
where !secondQuery.Contains(or.PkOrgID)
select ...;
Check the SQL it produces, but I think it should do the right thing. Note that there's no point in performing any ordering in the second query - or even the join against TblOrganisations. In other words, you could use:
var query = from or in TblOrganisations
where or.OrgType == 2
where !LuMetricSites.Select(m => m.FkSiteID).Contains(or.PkOrgID)
select ...;
Use Except:
var filtered = first.Except(second);
Let's say I have a table in a database that has three columns: Agency ID, Name, and Value.
I want to get a collection of <Name, Value> pairs grouped by Agency ID.
How can I do this? I tried something like below, which works, but makes a DB call for each agency!
from div in db.AgencyDivisionsENT
group div by div.AgencyId into NamePairCollection
select new KeyValuePair<int, IEnumerable<DivisionResults>>(NamePairCollection.Key,
NamePairCollection.Select(k => new DivisionResults
{
Name = k.Name,
Value = k.Value
));
I want to end up with something like this: IEnumerable<KeyValuePair<int, IEnumerable<NameValuePair>>>
Using chain syntax it would be:
db.AgencyDivisionsENT
.GroupBy(x=>x.AgencyId)
.ToDictionary(x=>x.Key, g=>g.Select(x=>new { k.Name, k.Value }).ToArray());
The easiest way to avoid round-tripping with this type of query is to group on the client side - by calling .AsEnumerable():
db.AgencyDivisionsENT
.Select (x => new { x.AgencyId, x.Name, x.Value } )
.AsEnumerable()
.GroupBy(...) // AsEnumerable() forces grouping to happen on the client
.ToDictionary(...)
This is in no way inefficient - as long as:
you select only the data you need from the server with the initial .Select statement
if you need .Where statement to filter the data, it is placed before the .AsEnumerable
you're selecting detail rows (as in this case) rather than just aggregates.
I couldn't get last articles of every writers in this statement.
List<Editor> lstEditors = dataContext.GetTable<Editor>().Where(t => t.M_Active).Select(t => t).ToList();
var lstArticles = from article in DAO.context.GetTable<Article>().ToList()
join editor in lstEditors on article.RefEditorId equals editor.EditorId
select
new
{
article.M_ArticleId,
article.M_Subject,
article.M_Text,
editor.M_EditorId,
editor.M_Member.M_EditorPicture,
M_NameSurname = editor.M_Member.M_Fname + " " + editor.M_Member.M_Lname
};
Be careful, your query is fetching all the contents of both the Editor and the Yazi tables and then performs Linq-to-Objects on it.
I'm not sure what you ask exactly either, do you want to obtain the list of all writers (editors) along with the last article of each one of these writers?
Do you want to get the writers that did not write any articles yet also?
Edit:
explanation of methods causing an immediate query
Any time you call one of the methods listed below on an IQueryable object (tables or other queries), it performs the actual query to SQL server:
ToList(), ToArray(), ToLookup(), ToDictionay()
Count(), Sum(), Avg(), Aggregate(), Min(), Max()
First(), FirstOrDefault(), Last(), LastOrDefault()
getting last article written by each writer
//create a subquery that returns an editor and its last article date
var editorLastArticleDates =
from article in DAO.context.GetTable<Article>()
group article by article.RefEditor into g
let lastArticleDate= g.Max(x => x.Date)
select new
{
Editor = g.Key,
LastArticleDate = lastArticleDate,
};
//Note: We did not do a ToList() here so the query is not executed
// The editorLastArticleDates object is a IQueryable<>
var query =
from article in DAO.context.GetTable<Article>()
join editorLastArticleDate in editorLastArticleDates
on new { article.Editor, article.Date } // 1
equals new { editorLastArticleDate.Editor, // 2
Date = editorLastArticleDate.LastArticleDate } // 3
select new
{
article.M_ArticleId,
article.M_Subject,
article.M_Text,
article.RefEditor.M_EditorId,
article.RefEditor.M_Member.M_EditorPicture,
M_NameSurname = article.RefEditor.M_Member.M_Fname + " "
+ article.RefEditor.M_Member.M_Lname,
};
//Note: We did not do a ToList() yet so the query is not executed
// The query object is a IQueryable<>
Console.WriteLine(query.ToString()); //Displays SQL query on the console
var results = query.ToList(); // SQL query is executed on this line.
In the code above, I left some remarks on things I had problems with:
When using join, the section between new and equals access only variables declared before the join keyword while the section after the equals keyword has access to the variable defined between join and in.
When writing your join condition, make sure you use equals and not ==.
When using new { XXX, YYY } syntax in your join condition, you declare anonymous types. If the property names are not identical on both sides, it will not compile. In order to have identical property names in this sample, I added the Date = before my value.
By the way, you should use LinqPad to test your queries, it is really a nice tool.