LINQ中LEFT JOIN加入实体?

时间:2013-10-14 08:43:53

标签: c# linq entity-framework entity-framework-4 linq-to-entities

我正在尝试LINQ到实体。

我遇到以下问题: 我希望它能做到这一点:

SELECT 
     T_Benutzer.BE_User
    ,T_Benutzer_Benutzergruppen.BEBG_BE
FROM T_Benutzer

LEFT JOIN T_Benutzer_Benutzergruppen
    ON T_Benutzer_Benutzergruppen.BEBG_BE = T_Benutzer.BE_ID 

我最接近的是:

        var lol = (
            from u in Repo.T_Benutzer

            //where u.BE_ID == 1
            from o in Repo.T_Benutzer_Benutzergruppen.DefaultIfEmpty()
                // on u.BE_ID equals o.BEBG_BE

            where (u.BE_ID == o.BEBG_BE || o.BEBG_BE == null)

            //join bg in Repo.T_Benutzergruppen.DefaultIfEmpty()
            //    on o.BEBG_BG equals bg.ID

            //where bg.ID == 899 

            orderby
                u.BE_Name ascending
                //, bg.Name descending

            //select u 
            select new
            {
                 u.BE_User
                ,o.BEBG_BG
                //, bg.Name 
            }
         ).ToList();

但是这会产生与内连接相同的结果,而不是左连接 而且,它创建了这个完全疯狂的SQL:

SELECT 
     [Extent1].[BE_ID] AS [BE_ID]
    ,[Extent1].[BE_User] AS [BE_User]
    ,[Join1].[BEBG_BG] AS [BEBG_BG]
FROM  [dbo].[T_Benutzer] AS [Extent1]

CROSS JOIN  
(
    SELECT 
         [Extent2].[BEBG_BE] AS [BEBG_BE]
        ,[Extent2].[BEBG_BG] AS [BEBG_BG]
    FROM ( SELECT 1 AS X ) AS [SingleRowTable1]
    LEFT OUTER JOIN [dbo].[T_Benutzer_Benutzergruppen] AS [Extent2] 
        ON 1 = 1 
) AS [Join1]

WHERE [Extent1].[BE_ID] = [Join1].[BEBG_BE] 
OR [Join1].[BEBG_BE] IS NULL

ORDER BY [Extent1].[BE_Name] ASC

如何以一种方式在LINQ-2实体中进行左连接 另一个人仍然可以理解该代码中正在做什么?

,最好 - 最好是生成的SQL看起来像:

SELECT 
     T_Benutzer.BE_User
    ,T_Benutzer_Benutzergruppen.BEBG_BE
FROM T_Benutzer

LEFT JOIN T_Benutzer_Benutzergruppen
    ON T_Benutzer_Benutzergruppen.BEBG_BE = T_Benutzer.BE_ID 

5 个答案:

答案 0 :(得分:113)

啊,自己动手吧。
LINQ-2实体的怪癖和夸克 这看起来最容易理解:

var query2 = (
    from users in Repo.T_Benutzer
    from mappings in Repo.T_Benutzer_Benutzergruppen
        .Where(mapping => mapping.BEBG_BE == users.BE_ID).DefaultIfEmpty()
    from groups in Repo.T_Benutzergruppen
        .Where(gruppe => gruppe.ID == mappings.BEBG_BG).DefaultIfEmpty()
    //where users.BE_Name.Contains(keyword)
    // //|| mappings.BEBG_BE.Equals(666)  
    //|| mappings.BEBG_BE == 666 
    //|| groups.Name.Contains(keyword)

    select new
    {
         UserId = users.BE_ID
        ,UserName = users.BE_User
        ,UserGroupId = mappings.BEBG_BG
        ,GroupName = groups.Name
    }

);


var xy = (query2).ToList();

删除.DefaultIfEmpty(),即可获得内部联接 这就是我一直在寻找的。

答案 1 :(得分:38)

您可以阅读我为LINQ here

中的联接撰写的文章
var query = 
from  u in Repo.T_Benutzer
join bg in Repo.T_Benutzer_Benutzergruppen
    on u.BE_ID equals bg.BEBG_BE
into temp
from j in temp.DefaultIfEmpty()
select new
{
    BE_User = u.BE_User,
    BEBG_BG = (int?)j.BEBG_BG// == null ? -1 : j.BEBG_BG
            //, bg.Name 
}

以下是使用扩展方法的等效方法:

var query = 
Repo.T_Benutzer
.GroupJoin
(
    Repo.T_Benutzer_Benutzergruppen,
    x=>x.BE_ID,
    x=>x.BEBG_BE,
    (o,i)=>new {o,i}
)
.SelectMany
(
    x => x.i.DefaultIfEmpty(),
    (o,i) => new
    {
        BE_User = o.o.BE_User,
        BEBG_BG = (int?)i.BEBG_BG
    }
);

答案 2 :(得分:5)

