问题
I'm having a very difficult time figuring out how to translate a simple SQL LEFT OUTER JOIN on multiple columns and a where clause into a working Linq-to-Entities query. There are only two tables. I need values for all rows from Table1, regardless of matches in Table2, but joining multiple columns is proving difficult. I also need to do a simple calculation in the query, but can't find that either. The query in SQL would look like this:
select t1.tableid1,
t1.tableid2,
t1.fieldvalue1,
t2.fieldvalue2,
isnull(t2.fieldvalue2,0) / t1.fieldvalue1 as calcvalue
t1.fieldvalue1 - isnull(t2.fieldvalue2,0) as calcvalue2
from table1 t1
left outer join table2 t2
on t1.tableid1 = t2.tableid1
and t1.tableid2 = t2.tableid2
and t1.tableid3 = t2.tableid3
where t1.tableid1 = @somevalue
I can do a single column left join, but I can't seem to find the proper syntax for multiple columns, not to mention not knowing how to add or subtract the values on the column. Below is my best guess but I'm getting a Type Expected error (after "new" and before the first opening parenthesis) for:
On New (t2.Field(Of String)("tableid1"), t2.Field(Of String)
Best Guess:
Dim query = From t1 In dtTable1 _
Group Join t2 In dtTable2 _
On New (t2.Field(Of String)("tableid1"), t2.Field(Of String)("tableid2")) Equals _
(t1.Field(Of String)("tableid1"), t1.Field(Of String)("tableid2")) _
Into t2outer = Group _
From t2 In t2outer.DefaultIfEmpty() _
Select New With _
{ _
.t1_tableid1 = t1.Field(Of String)("tableid1"), _
.t1_tableid2 = t1.Field(Of String)("tableid2"), _
.t1_fieldvalue1 = t1.Field(Of Integer)("fieldvalue1"))
.t2_fieldvalue2 = If(t2 Is Nothing, CType("0", Integer), t2.Field(Of Integer)("fieldvalue2"))
}
@Gert That got me close enough. I think the formatting was slightly off, as I had to add braces rather than parenthesis and it placed a space between the "Key" keyword and the id:
On New With {
Key .id1 = t2.Field(Of String)("tableid1"), _
Key .id2 = t2.Field(Of String)("tableid2")
} _
Equals _
New With {
Key .id1 = t1.Field(Of String)("tableid1"), _
Key .id2 = t1.Field(Of String)("tableid2")
} _
Thanks to Gert I was able to get this working as described in the SQL query. Please see below for what should be the full working representation of the SQL query:
Dim query = From t1 In dtTable1 _
Where (T1.Field(Of String)("fieldvalue1") = SOMEVALUE) _
Group Join t2 In dtTable2 _
On New (t2.Field(Of String)("tableid1"), t2.Field(Of String)("tableid2")) Equals _
(t1.Field(Of String)("tableid1"), t1.Field(Of String)("tableid2")) _
Into t2outer = Group _
From t2 In t2outer.DefaultIfEmpty() _
Select New With _
{ _
.t1_tableid1 = t1.Field(Of String)("tableid1"), _
.t1_tableid2 = t1.Field(Of String)("tableid2"), _
.t1_fieldvalue1 = t1.Field(Of Integer)("fieldvalue1"))
.t2_fieldvalue2 = If(t2 Is Nothing, CType("0", Integer), t2.Field(Of Integer)("fieldvalue2"))
.calcvalue = If(t2.Field(Of Integer)("fieldvalue2") Is Nothing, CType("0", Integer), t2.Field(Of Integer)("fieldvalue2")) _
/ CType(t1.Field(Of String)("fieldvalue1"), Integer),
.calcvalue2 = CType(t1.Field(Of String)("fieldvalue1"), Integer) _
- If(t2.Field(Of Integer)("fieldvalue2") Is Nothing, CType("0", Integer), t2.Field(Of Integer)("fieldvalue2")) _
}
回答1:
First, if you group on multiple fields, you are comparing anonymous types, so the part
On
New (t2.Field(Of String)("tableid1"), t2.Field(Of String)("tableid2"))
Equals
(t1.Field(Of String)("tableid1"), t1.Field(Of String)("tableid2"))
should be changed into the syntax for creating anonymous types (same as you use later in the query):
On
New With {.id1 = t2.Field(Of String)("tableid1"),
.id2 = t2.Field(Of String)("tableid2")}
Equals
New With {.id1 = t1.Field(Of String)("tableid1"),
.id2 = t1.Field(Of String)("tableid2")}
but
In C# this would be enough, because with anonymous types, the C# compiler uses equality based on values. For some reason however, with VB this is different, it uses reference equality. So the Equals
is always false
, because the first object is not the same object as the second.
Fortunately, VB has the Key
keyword that lets you indicate properties to be used when comparing anonymous objects. So the final syntax is:
On
New With {Key .id1 = t2.Field(Of String)("tableid1"),
Key. id2 = t2.Field(Of String)("tableid2")}
Equals
New With {Key .id1 = t1.Field(Of String)("tableid1"),
Key .id2 = t1.Field(Of String)("tableid2")}
回答2:
With entities you have a container and assess your tables by their name. This where clause filters results by a fields value and can be concatenated with more values. The parts in {} are for you to change to match your entities. This query is an IEnumerable(Of {your entity})
- great for databinding.
Dim query = model.{table name}.Where(Function(o) o.{column name} = {some value}).ToList()
来源:https://stackoverflow.com/questions/17257064/linq-to-entities-left-outer-join-with-where-clause-calculation-and-projection