Select with fields to include in detail table with EF Core - entity-framework

How can I select whitch fields should be included for detail table in EF Core.
I tried with this query:
var result= this.context.MainTable
.Include(t => t.DetailTable)
.Select(t => new {
id = t.Id,
values = t.DetailTable.Select(t2 => t2.SomeField)
})
.ToArray();
I would expect this result to single query, but it does not. It automatically execute query one by one for every row in MainTable and select SomeField.

Related

Using string_agg in the many-to-many relation

I have entities like Product(Id, Name) and Keyword(Id, Description), and there is a many-to-many relationship between them.
The essence of my task is the following, I need to do a full-text search on Name and Description columns, using EF CORE 6
I already have some SQL code that works fine.
SELECT a."Id", a."Name" as name, k.txt
FROM "Products" AS a
LEFT JOIN (
SELECT x."ProductsId" as Id, string_agg(y."Description", ' ') as txt
FROM "ProductKeywords" x
JOIN "Keywords" y ON y."Id" = x."KeywordId"
GROUP BY 1
) k ON a."Id" = k.Id
WHERE to_tsvector(concat_ws(' ', a."Name", k.txt))
## to_tsquery('Some text');
And I need to write some LINQ code that will do something similar, but I have a problem with string_agg, and I don't understand how to implement it in LINQ and EF CORE will reflect it correctly
I tried to do the following
var products = _context.Products
.Select(e => new
{
Id = e.Id,
Name = e.Name,
Keywords = string.Join(" ", e.Keywords.Select(q => q.Description))
}).Where(e => EF.Functions.ToTsVector(e.Keywords).Matches("Some text")).ToList();
But I get an error, and it's most likely because of string.Join
could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'
Got the result, using linq2db
var query = _context.Products.ToLinqToDB()
.LeftJoin(_context.ProductsKeywords.ToLinqToDB().GroupBy(r => r.ProductId).Select(e => new {
Key = e.Key,
Txt = e.StringAggregate(",", t => t.Keyword.Description).ToValue()
}),
(product, productKeyword) => product.Id == productKeyword.Key,
(i, j) => new {
Id = i.Id,
Txt = j.Txt
}).Select(e => new {
Id = e.Id,
Txt = EF.Functions.ToTsVector(e.Txt)
}).Where(w => w.Txt.Matches("Some text"));

Left outer join statements containing Reference navigation properties found in all split queries

I have an entity which contains several reference navigation properties under it.
The Repository implementation for the entity looks some thing like this:
return await _dbContext.MyEntity
.Include(s => s.Address) //Reference Navigation
.Include(s => s.BuildingDetails) //Reference Navigation
.ThenInclude(s => s.ChildOfBuildingDetails)
.Include(s => s.ContactPersons)
.Include(s => s.Technicians)
.Include(s => s.DeactivationDetails) //Reference Navigation
.FirstOrDefaultAsync(s => s.Id == id, cancellationToken);
When I check the actual DB queries being executed, all the queries contain the reference navigation properties included in them as joins to the parent entity.
SELECT [m92].[Id], .......
FROM [MyDB].[ContactPersons] AS [m92]
INNER JOIN (
SELECT DISTINCT [m93].[Id], [t76].[Id] AS [Id0]
FROM [MyDB].[MyEntity] AS [m93]
LEFT JOIN (
SELECT [m94].*
FROM [MyDB].[DeactivationDetails] AS [m94]
WHERE [m94].[Deleted] = 0
) AS [t75] ON [m93].[Id] = [t75].[MyEntityId]
LEFT JOIN (
SELECT [m95].*
FROM [MyDB].[BuildingDetails] AS [m95]
WHERE [m95].[Deleted] = 0
) AS [t76] ON [m93].[Id] = [t76].[MyEntityId]
LEFT JOIN (
SELECT [m96].*
FROM [MyDB].[Address] AS [m96]
WHERE [m96].[Deleted] = 0
) AS [t77] ON [m93].[Id] = [t77].[MyEntityId]
WHERE [m93].[Deleted] = 0
) AS [t78] ON [m92].[MyEntityId] = [t78].[Id]
WHERE [m92].[Deleted] = 0
ORDER BY [t78].[Id], [t78].[Id0]
Basically, the whole portion inside the INNER JOIN is present in all the queries that are being executed. Ideally we only need to join the child entities with parent entity in the queries.
1) Why does EF core translate to queries such that it includes the reference navigation property in all the split queries?
2) Is there a way to avoid this behavior, to be specific, replace the INNER JOIN block with just the parent entity
1) Why does EF core translate to queries such that it includes the reference navigation property in all the split queries?
It's an implementation defect/missing optimization.
2) Is there a way to avoid this behavior, to be specific, replace the INNER JOIN block with just the parent entity
The only way I found is to materialize the query with collection navigation property includes (which generate the additional queries) removed, then manually execute queries to load the related collections (requires tracking queries and relies on navigation property fix-up).
For instance (assuming navigation properties not marked as reference are collections):
// Query with filters only
var query = _dbContext.MyEntity
.Where(s => s.Id == id);
// Execute and materialize query with only filters and reference includes
var result = await query
.Include(s => s.Address) //Reference Navigation
.Include(s => s.BuildingDetails) //Reference Navigation
//.ThenInclude(s => s.ChildOfBuildingDetails)
//.Include(s => s.ContactPersons)
//.Include(s => s.Technicians)
.Include(s => s.DeactivationDetails) //Reference Navigation
.FirstOrDefaultAsync(cancellationToken);
// Load the related collections
await query.SelectMany(s => s.BuildingDetails.ChildOfBuildingDetails)
.LoadAsync(cancellationToken);
await query.SelectMany(s => s.ContactPersons)
.LoadAsync(cancellationToken);
await query.SelectMany(s => s.Technicians)
.LoadAsync(cancellationToken);