可能是我后来回答但是现在我正面对这个...如果有帮助还有一个解决方案(我解决它的方式)。

import squants.space.{Radians, Meters}
import squants.{Angle, Length}

object Geospatial {
  case class Longitude(angle: Angle) {
    require(
      (Radians(-Math.PI) <= angle) && (angle < Radians(Math.PI)),
      "angle.inRadians must be greater than or equal to -Math.PI and less than Math.PI"
    )
  }
  case class Latitude(angle: Angle) {
    require(
      (Radians(-(Math.PI * 0.5d)) <= angle) && (angle < Radians(Math.PI * 0.5d)),
      "angle.inRadians must be greater than or equal to -(Math.PI * 0.5d) and less than (Math.PI * 0.5d)"
    )
  }
  case class Distance(length: Length) {
    require(
      Meters(0.0d) <= length,
      "length.inMeters must be greater than or equal to 0.0d"
    )
  }
  case class Bearing(angle: Angle) {
    require(
      (Radians(0.0d) <= angle) && (angle < Radians(Math.PI * 2.0d)),
      "angle.inRadians must be greater than or equal to 0.0d and less than (Math.PI * 2.0d)"
    )
  }

  case class Coordinate(longitude: Longitude, latitude: Latitude)

  def calculateDistance(
    coordinate1: Coordinate,
    coordinate2: Coordinate
  ): (Distance, Bearing, Bearing) = {
    def calculateDistanceUsingLegacyCodeRifeWithDoubles(
      coordinate1LongitudeInRadians: Double,
      coordinate1LatitudeInRadians: Double,
      coordinate2LongitudeInRadians: Double,
      coordinate2LatitudeInRadians: Double
    ): (Double, Double, Double) = {
      //Legacy code encapsulated here only works with meters and radians
      //returns (distance, initial bearing, final bearing)
      (1.0d, 1.0d, 2.0d) //TODO: replace with real calculation results
    }
    val (coordinate1InRadians, coordinate2InRadians) = (
      (coordinate1.longitude.angle.toRadians, coordinate1.latitude.angle.toRadians),
      (coordinate2.longitude.angle.toRadians, coordinate2.latitude.angle.toRadians)
    )
    val (distanceInMeters, bearingInitialInRadians, bearingFinalInRadians) =
      calculateDistanceUsingLegacyCodeRifeWithDoubles(
        coordinate1InRadians._1,
        coordinate1InRadians._2,
        coordinate2InRadians._1,
        coordinate2InRadians._2
      )
    (
      Distance(Meters(distanceInMeters)),
      Bearing(Radians(bearingInitialInRadians)),
      Bearing(Radians(bearingFinalInRadians))
    )
  }

  def calculateCoordinate(
    coordinate1: Coordinate,
    bearingInitial: Bearing,
    distance: Distance
  ): Coordinate = {
    def calculateCoordinateUsingLegacyCodeRifeWithDoubles(
      coordinate1Longitude: Double,
      coordinate1Latitude: Double,
      bearingInitialInRadians: Double,
      distanceInMeters: Double
    ): (Double, Double) = {
      //Legacy code encapsulated here only works with meters and radians
      //returns (longitude, latitude)
      (-1.0d, 1.0d) //TODO: replace with real calculation results
    }
    val (coordinate1InRadians, bearingInitialInRadians, distanceInMeters) = (
      (coordinate1.longitude.angle.toRadians, coordinate1.latitude.angle.toRadians),
      bearingInitial.angle.toRadians,
      distance.length.toMeters
    )
    val (longitude, latitude) =
      calculateCoordinateUsingLegacyCodeRifeWithDoubles(
        coordinate1InRadians._1,
        coordinate1InRadians._2,
        bearingInitialInRadians,
        distanceInMeters
      )
    Coordinate(Longitude(Radians(longitude)), Latitude(Radians(latitude)))
  }
}

顺便说一句,我尝试使用Stefan Steiger代码也很有帮助,但是速度慢了。

答案 3 :(得分:3)

简单的方法是使用Let关键字。这对我有用。

from AItem in Db.A
Let BItem = Db.B.Where(x => x.id == AItem.id ).FirstOrDefault() 
Where SomeCondition
Select new YourViewModel
{
    X1 = AItem.a,
    X2 = AItem.b,
    X3 = BItem.c
}

这是左连接的模拟。如果B表中的每个项目都与A项目不匹配,则BItem返回null

答案 4 :(得分:1)

您不仅可以在实体中使用它,还可以在存储过程或其他数据源中使用它:

var customer = (from cus in _billingCommonservice.BillingUnit.CustomerRepository.GetAll()  
                          join man in _billingCommonservice.BillingUnit.FunctionRepository.ManagersCustomerValue()  
                          on cus.CustomerID equals man.CustomerID  
                          // start left join  
                          into a  
                          from b in a.DefaultIfEmpty(new DJBL_uspGetAllManagerCustomer_Result() )  
                          select new { cus.MobileNo1,b.ActiveStatus });