How to dynamically change property in lambda expression - entity-framework

I have written a function below. In second line of code I have written "Where(t => t.PickTicket == EmployeeId)". I want to change (t.PickTicket) property dynamically. How can I achieve this??? And how can I make below code more better and shorter?
Thanks in advance.
public List<TicketJoin> GetTicketJoin(string EmployeeId)
{
var query = _context.tickets.Where(t => t.PickTicket == EmployeeId).Join
(
_context.employees, t => t.CreatedBy, e => e.EmployeeId, (t, e) => new
{
t.Id,
e.Employee_Name,
e.Department,
e.Location,
t.Subject,
t.MainCatId,
t.SubCatId,
t.PickTicket,
t.Status,
t.CreateDate
}).Join
(
_context.MainCategory, t => t.MainCatId, m => m.MainCatId, (t, m) => new
{
t.Id,
t.Employee_Name,
t.Department,
t.Location,
t.Subject,
t.MainCatId,
m.MainCatName,
t.SubCatId,
t.PickTicket,
t.Status,
t.CreateDate
}).Join
(
_context.SubCategory, t => t.SubCatId, s => s.SubCatId, (t, s) => new
{
t.Id,
t.Employee_Name,
t.Department,
t.Location,
t.Subject,
t.MainCatId,
t.MainCatName,
t.SubCatId,
s.SubCatName,
t.PickTicket,
t.Status,
t.CreateDate
}).ToList();
var TicketJoin = query.ToList().Select(r => new TicketJoin
{
EmployeeName = r.Employee_Name,
Subject = r.Subject,
Location = r.Location,
MainCatName = r.MainCatName,
SubCatName = r.SubCatName,
CreateDate = r.CreateDate
}).ToList();
return TicketJoin;
}
}

How can I make it shorter?
By using database view to hide all those joins. In such case you will have have only simple query with single condition.
How can I make it dynamic?
Do you really need it dynamic? You can just do this:
public List<TicketJoin> GetTicketJoin(Expression<Func<Ticket, bool>> condition)
{
var query = _context.Tickets.Where(condition)...
}
and call it
var result = this.GetTicketJoin(t => t.PickTicket == employeeId);
It is not fully dynamic but it allows calling code specifying the condition.
Fully dynamic solution where you would be able to pass the property name as string requires using Expression trees to build query. There also used to be Dynamic-Linq library for this purpose.

Related

EF Core Filtered Include with Select

Sorry for my English.
I ran into a mistake with EF Core 5.0.9 when is use Filtered Include and Select in same time and i don't know is a bug or feature. :)
return await _dbContext.User
.Where(u => !u.TOROLT)
.Where(u => ids.Contains(u.Id))
.Include(u => u.EventUsers.Where(eu => !eu.TOROLT && eu.EventId == eventId))
.Select(u => new UserDropDownDtoWithInviteData
{
Id = u.Id,
FirstName = u.FirstName,
LastName = u.LastName,
EventUserId = u.EventUsers.First().Id,
IsCelebrated = u.EventUsers.First().IsCelebrated,
IsEventAdmin = u.EventUsers.First().IsEventAdmin,
IsInviteAccepted = u.EventUsers.First().IsInviteAccepted,
IsInvited = u.EventUsers.First().IsInvited,
})
.ToListAsync();
In this time the first elements is select is not from filtered include just from a normal include. SQL Script from Profiler:
SELECT
[u].[Id],
[u].[FirstName],
[u].[LastName],
(
SELECT TOP(1) [e].[Id]
FROM [dbo].[EventUser] AS [e]
WHERE [u].[Id] = [e].[UserId]) AS [EventUserId],
(
SELECT TOP(1) [e0].[IsCelebrated]
FROM [dbo].[EventUser] AS [e0]
WHERE [u].[Id] = [e0].[UserId]) AS [IsCelebrated],
(
SELECT TOP(1) [e1].[IsEventAdmin]
FROM [dbo].[EventUser] AS [e1]
WHERE [u].[Id] = [e1].[UserId]) AS [IsEventAdmin],
(
SELECT TOP(1) [e2].[IsInviteAccepted]
FROM [dbo].[EventUser] AS [e2]
WHERE [u].[Id] = [e2].[UserId]) AS [IsInviteAccepted],
(
SELECT TOP(1) [e3].[IsInvited]
FROM [dbo].[EventUser] AS [e3]
WHERE [u].[Id] = [e3].[UserId]) AS [IsInvited]
FROM [dbo].[User] AS [u]
WHERE ([u].[TOROLT] <> CAST(1 AS bit))
AND [u].[Id] IN (2, 1, 3, 4, 5)
But if is separate filtered include and select, than it work's fine, but this select complete record and not the part of him from database:
var a = await _dbContext.User
.Where(u => !u.TOROLT)
.Where(u => ids.Contains(u.Id))
.Include(u => u.EventUsers.Where(eu => !eu.TOROLT && eu.EventId == eventId))
.ToListAsync();
return a.Select(u => new UserDropDownDtoWithInviteData
{
Id = u.Id,
FirstName = u.FirstName,
LastName = u.LastName,
EventUserId = u.EventUsers.First().Id,
IsCelebrated = u.EventUsers.First().IsCelebrated,
IsEventAdmin = u.EventUsers.First().IsEventAdmin,
IsInviteAccepted = u.EventUsers.First().IsInviteAccepted,
IsInvited = u.EventUsers.First().IsInvited,
})
.ToList();
Any idea why is this, and how can i solve solve this?
THX
Include is completely ignored if you have custom projection Select, so your filter will be also ignored. It is not a bug, Include works only when you get whole entity from query.
Anyway consider to rewrite your query:
var query =
from u in _dbContext.User
where !u.TOROLT && ids.Contains(u.Id)
from eu in u.EventUsers.Where(eu => !eu.TOROLT && eu.EventId == eventId)
.Take(1)
.DefaultIfEmpty()
select new UserDropDownDtoWithInviteData
{
Id = u.Id,
FirstName = u.FirstName,
LastName = u.LastName,
EventUserId = eu.Id,
IsCelebrated = eu.IsCelebrated,
IsEventAdmin = eu.IsEventAdmin,
IsInviteAccepted = eu.IsInviteAccepted,
IsInvited = eu.IsInvited,
};
var result = await query.ToListAsync();

