Multiple left join using lambda syntax in Entity Framework - entity-framework

I have the following 3 tables
Courses
Id, SortOrder, CourseName, CourseArea, CourseFor
Students
Id, FullName
CourseStudents
CourseId, StudentId, CollegeId
Requirement:
Get all course students from the 'Medical' area for 'foreign' students available in College '125'. Include courses even if there are no students enrolled in it.
Working SQL query:
SELECT cr.Id, cr.CourseName, st.FullName
FROM dbo.Courses cr
LEFT JOIN dbo.CourseStudents cst ON cr.Id = cst.CourseId
AND cst.CollegeId = 125
LEFT JOIN dbo.Students st ON cst.StudentId = st.Id
WHERE
cr.CourseArea = 'Medical'
AND cr.CourseFor = 'Foreigner'
ORDER BY
cr.SortOrder, st.FullName
Can anyone help me with the lambda syntax (I tried GroupJoin)? While what I am looking for is the lambda syntax, the query syntax is also good to know.
UPDATE: I am very close, but still not complete
context.Courses
.GroupJoin(context.CourseStudents,
x => new { x.Id, CollegeId NOT IN COURSES TABLE :( },
y => new { Id = y.CourseId, y.CollegeId=125 },
(x, y) => new { Courses = x, CourseStudents = y })
.SelectMany(x => x.CourseStudents.DefaultIfEmpty(),
(x, y) => new { x.Courses, CourseStudents = y })
.GroupJoin(context.Students,
x => x.CourseStudents.StudentId,
y => y.Id,
(x, y) => new { CoursesCourseStudents = x, Students = y }
)
.SelectMany(x => x.Students.DefaultIfEmpty(),
(x, y) => new { x = x.CoursesCourseStudents, Students = y })
.Select(x => new
{
x.x.Courses.Id,
x.x.Courses.CourseName,
x.Students.FullName,
x.x.CourseStudents.CollegeId,
x.x.Courses.CourseFor,
x.x.Courses.CourseArea,
x.x.Courses.SortOrder
})
.Where(x => x.CourseFor == "Foreigner" && x.CourseArea == "Medical")
.OrderBy(x => x.SortOrder)
.ToList();

SOLUTION: I got it working by doing the following. See line 3 and 4.
context.Courses
.GroupJoin(context.CourseStudents,
x => new { x.Id, CollegeId=125 },
y => new { Id = y.CourseId, y.CollegeId },
(x, y) => new { Courses = x, CourseStudents = y })
.SelectMany(x => x.CourseStudents.DefaultIfEmpty(),
(x, y) => new { x.Courses, CourseStudents = y })
.GroupJoin(context.Students,
x => x.CourseStudents.StudentId,
y => y.Id,
(x, y) => new { CoursesCourseStudents = x, Students = y }
)
.SelectMany(x => x.Students.DefaultIfEmpty(),
(x, y) => new { x = x.CoursesCourseStudents, Students = y })
.Select(x => new
{
x.x.Courses.Id,
x.x.Courses.CourseName,
x.Students.FullName,
x.x.CourseStudents.CollegeId,
x.x.Courses.CourseFor,
x.x.Courses.CourseArea,
x.x.Courses.SortOrder
})
.Where(x => x.CourseFor == "Foreigner" && x.CourseArea == "Medical")
.OrderBy(x => x.SortOrder)
.ToList();

Related

Write from/group in method instead of query syntax in efcore

How can this query syntax be translated into method syntax?
var query =
from a in _dbContext.A
from b in a.B.DefaultIfEmpty()
from c in a.C.DefaultIfEmpty()
group new { b, c } by new
{
Id = a.Id.ToString(),
a.Name,
//...
} into g
select new SomeDto
{
Id = g.Key.Id,
Name = g.Key.Name,
//...
};
Well, this how it looks in Method Chain syntax:
var uglyQuery = _dbContext.A
.SelectMany(a => a.B.DefaultIfEmpty(), (a, b) => new { a, b })
.SelectMany(t => t.a.C.DefaultIfEmpty(), (t, c) => new { t, c })
.GroupBy(x => new
{
Id = x.t.a.Id.ToString(),
x.t.a.Name,
//...
}, x => new { x.t.b, x.c })
.Select(g => new SomeDto
{
Id = g.Key.Id,
Name = g.Key.Name,
//...
});
Note that this conversion is available in ReSharper in context menu Convert to LINQ method chain.

EF Core GroupBy query throws: could not be translated

Can anyone point me at the faux pas in this EF Core query?
var employers = await iqDbContext.Payrolls
.GroupBy(p => p.PayeScheme.Employer, p => p)
.Select(group => new EmployerViewModel
{
Id = group.Key.Id,
Name = group.Key.Name
})
.ToListAsync();
throws:
System.InvalidOperationException: The LINQ expression 'DbSet<Payroll>()
.Join(
inner: DbSet<PayeScheme>(),
outerKeySelector: p => EF.Property<Nullable<int>>(p, "PAYR_fk1_PAYE_SCHEME"),
innerKeySelector: p0 => EF.Property<Nullable<int>>(p0, "Id"),
resultSelector: (o, i) => new TransparentIdentifier<Payroll, PayeScheme>(
Outer = o,
Inner = i
))
.Join(
inner: DbSet<Employer>(),
outerKeySelector: p => EF.Property<Nullable<int>>(p.Inner, "PychFk1Employer"),
innerKeySelector: e => EF.Property<Nullable<int>>(e, "Id"),
resultSelector: (o, i) => new TransparentIdentifier<TransparentIdentifier<Payroll, PayeScheme>, Employer>(
Outer = o,
Inner = i
))
.Where(p => p.Inner != null && __accessiblePayrollIds_0.Contains(p.Outer.Outer.Id))
.GroupBy(
keySelector: p => p.Inner,
elementSelector: p => p.Outer.Outer)' 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'. See https://go.microsoft.com/fwlink/?
If you need correct grouping, you have to specify which columns to group, not whole object.
var employers = await iqDbContext.Payrolls
.GroupBy(p => new { p.PayeScheme.Employer.Id, p.PayeScheme.Employer.Name })
.Select(group => new EmployerViewModel
{
Id = group.Key.Id,
Name = group.Key.Name,
})
.ToListAsync();

Advice needed implementing direct and inverse Dijkstra algorithm in Scala/Spark

I'm trying to implement both direct Dijkstra and its inverse version (that is finding longest path instead of shortest ones) but I'm having some trouble because I'm getting infinite distances for not disconnected nodes in the weighted undirected graph (and zero distances in the inverse version).
So far, I trusted in and modified this implementation I found in web: [http://note.yuhc.me/2015/03/graphx-pregel-shortest-path/]
My implementations for both functions are as follows:
Direct Dijkstra:
// Implementation of Dijkstra algorithm using Pregel API
def computeMinDistance(u: VertexId, k1: VertexId): Double = {
val g: Graph[(Double, VertexId), Double] = this.uncertainGraph.mapVertices((id, _) =>
if (id == u) (0.0, id) else (Double.PositiveInfinity, id)
)
println("Computing Digkstra distance info fof id: " + u.toString)
val sssp: Graph[(Double, VertexId), Double] = g.pregel[(Double, VertexId)]((Double.PositiveInfinity, Long.MaxValue), Int.MaxValue, EdgeDirection.Either)(
(id, dist, newDist) => {
if(dist._1 < newDist._1) {
(dist._1, id)
} else {
(newDist._1, id)
}
},
triplet => { // Send Message
if (triplet.srcAttr._1 + triplet.attr < triplet.dstAttr._1) {
println("triplet.srcAttr._1 = " + triplet.srcAttr._1 .toString)
println("triplet.dstAttr._1 = " + triplet.dstAttr._1 .toString)
Iterator((triplet.dstId, (triplet.srcAttr._1 + triplet.attr, triplet.srcId)))
} else {
Iterator.empty
}
},
(a, b) => (math.min(a._1, b._1), a._2) // Merge Message
)
sssp.vertices.take(20).foreach(println(_))
sssp.vertices.filter(element => element._1 == k1).map(element => element._2._1).collect()(0)
}
Inverse Dijkstra:
def computeMaxDistance(node: VertexId, center: VertexId): Double = {
val g: Graph[(Double, VertexId), Double] = this.uncertainGraph.mapVertices((id, _) =>
if (id != node) (0.0, id) else (Double.PositiveInfinity, id)
)
val sslp: Graph[(Double, VertexId), Double] = g.pregel[(Double, VertexId)]((Double.PositiveInfinity, Long.MaxValue), Int.MaxValue, EdgeDirection.Either)(
(id, dist, newDist) => {
if(dist._1 > newDist._1) {
(dist._1, id)
} else {
(newDist._1, id)
}
},
triplet => { // Send Message
if (triplet.srcAttr._1 + triplet.attr > triplet.dstAttr._1) {
println("triplet.srcAttr._1 = " + triplet.srcAttr._1 .toString)
println("triplet.dstAttr._1 = " + triplet.dstAttr._1 .toString)
Iterator((triplet.dstId, (triplet.srcAttr._1 + triplet.attr, triplet.srcId)))
} else {
Iterator.empty
}
},
(a, b) => (math.max(a._1, b._1), a._2) // Merge Message
)
sslp.vertices.take(20).foreach(println(_))
sslp.vertices.filter(element => element._1 == center).map(element => element._2._1).collect()(0)
}
Any help deeply appreciated. I'm not really that experienced with Scala and Spark. Thanks in advance.

How to update the list only when certain conditions match

I'm doing the following to map and update a list:
if (colors.map(_.id).contains(updated.id)) {
val newColorList = updated :: colors.filterNot(s => s.id == updated.id)
SomeMethod(newColorList)
}
else {
this
}
The above works ok, However, I want to add a condition such as this: If, and only if, the updated quantity is 0 then also update the enddate to todays date.
I've done the following
if (color.map(_.id).contains(updated.id) && updated.quantity == 0) {
val newColorList = updated.copy(enddate = Instant.now().toEpochMilli) :: colors.filterNot(s => s.id == updated.id)
}
else if (color.map(_.id).contains(updated.id)) {
val newColorList = updated :: colors.filterNot(s => s.id == updated.id)
SomeMethod(newColorList)
}
else {
this
}
Is there a better way to do this than multiple if / else statements
something like
val hasColor=color.map(_.id).contains(updated.id)
newColorList = (hasColor,updated.quantity) match {
case (true,0) => updated.copy(enddate = Instant.now().toEpochMilli) :: colors.filterNot(s => s.id == updated.id)
case (true,_) => updated :: colors.filterNot(s => s.id == updated.id)
otherwise => this
}
You can use collect over the colors collection.
I assume there is a simple a case class Color like this
case class Color(id:String, quantity: Int, endDate : LocalDate)
And updated is also of type Color
import java.time.LocalDate
case class Color(id:String, quantity: Int, endDate : LocalDate)
val updated = Color("one", 0, LocalDate.now)
val colors = List(
Color("one", 1, LocalDate.now().minusMonths(2)),
Color("two", 2, LocalDate.now.minusMonths(4)),
Color("three", 3, LocalDate.now.minusMonths(6))
)
colors.collect{
case e if e.id == updated.id && updated.quantity == 0 =>
updated.copy(endDate = LocalDate.now) //add element to new list, call method
case e if e.id == updated.id => updated
case e => e
}
//List(Color(one,0,2018-12-06), Color(two,2,2018-08-06), Color(two,2,2018-06-06))
Hope this helps you.

Slick Queries With GroupBy

I'm new to Scala and Slick. I'm trying to run a query with a grouping by an object and creating two sequences for each object.
This is my code :
val firstQuery = {
for {
table1Id <- Table1.filter(_.someid === someid).map(_.someid)
table2 <- Table2.filter(_.someid === table1Id)
table3 <- Table3.filter(_.someId === someid)
_ <- {
Table4.filter(table4 => table4.someid === table3.someid &&
table4.name === "$$$")
}
table5 <- Table5.filter(_.someid === table2.someid)
} yield {
(table2, table3, table5)
}
}
val finalQuery = {
for {
(table6, firstQueryTuple) <- {
Table6 joinRight firstQuery on
((firstTable, secondTable) => firstTable.someid === secondTable._1.someid)
}
} yield {
(table6, firstQueryTuple._1, firstQueryTuple._2, firstQueryTuple._3)
}
}
dataBaseConnection.run(finalQuery.result).map { resultSequence =>
val grouping = resultSequence.groupBy { case (table6, table2, _, _) => (table6, table2) }
val groupingFiltered = grouping.map { case (table6table2tuple, sequence) => (table6table2tuple, sequence.map(_._4) (which is Table5), sequence.map(_._3) (which is table3)) }.toSeq
groupingFiltered match {
case tupleQuery if tupleQuery.nonEmpty => Success(tupleQuery.map(x => (x._1._2, x._1._1, x._2, x._3) ))
case _ => Failure(NonEmptyList(DBMissingRecordProblem(s"Error")))
}
}
Now what I need to achieve is this result
Seq[(Table2, Option[Table6], Seq[Table5], Seq[Table3])]
Is there a way to group by and achieve two sequences? I know it can be done for one sequence, I mean group by an object and achieve (object, Seq[anotherObject])
Please help me if you can.