DefaultIfEmpty returns empty rows - c#

I've been trying to perform a Left Join kind of expression in LINQ to Entities, however the DefaultIfEmpty method works differently to what I expected - it returns an empty row for each CounterNo that doesn't have a match in the Readings table.
var leftjoin = from counter in database.Counters
join reading in database.Readings
on counter.CounterNo equals reading.CounterNo into gj
from x in gj.DefaultIfEmpty()
select x;
This way I don't know which rows from the Counters table don't have a corresponding row the Readings table.
How do I make this work?

Sounds like you simply don't want to add in the from x in gj.DefaultIfEmpty(), and you instead want to have each item in the left table paired with a group of items in the right table (that group may have zero elements, which is how you know when there are no matching items) which is exactly the behavior you get when you remove that line.

Related

MVC Joining many 2 many tables and filling list based on result in c#

I am doing a school project and need help with this last problem I am having.
Currently I am trying to do a many 2 many join and then fill an IEnumerable list with the result - using linq and lambda.
The purpose is to show the compatible games along with every product.
My code as of now:
else
{
var result = (from g in db.Games
join gs in db.GameSize
on g.GameId equals gs.GameId
join s in db.Size
on gs.SizeId equals s.SizeId
join p in db.Product
on s.SizeId equals p.SizeId
select p.Size.Name);
games = db.Games
.Where(game => game.GameSize
.All(s => s.Size.Name == result.FirstOrDefault()));
}
My idea is to join through the tables and find the gameid who have a matching productid - and then add them to "games".
I am aware that this table design is horrible and that I am only getting the first result in the list with FirstOrDefault().
Does anyone have a suggestion or solution to help me? Thanks.
Please ask if I am not making any sense.
Essentially I just wan't to show the games linked to a size. My table looks like this:
--SIZE
insert into size values ('Large')
insert into size values ('Medium')
insert into size values ('Small')
--GAMES
insert into games values ('Magic The Gathering')
insert into games values ('Pokemon')
insert into games values ('Dead of Winter')
--GAMESIZE (RELATION GAMES AND SIZE) (SIZEID, GAMEID)
insert into gamesize values (1, 1)
insert into gamesize values (2, 2)
insert into gamesize values (2, 3)
First, you should really move on from the LINQ to SQL syntax. The EF syntax is much easier and readable. For example:
var result = db.Games.Include("GameSize.Size.Product").Select(m => m.Size.Name);
Second, All doesn't do what you seem to think it does. It returns a boolean: true if all the items match the condition, false if not. It's very unlikely that all your GameSizes have the same Size.Name. You might be looking for Any here.
Third, this whole thing seems counter-intuitive. You're getting all the games and selecting all the size names. Then using that list of size names, to select games that have those size names. In other words, you're doing an extra query to get the results you already had. Remove the select from the result and use that for games instead.
Long and short, if I'm understanding your code properly, you can reduce all of this to just one simple line:
games = db.Games.Include("GameSize.Size.Product");

Left outer join using LINQ -- understanding the code

