DynamicLinq build select with subquery - entity-framework

I dynamically build select with System.Linq.Dynamic.Core. I want to add another column (subquery). Is this possible?
I need to get same results as this query:
var test = this.DbContext.Countries.Select(t => new
{
t.Id,
t.ISOCode,
lookup = t.Translates.Where(t2 => t2.LangISOCode=="ENG").Select(t2 => t2.Title).FirstOrDefault()
}).ToArray();
I get this far:
this.DbContext.Countries.Select("new(Id,ISOCode)").ToDynamicArrayAsync()
but not sure how to add additional subquery column.

Solution is:
await this.DbContext.Countries.Select("new(Id,ISOCode,Translates.Where(LangISOCode=\"SLV\").Select(Title).FirstOrDefault() as translates)").ToDynamicArrayAsync();
I found solution by combing code from source tests (select, where, firstordefault).

Related

TypeORM Postgres filter by relation count

context
I have Users collection and Recipe collection, ManyToMany relation between them
I'm new in this framework, wondering how can I do the following query:
count users with at least one recipe
count users without any recipes
I have found loadRelationCountAndMap is very useful in counting how many recipes a user has, but I can't seem to filter the total response according to this property.
I have tried this:
const users_without_recipes = await getRepository(User)
.createQueryBuilder('user')
.addSelect(['user.createdAt', 'user.email'])
.loadRelationCountAndMap('user.recipes_count', 'user.recipes')
.where('user.recipes_count = :count', {count: 0})
.getManyAndCount();
also tried to use postgres array_count but not sure how to integrate it with the typeORM framework
and help is very appreciated
You can do this with subqueries I think.
Something like this in SQL:
SELECT *
// ... other stuff
WHERE user.id IN (
SELECT u.id
FROM user u JOIN recipie r USING(id)
GROUP BY u.id
HAVING COUNT(*) > 10
)
Or in TypeORM:
// ...
.where(qb => {
const subQuery = qb.subQuery()
.select("u.id")
.from(User, "u")
// join users and recipies
.groupBy("u.id")
.having("COUNT(*) > :count", { count: 10 })
.getQuery();
return "user.id IN " + subQuery;
});
//...
I have stumbled upon this exact problem myself. I have fond a couple of sources that could help you find the solution. Your underlying problem is more general. If you turn on logging, you will probably see an error message something like:
Error column "user"."recipes_count" does not exist
The problem is that you are trying to use an alias. This question deals with this problem:
Using an Alias in a WHERE clause
I hope you are more successful then me, but I decided to use a workaround after a log trial and error. I am sure there is a better way. If you manage to find it, please let me know.
(If you want to get back the list of user entities, without the user.recipes_count property, include the last .map as well.)
const users_without_recipes = await getRepository(User)
.createQueryBuilder('user')
.loadRelationCountAndMap('user.recipes_count', 'user.recipes')
.getMany();
return users_without_recipes
.filter((user) => user['user.recipes_count'] === 0)
.map(({recipes_count, ...otherProperties}) => otherProperties);

Query Combinaton