EF Core - Order by on filtered left join properties

I would like to generate this SQL query in entity framework
SELECT FunctionalAssets.Name, FunctionalAssetStructurePath.Path
FROM FunctionalAssets
LEFT JOIN FunctionalAssetStructurePath ON FunctionalAssets.Id =
FunctionalAssetStructurePath.FunctionalAssetId AND
FunctionalAssetStructurePath.StructureConfigurationId = 'A8A41B14-0A35-45D3-2A2B-08D904A3CD0B'
ORDER BY FunctionalAssetStructurePath.Path, FunctionalAssets.Name
OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY
I try with this code (but I know that it won't work)
var result = context.FunctionalAssets
.Include(x => x.FunctionalAssetsStructurePaths.Any(y => y.StructureConfigurationId == configurationId))
.OrderBy(x => x.FunctionalAssetsStructurePaths.Select(y => y.Path))
.ThenBy(x => x.Name)
.Skip(0)
.Take(100)
.AsNoTracking()
.Select(x => x.Name)
.ToList();
Have you an idea how to do this ?
Thank you
I found a solution.
var result = context.FunctionalAssets
.GroupJoin(context.FunctionalAssetStructurePathModel.Where(y => y.StructureConfigurationId == 'A8A41B14-0A35-45D3-2A2B-08D904A3CD0B'),
fa => fa.Id,
fasp => fasp.FunctionalAssetId,
(fa, fasp) => new { fa, fasp })
.SelectMany(x => x.fasp.DefaultIfEmpty(), (x, p) => new { x.fa, p.Path })
.OrderBy(x => x.Path)
.Skip(0)
.Take(100)
.ToList();
And the generated SQL query is :
SELECT [f].[Id], [f].[Name], [t].[Path]
FROM [FunctionalAssets] AS [f]
LEFT JOIN (
SELECT [f0].[FunctionalAssetId], [f0].[Path]
FROM [FunctionalAssetStructurePath] AS [f0]
WHERE [f0].[StructureConfigurationId] = 'A8A41B14-0A35-45D3-2A2B-08D904A3CD0B'
) AS [t] ON [f].[Id] = [t].[FunctionalAssetId]
ORDER BY [t].[Path]
OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY

Ef core 5 many to many filter

This is my query
public async Task<IEnumerable<Menu>> GetMenuByRolesAsync(string[] roles)
{
var result= await _context.Menus//.Include(o => o.Parent)
.Include(m => m.Childrens)
.ThenInclude(m => m.Childrens)
.Include(m => m.Roles.Where(r => roles.Contains(r.Name))) --it is not filtering basd on roles
.Where(m => m.ParentId == null)
.ToListAsync();
}
It is generating below query
-- #__roles_0='System.String[]' (DbType = Object)
SELECT m.id, m.icon, m.name, m.parent_id, m.url, t.role_id, t.menu_id, t.id, t.concurrency_stamp, t.name, t.normalized_name
FROM security.menu AS m
LEFT JOIN (
SELECT r.role_id, r.menu_id, r0.id, r0.concurrency_stamp, r0.name, r0.normalized_name
FROM security.role_menu AS r
INNER JOIN security.role AS r0 ON r.role_id = r0.id
WHERE r0.name = ANY (#__roles_0) OR ((r0.name IS NULL) AND (array_position(#__roles_0, NULL) IS NOT NULL))
) AS t ON m.id = t.menu_id
WHERE (m.parent_id IS NULL)
ORDER BY m.id, t.role_id, t.menu_id, t.id
This is Many to many configuration
// many to many
builder.HasMany(r => r.Menus)
.WithMany(r => r.Roles)
.UsingEntity<RoleMenu>(
j => j
.HasOne(rm => rm.Menu)
.WithMany(m => m.RoleMenus)
.HasForeignKey(rm => rm.MenuId),
j => j
.HasOne(rm => rm.Role)
.WithMany(r => r.RoleMenus)
.HasForeignKey(rm => rm.RoleId),
j =>
{
j.ToTable("role_menu", schema: "security");
j.HasKey(rm => new { rm.RoleId, rm.MenuId });
});
i need to filter menus based on roles..But it is not filtering based on roles..Am getting all the roles..I checked generated query..Please let me know what is the issue..
You are mixing filtered include with entity filtering.
Filtered include
.Include(m => m.Roles.Where(r => roles.Contains(r.Name)))
just filters the items in the related collection (menu roles in this case).
In order to filter the entity set (menus in this case), you need to replace it with the usual Where oparator, which for the desired filtering will be
.Where(m => m.Roles.Any(r => roles.Contains(r.Name)))

Convert SQL Command to lambda expression

If possible, please guide me how to convert my SQL command to lambda expression
select d.* from takcontractdet d where d.contractid=pubcontractid and d.personid in
(
select p.id from personel p where p.parentid=pubparentid)
)
Two query
var personelIds = Personels.Where(p => p.ParentId == pubparentid)
.Select(e => e.Id).ToList();
var result = Takcontractdets.Where(d => d.contractid == pubcontractid &&
personelIds.Contains(d.PersonId)).ToList();
One query
var result = Takcontractdets.Where(d => d.contractid == pubcontractid &&
Personels.Where(p => p.ParentId == pubparentid)
.Select(e => e.Id).ToList().Contains(d.PersonId))
.ToList();

Entity Framework Core: combining multiple result objects into one collection

I am facing this linq query for entity framework core (2.0).
var result = await dbContext.Table1DbSet
.Where(t1e => t1e.Id == id && t1e.Id2 == id2)
.Select
(
t1e =>
t1e.Table2NavPropICollection.Select
(
t2e => new
{
singleObject = t2e.Table3NavPropObject.TargetObject,
enumerable1 = t2e.Table3NavPropObject.Table4NavPropObject.Table5NavPropICollection.Select(t5e => t5e.TargetObject),
enumerable2 = t2e.Table3NavPropObject.Table6NavPropObject.Table7NavPropICollection.Select(t7e => t7e.TargetObject),
enumerable3 = t2e.Table3NavPropObject.Table8NavPropICollection.SelectMany(t8e => t8e.Table9NavPropICollection.Select(t9e => t9e.TargetObject))
}
)
)
.ToListAsync();
The goal is to query all the referenced instances of TargetObject which is referenced accros ~ 10 different tables.
Currently it returns an IEnumerable where the anonymous object contains the properties singleObject, enumerable1, enumerable2, enumerable3. All those properties are of type TargetObject or IEnumerable.
Can I, and how, rewrite the query to not return anonymous objects but just an IEnumerable containing all the values?
For some reason the compiler won't let me iterate over the anonymous collection and flatten it manually.
This should do the trick if you want one array per t1e row.
var result = await dbContext.Table1DbSet
.Where(t1e => t1e.Id == id && t1e.Id2 == id2)
.Select
(
t1e =>
t1e.Table2NavPropICollection.Select
(
t2e => new[] {t2e.Table3NavPropObject.TargetObject}.Concat(
t2e.Table3NavPropObject.Table4NavPropObject.Table5NavPropICollection.Select(t5e => t5e.TargetObject)).Concat(
t2e.Table3NavPropObject.Table6NavPropObject.Table7NavPropICollection.Select(t7e => t7e.TargetObject)).Concat(
t2e.Table3NavPropObject.Table8NavPropICollection.SelectMany(t8e => t8e.Table9NavPropICollection.Select(t9e => t9e.TargetObject)))
)
)
.ToListAsync();
If you want it completely flat, you'll want to switch that Select to a SelectMany().
var result = await dbContext.Table1DbSet
.Where(t1e => t1e.Id == id && t1e.Id2 == id2)
.SelectMany
(
t1e =>
t1e.Table2NavPropICollection.Select
(
t2e => new[] {t2e.Table3NavPropObject.TargetObject}.Concat(
t2e.Table3NavPropObject.Table4NavPropObject.Table5NavPropICollection.Select(t5e => t5e.TargetObject)).Concat(
t2e.Table3NavPropObject.Table6NavPropObject.Table7NavPropICollection.Select(t7e => t7e.TargetObject)).Concat(
t2e.Table3NavPropObject.Table8NavPropICollection.SelectMany(t8e => t8e.Table9NavPropICollection.Select(t9e => t9e.TargetObject)))
)
)
.ToListAsync();
Is Add and/or Concat a possible solution?
PoC:
var a = new [] { 1,2,3 };
var result = new {
firstA = a.First(),
otherAs = a,
backwards = a.Reverse()
};
var final = new List<int>();
final.Add(result.firstA);
final.Concat(result.otherAs.Concat(result.backwards))
.Dump();