I would be grateful if someone could explain the meaning of the term into while using LINQ. In general, I am trying to understand how to make INNER JOIN, LEFT OUTER JOIN etc. in C#.
I have the main table Students that stores a few foreign ID keys which then are substituted by their names when running a query. The names are read from look up tables such as Marks, SoftwareVersions, Departments etc. All fields are required but MarkID. The query I tried to build in LINQ is this:
SELECT * FROM dbo.Students
INNER JOIN dbo.Departments ON dbo.Students.DepartmentID=dbo.Departments.DepartmentID
INNER JOIN dbo.SoftwareVersions ON dbo.Students.SoftwareVersionID=dbo.SoftwareVersions.SoftwareVersionID
INNER JOIN dbo.Statuses ON dbo.Students.StatusID=dbo.Statuses.StatusID
LEFT JOIN dbo.Marks ON dbo.Students.MarkID=dbo.Marks.MarkID
WHERE dbo.Students.DepartmentID=17;
I somehow managed to get the code below worked after reading plenty of articles and watching some videos but I don't feel like I have a complete understanding of the code. The bits that confuse me are in 5th line ending with into and then in the very next line beginning with from m .... I'm confused what into does and and what really happens in from m .... And this is the code in LINQ:
var result = from st in dbContext.Students where st.DepartmentID == 17
join d in dbContext.Departments on st.DepartmentID equals d.DepartmentID
join sv in dbContext.SoftwareVersions on st.SoftwareVersionID equals sv.SoftwareVersionID
join stat in dbContext.Statuses on st.StatusID equals stat.StatusID
join m in dbContext.Marks on st.MarkID equals m.MarkID into marksGroup
from m in marksGroup.DefaultIfEmpty()
select new
{
student = st.StudentName,
department = p.DepartmentName,
software = sv.SoftwareVersionName,
status = st.StatusName,
marked = m != null ? m.MarkName : "-- Not marked --"
};
I believe Example section from How to: Perform Left Outer Joins MSDN page is really well explained. Let's project it to your example. To quote first paragraph from the page
The first step in producing a left outer join of two collections is to
perform an inner join by using a group join. (See How to: Perform
Inner Joins (C# Programming Guide) for an explanation of this
process.) In this example, the list of Person objects is inner-joined
to the list of Pet objects based on a Person object that matches
Pet.Owner.
So in your case, the first step is to perform an inner join of list of Students objects with the list of Marks objects based on MarkID in Students object matches MarkID in Marks object. As can be seen in the quote, inner join is being performed using group join. If you check Note section in MSDN page on how to perform group join, you can see that
Each element of the first collection appears in the result set of a
group join regardless of whether correlated elements are found in the
second collection. In the case where no correlated elements are found,
the sequence of correlated elements for that element is empty. The
result selector therefore has access to every element of the first
collection.
What this means in the context of your example, is that by using into you have group joined results where you have all Students objects, and sequence of correlated elements of Marks objects (in case there is no matching Marks objects, the sequence is going to be empty).
Now let's go back to How to: Perform Left Outer Joins MSDN page, in particular second paragraph
The second step is to include each element of the first (left)
collection in the result set even if that element has no matches in
the right collection. This is accomplished by calling DefaultIfEmpty
on each sequence of matching elements from the group join. In this
example, DefaultIfEmpty is called on each sequence of matching Pet
objects. The method returns a collection that contains a single,
default value if the sequence of matching Pet objects is empty for any
Person object, thereby ensuring that each Person object is represented
in the result collection.
Again, to project this to your example, DefaultIsEmpty() is being called on each sequence of matching Marks objects. As explained above, the method returns a collection that contains a single, default value if the sequence of matching Marks objects is empty for any Student object, which ensures each Student object will be represented in the resulting collection. As a result what you have is set of elements, that contain all Student objects, and matching Marks object, or if there is no matching Marks object, default value of Marks, which in this case is null.
what I can say is that "into MarksGroup" stores the result data of your joined tables into a temporary (application based, not database based) resultset (in sql terms: a table, so its a SELECT INTO)
In the next line, your code then selects from Marksgroup the columns with your data (in sql terms: SELECT student, department, software, status, marked FROM Marksgroup
So basically, it's getting your data from the db, then putting it aside to "Marksgroup, and in the very next step getting Marksgroup back in your fingers to take out the data you want to use in your c# code.
Try to get rid of Marksgroup, it should be possible (haven't tested ist with your code). It should be something like this:
from st in dbContext.Students where st.DepartmentID == 17
join d in dbContext.Departments on st.DepartmentID equals d.DepartmentID
join sv in dbContext.SoftwareVersions on st.SoftwareVersionID equals sv.SoftwareVersionID
join stat in dbContext.Statuses on st.StatusID equals stat.StatusID
join m in dbContext.Marks on st.MarkID equals m.MarkID
select new
{
student = st.StudentName,
department = p.DepartmentName,
software = sv.SoftwareVersionName,
status = st.StatusName,
marked = m != null ? m.MarkName : "-- Not marked --"
};
Your second question with 'm' : This should also show a different behaviour without your temporary resultset "Marksgroup"

Correct Join statement in Linq

I searched for the answer of my question, since am a beginner am not able to get those all, so asked my own,
I have two tables SUBMENU and AUTHORIZATION am tryin to put a join but not clear whether to put left or right or some other way, since am new to linq.
Here is what i have done so far,
var _lststage =
from sm in db.SUB_MENUs
join a in db.AUTHORISATIONs
on sm.SUB_MENU_ID equals a.SUB_MENU_ID into joined_autho
from jA in joined_autho.DefaultIfEmpty()
where sm.MENU_ID.Equals(ViewState["MenuId"]) &&
jA.Roleid Equals ddlroleid.Selectedvalue
select new
{
sm.SUB_MENU_ID,
sm.SUB_MENU_NAME,
jA.checkbox,
};
I want to get all the submenus from the submenu table based on the menuid in view state, and,
I need to get the value for check box in Authorization table based on the role id and submenuid, and if there is no value for that role id in authorization table default false value should return.
Hope i explained my scenario well,
Possibilities of duplicate question...
Sorry if.
Your query even will not compile. In your where condition second Equals should be method call:
// instead of: jA.Roleid Equals ddlroleid.Selectedvalue
jA.Roleid.Equals(ddlroleid.Selectedvalue)
Also you have unnecessary comma on last line of select statement:
// instead of: jA.checkbox,
jA.checkbox
Complete query should look like:
from sm in db.SUB_MENUs
join a in db.AUTHORISATIONs
on sm.SUB_MENU_ID equals a.SUB_MENU_ID into joined_autho
from jA in joined_autho.DefaultIfEmpty()
where sm.MENU_ID.Equals(ViewState["MenuId"]) &&
jA.Roleid.Equals(ddlroleid.Selectedvalue)
select new
{
sm.SUB_MENU_ID,
sm.SUB_MENU_NAME,
jA.checkbox
};
I believe you have tagged your question appropriately, and this is Linq to SQL query.

Linq Where Contains ... Keep default order

I have a collection of ID numbers that I wish to return some object for, I'm doing it by using a linq statement with a where that's using a contains statement:
var recentCats = (from i in EntityCache.Default.GetAll<Categories>()
where WebProfile.Current.RecentlyCreatedCategories.Contains(i.Id)
&& BoundCategory.ParentItemClass.Id.Equals(i.ParentItemClass.Id)
select new CategoryInfo()
{
Category = i,
ClassId = i.ParentItemClass.Id,
ClassImage = NamedResourceManager.GetResourceBinary(i.ParentItemClass.NameResourceId)
});
This works perfectly fine, except that I want to keep the order of items in the returned collection the same as they were in the list that goes in. So for example if I had a list of IDs: 14, 603, 388, I want the objects that come back to be in the same order, not the order that they're returned by the cache. Is there any way in entity framework to do this, or any way to do it that doesn't involve me writing a foreach loop?
Thanks
The Where Linq extension as with most extensions maintains the original order of the list.
Do LINQ's Enumerable Methods Maintain Relative Order of Elements?
As long as you do not explicitly reorder or use an operator that naturally would reorder the original list's order should be maintained. Obviously reordering the list for a where statement would be unneeded overhead.
The reason the information above does not apply to this question is in the comments bellow.
I would suggest changing the output of the select to be a key/value pair, where the key is the index of the Id in your list, and the value is your new object, then orderBy the key on the resulting set, and select the value.
For anyone interested, I was able to get them to come out in the same order as the list by joining them, as mentioned by Ray in the comments above.
var recentCats = (from i in WebProfile.Current.RecentlyCreatedCategories
join b in allCats
on i equals b.Id
where BoundCategory.ParentItemClass.Id.Equals(b.ParentItemClass.Id)
select ...
Thanks again for all your help & answers.

Which LINQ expression do I need for this, without looping?

I have an MSSQL database with LINQ to SQL.
I have three tables.
Requests -> id, string name
Results -> id, requestID, int jumps
Places -> id, resultID, int location
Then, using an input string, I need to get an ICollectable or array or something of Place which meets the following:
Each Request that has name=input, take its ID.[you can assume only one has]
Each Result that has requestID=ID[from above] - take its id.
Each Place that has resultID='id[from above]' - append to array for further processing.
I made it by looping on all Results and then executing another LINQ statement, but its extremely slow [about 500ms for a single request!]. Can I make it any faster?
Thank you!
Edit: Whoops, I also need it grouped by result. aka a List of List of Places, while each inner list contains one column from Result.
You can perform table joins in Linq2Sql using the join keyword:
var places = from request in Requests
join result in Results on request.Id equals result.requestID
join place in Places on result.Id equals place.ResultId
where request.name = input
select place;
Somthing like
Requests.Where(r => r.name == input).Results.Places.Select();
If this is too slow then I expect you need some indexes on your database.
If you don't have the relationships in your model then you need to establish some foreign key constraints on your tables an rebuild your model.

Categories

Resources