Linq GroupBy Clause not including items with zero count - c#

I have a query below which is supposed to group the result by Id, EntityName, DocType, Jurisdiction. For each group the query also returns the ProductList items.
At the moment if the group contains one or more than one product, Then i can see the result giving out a group with a combination of Id,EntityName,DocType,Jurisdiction and ProductList, However if the result doesnt contain products for a particular group i do not see the group at all. What i would like to do is show the groups even if does not have any products in its group. So if the count of ProductList is zero, i would like to set
ProductList= new List NettingAgreementProductDto. Any input would be highly appreciated.
var result = from nae in nettingAgreementEntities.Result
join no in nettingOpinions.Result
on nae.EntityId equals no.EntityId
join np in nettingProducts.Result
on no.ProductId equals np.Id
group np by new
{ nae.EntityId,
nae.EntityName,
nae.DocType,
nae.Jurisdiction
} into g
select new NettingAgreementEntityDto
{
Id = g.Key.EntityId,
EntityName = g.Key.EntityName,
DocType = g.Key.DocType,
Jurisdiction = g.Key.Jurisdiction,
ProductList = g.Select(x => new
NettingAgreementProductDto
{
Id = x.Id,
Name = x.Name
}).ToList()
};

To recap from the comments, currently your query is using Inner Join for associating NettingAgreementEntity with NettingAgreementProducts. This not only multiplies the result set (and thus requires you to use GroupBy after), but also filters out the NettingAgreementEntity without NettingAgreementProducts.
You can achieve the goal by switching to Group Join (or Left Outer Join + GroupBy).
But why entering all these complications. EF navigation properties allow you to almost forget about manual joins, and also allow you to easily see the multiplicity, thus whether you need to group the result or not.
So what I would suggest is to add the currently missing collection navigation property to your NettingAgreementEntity class:
public class NettingAgreementEntity
{
// ...
public virtual ICollection<NettingOpinion> Opinions { get; set; }
}
Optionally do the same for NettingAgreementProduct in case in the future you need something similar for products (it's a many-to-many relationship and should be able to be queried from both sides).
Also I would rename the NettingOpinion class navigation properties NettingAgreementProductNavigation and NettingAgreementEntityNavigation to something shorter, for instance Product and Entity. These names (as well as the names of the collection navigation properties) do not affect the database schema, but IMHO provide better readability.
Once you have that, you'll see that the desired LINQ query is a matter of simple Selects which convert entity class to DTO and let EF query translator produce the necessary joins for you:
var result = db.Set<NettingAgreementEntity>()
.Selec(nae => new NettingAgreementEntityDto
{
Id = nae.EntityId,
EntityName = nae.EntityName,
DocType = nae.DocType,
Jurisdiction = nae.Jurisdiction,
ProductList = nae.Opinions
.Select(no => new NettingAgreementProductDto
{
no.Product.Id,
no.Product.Name,
}).ToList(),
});

Related

Obtain more information from the user via Inner join Linq

I supposed in the process developed is such that it must show all the movies that are into film tablen and showing off, but this is how I have tried to do this:
it must find out which genres have in users tablen where after to show the users who like the first.
//As I said, I have a session at the top of the code.
int brugerid = Convert.ToInt16(Session["id"]);
var result = (from f in db.films
//it must find out which genres have in users tablen where after to show the users who like the first.
//brugere are users
//gener It is the genes users like.
join usersgenerId in brugere.Fk_generId on gener.generId equals usersgenerId.BrugereId
select new
{
image_navn = ((f.imgs.FirstOrDefault(i => i.feature == true)).navn == null ? "default.png" : (f.imgs.FirstOrDefault(i => i.feature == true)).navn),
image_feature = f.imgs.Where(A => A.feature == true),
film_navn = f.navn,
film_id = f.filmId,
film_tekst = f.tekst,
film_gener = f.gener.navn
}).ToList();
RepeaterFilmList.DataSource = result;
RepeaterFilmList.DataBind();
Table information
Brugere the name
id = BrugereId
Fk_generId belonging to the genes that user has selected.
and many other
Gener is the name
has generId as id
As mentioned in the comment, the question really is: show all movies that is in the same genre that the user preferred and then show everything else.
Although the following approach might not be db efficient (too lazy to create the db for this, so I am simulating everything in memory and using Linq to Object to solve the issue), it can certainly be resolved by the following steps:
Get the recommendation (matching the user's movie genre preference) like so:
var recommendation =
from f in films
from ug in userGenres
where ug.UserId == user.Id && ug.GenreId == f.GenreId
select f;
Now that we know what the user preferred, we can further filter this to just the preferred films' Id... and use that to get the rest of the unpreferred films (basically anything not matching the preferred film Ids):
var recommendedFilmIds = recommendation.Select(f => f.Id);
var everythingElse =
from f in films
where !recommendedFilmIds.Contains(f.Id)
select f;
Finally, join them together using Union and injecting the nessary fields for display purpose like Genre.Name, etc. like so:
var filmList = recommendation.Union(everythingElse).Select(f => new {
f.Id,
f.Title,
Genre = genres.Where(g => g.Id == f.GenreId).Select(g => g.Name).First()
});
And there you have it, the combined list will now contains both preferred films first (at top), followed by unpreferred films afterward.
The simulated tables are as follows: films which contains its own Id and genreId and userGenres which contains many to many relationship between user and genre and a particular user object which contains the user id.
An example of this can be found at: https://dotnetfiddle.net/Skuq3o
If you use EF, and you have a navigation property to genre table and you want to include those table as part of the query, use .Include(x => x.genre) or whatever you call your genre table after from f in films to avoid n+1 select if you wish to include the genre info in the final select clause.

Lazy loading vs individually loading each property for foreign keys

Since I am using Entity Framework 6 Database First and my database has all of its relationships setup, my EDMX auto generates navigation properties for me whether they are object lists or individual objects. This way I can lazy load using .Include(x => x.NavProperty).
How would I handle something such as the following using lazy loading that was previously done in a stored procedure that would return a list of OrderDetailDto which has a string property called MaterialDescription?
SELECT od.*, IIF(m.Description is null, od.Description, m.description) as MaterialDescription
FROM OrderDetail od
LEFT OUTER JOIN Material m ON od.MaterialId = m.Id
I am really trying to avoid doing this but I might end up ignoring all of the build in navigation properties and create a Dto for each entity and strictly add the properties I want bound to it. Then do selects like the following:
using (var context = new AppContext())
{
var odList = (from od in context.MaterialPODetails
join m in context.Materials on od.MaterialId equals m.Id into gj
from m in gj.DefaultIfEmpty()
select OrderDetailDto
{
Id = od.Id,
MaterialPOId = od.MaterialPOId,
MaterialId = od.MaterialId,
Description = od.Description,
Unit = od.Unit,
Quantity = od.Quantity,
UnitPrice = od.UnitPrice,
Archived = od.Archived,
DateCreated = od.DateCreated,
CreatedBy = od.CreatedBy,
DateModified = od.DateModified,
ModifiedBy = od.ModifiedBy,
// navigation properties
MaterialDescription = (m == null ? obj.Description : m.Description)
}).ToList();
return odList;
}
}
Please give me any advice possible on how to avoid. I want to avoid creating Dtos and simply use the Auto Generated EF classes with the virtual properties but it seems that there is limitations and performance differences. I won't be able to do the If statement that I want to do with the left outer join.
I don't know if this is proper terminology but I believe what I want is to still use Flattened objects but not have to create Dtos.

Lambda Syntax for Joins via Navigation Property?

I can perform a join using a navigation property, which to me is more DRY because I'm not repeating the join criteria everywhere:
(from c in db.Companies
from e in c.Employees
select new { Employee = e, Company = c}).ToList();
Because the ORM knows how Companies is related to Employees, the c.Employees navigation property is used to infer the FK criteria for the join.
What is the direct translation to extension/lambda syntax of a multiple from clause via navigation property?
I know there is a Join extension method, but requires you explicitly name the FK's being compared, rather than it imply that criteria by the navigation property. This is non-working but hopefully expresses my intent:
db.Companies
.Join(c.Employees, /* don't want to explicitly name FKs*/)
.Select(x => new { Employee = x.e, Company = x.c}).ToList();
Of course Join(c.Employees doesn't work because there is no c in this context, but idea being somehow use the Companies.Employees navigation property to imply the join criteria.
I know I can do:
db.Companies.Select(c => new { Employees = c.Employees, Company = c })
but that is a different result set, as it returns one record per company, and then the list of employees as a nested property. Rather than the first which is a join, thus there is a record for every related combination, and the result has a Employee property instead of a Employees collection.
I'm not sure, but guessing .SelectMany is the direct translation. You don't get a c reference to the parent, so if you do multiple of these:
db.Companies.SelectMany(c=>c.Employees).SelectMany(e=>e.VacationDays).Select(v => new { VacationDay = v, Employee = v.Employee, Company = v.Employee.Company })
You have to walk backwards across the relationships to flatten out the join. In linq it's much simpler because you would have c, e and v all in the context of the select. I don't know if you can express the same thing in extension methods, such that all three alias/references are passed down. Maybe just a consequence of the extension method syntax, but hoping someone will provide a better equivalent.
SelectMany is indeed what multiple from clauses are mapped into.
In order to keep variables in scope the projection each SelectMany needs to project the sequence into a new anonymous object that keeps all of the appropriate variables in scope:
var query = db.Companies.SelectMany(company => company.Employees,
(company, employee) => new
{
company,
employee
});
To add additional projections for additional nested navigation properties, simply repeat the pattern with a subsequent call to SelectMany:
var query = db.Companies.SelectMany(company => company.Employees,
(company, employee) => new
{
company,
employee
}).SelectMany(pair => pair.employee.VacationDays,
(pair, vactionDay) => new
{
pair.company,
pair.employee,
vactionDay,
});
See this blog post for some more details and an in-depth description of this transformation, and how it scales.
Wouldn't it just something like:
db.Employees.Select(m => new { Employee = m, Company = m.Company });
As each employee has a Company, why don't just add navigation property "Company" to Employee entity?
To get vacations, just change it to the following:
db.Employees.SelectMany(
employee => employee.VacationDays,
(employee, vacationDay) => new
{
Employee = employee,
Company = employee.Company,
VacationDay = vacationDay
});
Update:
Actually, there is no difference between:
(from c in db.Companies
from e in c.Employees
select new { Employee = e, Company = c}).ToList();
and:
(from e in c.Employees
from c in db.Companies
select new { Employee = e, Company = c}).ToList();

IQueryable Linq query with multiple joins at inner level

EDIT: forgot to say I'm using Fluent NHibernate, even though the tag could hint about it anyway.
I have these entity classes:
class OuterLevel
{
ICollection<MidLevel> mid_items;
... other properties
}
class MidLevel
{
OuterLevel parent;
Inner1 inner1;
Inner2 inner2;
... other properties
}
class Inner1
{
int id;
string description;
}
class Inner2
{
int id;
string description;
}
I need to build a Linq query that returns a list of OuterLevel objects with all children populated properly.
Supposing all mappings are correct and working, the hard part I'm finding here is that the resulting query should be something like
SELECT * FROM OuterLevelTable OLT INNER JOIN MidLevelTable MLT ON (MLT.parentID = OLT.ID) INNER JOIN
Inner1Table ON (MLT.Inner1ID = Inner1Table.ID) INNER JOIN
Inner2Table ON (MLT.Inner2ID = Inner2Table.ID)
WHERE (Inner1Table.someproperty1 = somevalue1) AND (Inner2Table.someproperty2 = somevalue2)
The main problem is that two joins start from MidLevel object downward the hierarchy, so I cannot figure out which Fetch and FetchMany combination can be used without having the resulting query join two times the MidLevelTable, such as the following does:
return All().FetchMany(x => x.mid_items).ThenFetch(x => x.inner1).FetchMany(x => x.mid_items).ThenFetch(x => x.inner2);
I would like to return a IQueryable that can be further filtered, so I would prefer avoiding Query and QueryOver.
Thanks in advance,
Mario
what you want is not possible because when you filter on the joined tables the resulting records are not enough to populate the collections anyway. You better construct the query in one place to further tune it or set the collection batch size to get down SELECT N+1.

Counting in a Linq Query

I have a fairly complicated join query that I use with my database. Upon running it I end up with results that contain an baseID and a bunch of other fields. I then want to take this baseID and determine how many times it occurs in a table like this:
TableToBeCounted (One to Many)
{
baseID,
childID
}
How do I perform a linq query that still uses the query I already have and then JOINs the count() with the baseID?
Something like this in untested linq code:
from k in db.Kingdom
join p in db.Phylum on k.KingdomID equals p.KingdomID
where p.PhylumID == "Something"
join c in db.Class on p.PhylumID equals c.PhylumID
select new {c.ClassID, c.Name};
I then want to take that code and count how many orders are nested within each class. I then want to append a column using linq so that my final select looks like this:
select new {c.ClassID, c.Name, o.Count()}//Or something like that.
The entire example is based upon the Biological Classification system.
Assume for the example that I have multiple tables:
Kingdom
|--Phylum
|--Class
|--Order
Each Phylum has a Phylum ID and a Kingdom ID. Meaning that all phylum are a subset of a kingdom. All Orders are subsets of a Class ID. I want to count how many Orders below to each class.
select new {c.ClassID, c.Name, (from o in orders where o.classId == c.ClassId select o).Count()}
Is this possible for you? Best I can do without knowing more of the arch.
If the relationships are as you describe:
var foo = db.Class.Where(c=>c.Phylum.PhylumID == "something")
.Select(x=> new { ClassID = x.ClassID,
ClassName = x.Name,
NumOrders= x.Order.Count})
.ToList();
Side question: why are you joining those entities? Shouldn't they naturally be FK'd, thereby not requiring an explicit join?

Categories