Linq with Included Subquery is failing on EF Core

I am using MySQL DB with ef core all works fine but the following query does nto return expected result.
var query = _context.ServiceData.
Include(x => x.Country)
.Include(x=>x.Country.CountryLocale)
.Where(x=>x.Country.CountryLocale.Any(l=>l.Locale == "en-US"));
After executing and doing
query.First().Country.CountryLocale.Count // Returns count of greater than 1 when expected count is 1
The table only has 2 entries and the above pulls both when only 1 is expected.
Table Layout
Id Name Locale
1 Test en-US
2 Test en-GB
All examples I have seen seem to suggest doing it how I am doing it so not sure what I am missing.
Here is the sql generated by above Linq
SELECT `a`.`Id`, `a`.`ServiceDataCode`, `a`.`CountryId`, `a`.`Enabled`, `a`.`LastUpdated`, `a`.`TimezoneId`, `c`.`Id`, `c`.`DialingCode`, `c`.`Enabled`, `c`.`IsoNumeric`, `c`.`IsoThreeLetterCode`, `c`.`IsoTwoLetterCode`, `c`.`LastUpdated`, `c0`.`Id`, `c0`.`IsoTwoLetterCode`, `c0`.`LastUpdated`, `c0`.`Locale`, `c0`.`Name`
FROM `ServiceData` AS `a`
INNER JOIN `CountryData` AS `c` ON `a`.`CountryId` = `c`.`Id`
LEFT JOIN `CountryLocale` AS `c0` ON `c`.`IsoTwoLetterCode` = `c0`.`IsoTwoLetterCode`
WHERE EXISTS (
SELECT 1
FROM `CountryLocale` AS `c1`
WHERE (`c`.`IsoTwoLetterCode` = `c1`.`IsoTwoLetterCode`) AND (`c1`.`Locale` = 'en-US'))
ORDER BY `a`.`Id`, `c`.`Id`, `c0`.`Id`
I guess a stored procedure is another option but wanted to do it without.
Well, here is an object query with a right paths of country and specific country locale that belong to first service data:
using (var query = _context.ServiceData
.Include("Country.CountryLocale")
.Where(x => x.ServiceData.CountryId == x.Country.Id)
.Where(x => x.Country.IsoTwoLetterCode == x.Country.CountryLocale.IsoTwoLetterCode)
.Where(x => x.Country.CountryLocale.Locale == "en-US"))
{
query.FirstOrDefault();
}
Hope this helps.

Entity Framework Except Fields

I want to call all columns but except some fields. For example only base table select not include relationships.
var _person= db.Persons.Select(x => new
{
x.id,
x.Name,
x.Surname,
x.Age,
....
x.Address.City ..
}).ToList();
I want to select => var _person= db.Persons.UNSelect(x => x.Address).ToList();
Unselect or Except or nondisplay ???

Entity Framework LINQ Query match all members of child collection

I have a Site table that has a many-to-many relationship with a UtilityServiceConnection table using a linking table called LinkSiteUtilityServiceConnection. Given a set of ServiceConnectionIds, I need to locate the Site that is exclusively linked to all of them and no more. I think I should be able to write the query using All on the collection but it's not working as expected.
var serviceConnectionIds = new[] { 546892, 546911, 546923 };
var sites1 = db.Sites
.Where(x => x.LinkSiteUtilityServiceConnections.All(y => serviceConnectionIds.Contains(y.UtilityServiceConnectionId)))
.ToList();
Assert.AreEqual(1, sites1.Count); //fails
This produces the query below that returns ~250,000 records when I expect to get one.
SELECT [Extent1].*
FROM [dbo].[Site] AS [Extent1]
WHERE NOT EXISTS (SELECT 1 AS [C1]
FROM [dbo].[LinkSiteUtilityServiceConnection] AS [Extent2]
WHERE ([Extent1].[SiteId] = [Extent2].[SiteId])
AND ((NOT ([Extent2].[UtilityServiceConnectionId] IN (546892, 546911, 546923)))
OR (CASE
WHEN ([Extent2].[UtilityServiceConnectionId] IN (546892, 546911, 546923)) THEN cast(1 as bit)
WHEN (NOT ([Extent2].[UtilityServiceConnectionId] IN (546892, 546911, 546923))) THEN cast(0 as bit)
END IS NULL)))
Why isn't All working as I expect? What's the best way to write this query?
check this code:
query 1:
var sites1 = db.Sites
.Where(x => serviceConnectionIds.All(y =>
x.LinkSiteUtilityServiceConnections
.Select(u => u.UtilityServiceConnectionId).Contains(y)))
.ToList();
query 2:
var query = db.Posts.AsQueryable();
var sites1 = serviceConnectionIds.Aggregate(query,
(current, item) => current.Where(e => e.LinkSiteUtilityServiceConnections
.Any(c => c.UtilityServiceConnectionId== item))).ToList();