I have this LINQ to Entity:
var de = from dm in _context.DamageEvents
where dm.StatusID == statusId
group dm by new { dm.ClientId, dm.Client.ClientName, dm.SiteId, dm.Site.Name, dm.SiteObjectId } into g
select new
{
g.Key.ClientId,
g.Key.ClientName,
g.Key.SiteId,
g.Key.Name,
g.Key.SiteObjectId,
icon = g.Select(i=>i.SiteObject.ObjectModel.ObjectType.Icon).FirstOrDefault()
};
How can I convert the linq above to from query syntax to method syntax.
Any idea how can I implement it?
something like this?
var de = _context.DamageEvents.Where(dm => dm.StatusID == statusId)
.GroupBy(dm => new {dm.ClientId, dm.Client.ClientName, dm.SiteId, dm.Site.Name, dm.SiteObjectId})
.Select(g => new
{
g.Key.ClientId,
g.Key.ClientName,
g.Key.SiteId,
g.Key.Name,
g.Key.SiteObjectId,
icon = g.Select(i => i.SiteObject.ObjectModel.ObjectType.Icon).FirstOrDefault()
});
Related
I have pretty simple LINQ expression
IQueryable<FreeBetDTO> records = UnitOfWork.FreeBets
.Include(f => f.FreeBetCategories)
.Include(f => f.FreeBetCards)
.Where(f => f.FreeBetCards.Any(cards => cards.UserId == request.UserId))
.Select(f => new FreeBetDTO
{
FreeBetId = f.FreeBetId
LineCategories = f.FreeBetCategories
.GroupBy(g => new { g.LineCategoryID, g.Title })
.Select(c =>
new LineCategoryDTO
{
LineCategoryID = c.Key.LineCategoryID,
Title = c.Key.Title
}).AsEnumerable()
});
When I am executing it I catch the error:
System.InvalidOperationException: Unable to translate collection subquery in projection since it uses 'Distinct' or 'Group By' operations and doesn't project key columns of all of it's tables which are required to generate results on client side. Missing column: t.ID. Either add column(s) to the projection or rewrite query to not use 'GroupBy'/'Distinct' operation.
at Microsoft.EntityFrameworkCore.Query.SqlExpressions.SelectExpression.
....
The problem is here .GroupBy(g => new { g.LineCategoryID, g.Title }). If I don't group records, the error disappears.
I was trying a lot of cases with GroupBy() and Distinct(). But can't understand why this is happening. Because I just need grouping like this.
Error message says that you have to include Id column in projection. But you can't do that with GroupBy. So rewrite query into two steps (removed not needed includes):
var rawRecords = UnitOfWork.FreeBets
.Where(f => f.FreeBetCards.Any(cards => cards.UserId == request.UserId))
.Select(f => new
{
FreeBetId = f.FreeBetId
LineCategories = f.FreeBetCategories.Select(c => new { c.Id, c.LineCategoryID, c.Title })
.ToList()
})
.AsEnumerable();
var records = rawRecords
.Select(f => new FreeBetDTO
{
FreeBetId = f.FreeBetId
LineCategories = f.LineCategories.GroupBy(g => new { g.LineCategoryID, g.Title })
.Select(c =>
new LineCategoryDTO
{
LineCategoryID = c.Key.LineCategoryID,
Title = c.Key.Title
})
});
Similar query, but more optimal:
var query =
from f in UnitOfWork.FreeBets
from c in f.FreeBetCards
where f.FreeBetCards.Any(cards => cards.UserId == request.UserId)
select new { f.FreeBetId, c.LineCategoryID, c.Title };
query = query.Distinct();
var records = query.AsEnumerable()
.GroupBy(f => f.FreeBetId)
.Select(g => new FreeBetDTO
{
FreeBetId = g.Key
LineCategories = g.Select(c =>
new LineCategoryDTO
{
LineCategoryID = c.LineCategoryID,
Title = c.Title
})
.AsEnumerable()
});
Why am I getting only one entry in DownTimeDetails list even though in Data we have 3 entries.
VehicleEventDetails Res = dbEntity.DownTimeHeaders
.Join(dbEntity.DownTimeDetails, dth => dth.DownTimeHeaderID, dtd => dtd.DownTimeHeaderID, (dth, dtd) => new { dth, dtd })
.Where(x => x.dth.DownTimeHeaderID == 42)
.GroupBy(gx => gx.dtd.DownTimeDetailID)
.Select(t => new VehicleEventDetails()
{
BookingId = t.Select(a => a.dth.BookingId).FirstOrDefault(),
DownTimeDetails = t.Select(ab => new DownTimeDetails
{
LocalDTStartTime = (DateTime)ab.dtd.LocalDTStartTime,
LocalDTEndTime = (DateTime)ab.dtd.LocalDTEndTime,
CalculatedEventDTReason = ab.dtd.CalculatedEventDTReason,
CalculatedEventDTInMinutes = (int)ab.dtd.CalculatedEventDT,
}).ToList()
}).FirstOrDefault();
You are looking for something like this:
VehicleEventDetails Res = dbEntity.DownTimeHeaders
.Where(x => x.DownTimeHeaderID == 42)
.Select(x => new VehicleEventDetails
{
BookingId = x.BookingId,
DownTimeDetails = x.DownTimeDetails
.Select(dtd=> new DownTimeDetails
{
LocalDTStartTime = (DateTime)dtd.LocalDTStartTime,
LocalDTEndTime = (DateTime)dtd.LocalDTEndTime,
CalculatedEventDTReason = dtd.CalculatedEventDTReason,
CalculatedEventDTInMinutes = (int)dtd.CalculatedEventDT,
})
.ToList()
})
.FirstOrDefault();
Notes:
Using .Join is an anti-Entity Framework pattern. Always try to use navigation properties, they exist for a reason.
Don't use .GroupBy unless you actually need a group. You don't want any grouping in this query.
As a general note, try not to make the expression variable names so confusing.
This is my code that retrieves information from the database about a list of Printer Drivers. The table has a list of Printer Drivers, as well as what servers they were found on.
public List<PrinterDrivers> GetPrinterDriversFromCache()
{
using (dbPrintSimpleDataContext db = new dbPrintSimpleDataContext())
{
var q = from p in db.GetTable<tblPrinterDriverCache>()
where p.CacheGUID == mostRecentCacheID()
group p by p.PrinterDriver into g
select new PrinterDrivers
{
DriverName = g.Key,
InstalledOn = g.Where(x => x.PrinterDriver == g.Key).Select(x => x.PrinterServer).ToList(),
Usable = (g.Count() == Properties.Settings.Default.PrintServers.Count)
};
return q.ToList();
}
}
What I am trying to return is a List that contains a property that has a List in it that contains what servers that printer driver exists on. I think that I'm up against the limit of my current LINQ SQL knowledge :(
The resultant List should contain:
DriverName = Printer driver name, in this case the group key (string)
InstalledOn = List (containing the list of servers that this printer driver was found on)
Usable = A simple bool check if the servers that it was found on is the same amount as the servers we have in the preferences file.
Thanks for the help :)
Try this:
LINQ Lambda, Group by with list
The problem is that Linq does not know about ToList. Only part of the entire query is executed on the server as there is an extra ToList call before the final ToList call (Untested code below)
public List<PrinterDrivers> GetPrinterDriversFromCache()
{
using (dbPrintSimpleDataContext db = new dbPrintSimpleDataContext())
{
var q = (from p in db.GetTable<tblPrinterDriverCache>()
where p.CacheGUID == mostRecentCacheID()
group p by p.PrinterDriver.DriverName into g
select g
).ToList().Select(g => new PrinterDrivers
{
DriverName = g.Key,
InstalledOn = g.Where(x => x.PrinterDriver == g.Key).Select(x => x.PrinterServer).ToList(),
Usable = (g.Count() == Properties.Settings.Default.PrintServers.Count)
});
return q.ToList();
}
}
Translating the same pattern from the answer I linked, yours would be:
var q = db.GetTable<tblPrinterDriverCache>()
.Where(p => p.CacheGUID == mostRecentCacheID())
.Select(o => new { DriverName = o.DriverName, PrintServer = o.PrintServer })
.GroupBy(g => g.DriverName)
.ToList()
.Select(g => new PrinterDrivers
{
DriverName = g.Key,
InstalledOn = g.Select(p => p.PrinterServer).ToList(),
Usable = (g.Count() == Properties.Settings.Default.PrintServers.Count)
}
)
.ToList();
I am using LINQ to entitiy in my project.
I have this LINQ:
var result = (from inspArch in inspectionArchives
from inspAuth in inspArch.InspectionAuthority
select new
{
Id = inspArch.Id,
clientId = inspArch.CustomerId,
authId = inspAuth.Id
}).ToList();
After LINQ is executed result has this value :
Is there any elegant way (for example using LINQ or change above existing LINQ) to create from the list above, new list like that:
I haven't built this to see if it compiles, but this should work. You need to aggregate the Id and AuthId fields.
var result = (from inspArch in inspectionArchives
from inspAuth in inspArch.InspectionAuthority
select new
{
Id = inspArch.Id,
clientId = inspArch.CustomerId,
authId = inspAuth.Id
})
.GroupBy(g => g.clientId)
.select(s => new {
Id = string.Join(",", s.Select(ss => ss.Id.ToString())),
ClientId = s.Key,
AuthId = string.Join(",", s.Select(ss => ss.authId.ToString()).Distinct()),
}).ToList();
You need group by and you can apply String.Join on the resulting IGrouping:-
var result = (from inspArch in inspectionArchives
from inspAuth in inspArch.InspectionAuthority
group new { inspArch, inspAuth } by inspArch.CustomerId into g
select new
{
Id = String.Join(",",g.Select(x => x.inspArch.Id),
clientId = x.Key,
authId = String.Join(",",g.Select(x => x.inspAuth.Id)
}).ToList();
The tricky part here is to group both objects i.e. new { inspArch, inspAuth } because we need to access properties from both.
Update:
Since this is entity framework, it won't be able to translate the method String.Join to SQL, so we can bring back the grouped object to memory using AsEnumerable and then project it like this:-
var result = (from inspArch in inspectionArchives
from inspAuth in inspArch.InspectionAuthority
group new { inspArch, inspAuth } by inspArch.CustomerId into g
select g).AsEnumerable()
.Select(g => new
{
Id = String.Join(",",g.Select(x => x.inspArch.Id),
clientId = x.Key,
authId = String.Join(",",g.Select(x => x.inspAuth.Id)
}).ToList();
How this LINQ Query syntax:
var city = from c in _db.SubCategories where c.KategorijaID == stateID select new { c.PodKategorijaID, c.NazivPodKategorije };
change to LINQ Method syntax?
Example
This is LINQ Query syntax:
using (var context = new SchoolDBEntities())
{
var L2EQuery = from st in context.Students
where st.StudentName == "Bill"
select st;
var student = L2EQuery.FirstOrDefault<Student>();
}
and this is LINQ Method syntax:
//Querying with LINQ to Entities
using (var context = new SchoolDBEntities())
{
var L2EQuery = context.Students.where(s => s.StudentName == "Bill");
var student = L2EQuery.FirstOrDefault<Student>();
}
var city = _db.SubCategories.Where(c => c.KategorijaID == stateID)
.Select(c => new { c.PodKategorijaID, c.NazivPodKategorije });
var city = _db.SubCategories.Where(c => c.KategorijaID == stateID)
.Select(c => new { c.PodKategorijaID, c.NazivPodKategorije });