I am trying to build a query using asp.net core c#
https://www.reflectionit.nl/blog/2017/paging-in-asp-net-core-mvc-and-entityframework-core
I trying to do a filtering however I need the data from another table which have my unique id
var result = _context.UserRoles.Where(y => y.RoleId.Contains(selectedRoles.Id)); // Retrieve the the userid i have from another table with the selected roleid
var query = _context.Users.Where(x => //I have already tried contains, where join );
If there is a site where i can learn this query please recommend. "Join()" does not work as I am doing paging
a least two solutions (please note that I do not check the identity classes members, so the following is the "spirit" of the solution (you miss the select clauses) ):
var result = _context.UserRoles.
Where(y => selectedRoles.Contains(y.RoleId)).
Select(y => y.User);
or
var result = _context.UserRoles.
Where(y => selectedRoles.Contains(y.RoleId)).
Select(y => y.UserId);
query = _context.Users.
Where(x => result.Contains(x.Id));
That said, assuming that there is no UserRoles table exposed in Identity (v2), you probably want:
userManager.Users.
Where(u => u.Roles.Any(r => selectecRoles.Contains(r.RoleId)));
Up to you to instanciate the userManager.

Selecting Subcollections in Union-Query

I'm trying to select objects from the database with Entity Framework into an anonymous type. When using Union and Selecting a Subcollection, I get an exception:
System.ArgumentException: The 'Distinct' operation cannot be applied to the collection ResultType of the specified argument.
My model contains several types derived from BaseType. This base type has a reference to RefType which contains a collection of ItemType. The types derived from BaseType are stored in separate tables, thus the Union.
The query looks like this:
var q1 = ctx.Set<Type1>().Select(x => new { x.Id, x.Ref.Items });
var q2 = ctx.Set<Type2>().Select(x => new { x.Id, x.Ref.Items });
q1.Union(q2).ToList();
But to reproduce the error, you can even union queries of the same type, as long as you select a collection.
I would do the select after the union, but to union Type1, Type2, etc. I must cast them to BaseType, which is not allowed in LINQ-to-SQL.
Any way to do this in the same query?
The exception emerges from Entity Framework's query generation pipeline when the ExpressionConverter tries to translate the expression q1.Union(q2) into SQL.
In a valid query you'll see that EF adds a DISTINCT clause to the SQL query. A type with collection properties (x.Ref.Items) doesn't pass as a valid argument for a Distinct operation and EF throws the exception you see.
Unfortunately, using Concat instead of Union is not a valid work-around. EF will also throw an exception:
The nested query is not supported. Operation1='UnionAll' Operation2='MultiStreamNest'
Which means that it's simply not supported to concat nested queries containing types with collection properties.
So you have to do the Union in memory:
var result = q1.AsEnumerable() // Continues the query in memory
.Union(q2).ToList();
C# doesn't have any problem with equating anonymous types containing collections: it simply considers any collection member as unequal to another collections member. This does mean that the query can produce a collection containing non-unique results (same Id, same Items) which may not be expected when relying on Union's implicit Distinct.
I am not sure why, for some reason distinct is failing, maybe because it is anonymous type, and it is still IQuerable, I would suggest firing the query something like this
var q1 = ctx.Set<Type1>().Select(x => new { x.Id, x.Ref.Items }).ToList<object>();
var q2 = ctx.Set<Type2>().Select(x => new { x.Id, x.Ref.Items }).ToList<object>();
q1.Union(q2).ToList();
Note that in this case, Distinct will check for all properties equality, meaning if 2 objects have the same id but different items, both will be there.
if you don't care about distinct values, you can also use concat
if you care about distinct and first option didn't work, you can use group by and implement your own distinct,
something like this
var q1 = ctx.Set<Type1>().Select(x => new { Id = x.Id, Items =x.Ref.Items });
var q2 = ctx.Set<Type2>().Select(x => new { Id = x.Id, Items = x.Ref.Items });
//this will group by id, and select the first object items
var qFinal = q1.concat(q2).GroupBy(e => e.id)
.select(e => new {e.key, e.First().Items})
.ToList();
maybe you don't want First(), you can use whatever you want

how to select distinct with paging in entity framework?

how to select distinct with paging in entity framework?
i try to code below
var ll = _ctx.Cwzz_AccVouchMain.Select(v => v.Ddate).Distinct();
var l = ll.Skip(start).Take(limit).ToList();
but error:
must call orderBy method before skip
but my try
var ll = _ctx.Cwzz_AccVouchMain.Select(v => v.Ddate).Distinct();
var l = ll.OrderBy(v => v.Year).ThenBy(v => v.Month).ThenBy(v => v.Date).Skip(start).Take(limit).ToList();
error
ystem.NotSupportedException: LINQ to Entities not suppor type of "Date”。only support initial settlement,entity memeber,entity navagation property.
how to do?
Try this instead :
var ll = _ctx.Cwzz_AccVouchMain.Select(v => v.Ddate).Distinct();
var l = ll.OrderBy(v => v).Skip(start).Take(limit).ToList();
When you try to order by year, month and date, your query is not yet executed, and when .ToList() triggers it, it tries to build the appropriate sql query before sending it to your database server. However, your db has no clue about a Ddate.Year, Ddate.Month or Ddate.Date, because on the db side the Ddate field is a simple date, he doesn't understand your object with properties like the DateTime you use in C#.
If you wanted to order by month only (for example), you would have to trigger your query before that.

.Include in following query does not include really

var diaryEntries = (from entry in repository.GetQuery<OnlineDiary.Internal.Model.DiaryEntry>()
.Include("DiaryEntryGradeChangeLog")
.Include("DiaryEntryAction")
join diary in repository.GetQuery<OnlineDiary.Internal.Model.OnlineDiary>()
on entry.DiaryId equals diary.Id
group entry
by diary
into diaryEntriesGroup
select new { Diary = diaryEntriesGroup.Key,
DiaryEntry = diaryEntriesGroup.OrderByDescending(diaryEntry => diaryEntry.DateModified).FirstOrDefault(),
});
This query does not include "DiaryEntryGradeChangeLog" and "DiaryEntryAction" navigation properties, what is wrong in this query?
I have removed join from the query and corrected as per below, and still it populates nothing
var diaryEntries = from entry in repository.GetQuery<OnlineDiary.Internal.Model.DiaryEntry>()
.Include("DiaryEntryGradeChangeLog").Include("DiaryEntryAction")
.Where(e => 1 == 1)
group entry
by entry.OnlineDiary
into diaryEntryGroups
select
new { DiaryEntry = diaryEntryGroups.OrderByDescending(diaryEntry => diaryEntry.DateModified).FirstOrDefault() };
It will not. Include works only if the shape of the query does not change (by design). If you use this query it will work because the shape of the query is still same (OnlineDiary.Internal.Model.DiaryEntry):
var diaryEntries = (from entry in repository.GetQuery<OnlineDiary.Internal.Model.DiaryEntry>()
.Include("DiaryEntryGradeChangeLog")
.Include("DiaryEntryAction");
But once you use manual join, grouping or projection (select new { }) you have changed the shape of the query and all Include calls are skipped.
Edit:
You must use something like this (untested) to get related data:
var diaryEntries = from entry in repository.GetQuery<OnlineDiary.Internal.Model.DiaryEntry>()
group entry by entry.OnlineDiary into diaryEntryGroups
let data = diaryEntryGroups.OrderByDescending(diaryEntry => diaryEntry.DateModified).FirstOrDefault()
select new {
DiaryEntry = data,
GradeChangeLog = data.DiaryEntryGradeChangeLog,
Action = data.DiaryEntryAction
};
or any similar query where you manually populate property for relation in projection to anonymous or unmapped type.