具有许多表、左外连接和 where 子句的 LINQ 查询

Posted

技术标签:

【中文标题】具有许多表、左外连接和 where 子句的 LINQ 查询【英文标题】:LINQ query with many tables, left outer joins, and where clause 【发布时间】:2015-08-25 19:22:57 【问题描述】:

我需要将此 SQL 查询转换为 c# 中的 LINQ。我将不胜感激您可以提供的任何帮助或指导。 (我使用的是实体框架 6)。

SELECT f.FacId
  ,sli.SitLocIntId
  ,ei.EnvIntId
  ,p.PhoId
FROM Fac AS f
join SitLocInt AS sli on f.FacId = sli.FacId
join EnvInt AS ei on sli.EnvIntId = ei.EnvIntId
join EnvIntTyp AS eit on ei.EnvIntTypId = eit.EnvIntTypId
left outer join Aff AS a on ei.EnvIntId = a.EnvIntId
left outer join AffCon AS ac on a.AffId = ac.AffId
left outer join Con AS c on ac.ConId = c.ConId
left outer join Pho AS p on c.ConId = p.ConId
WHERE EnvIntTyp = 'Fleet'

我一直在研究组连接语法,但是对于这么多表、大量左外连接和 where 子句,我没有取得太大进展。到目前为止我有

    var testQuery =
        from f in _CEMDbContext.setFac
        join sli in _CEMDbContext.setSitLocInt on f.FacId equals sli.FacId
        join ei in _CEMDbContext.setEnvInt on sli.EnvIntId equals ei.EnvIntId
        join eit in _CEMDbContext.setEnvIntTyp on ei.EnvIntTypId equals eit.EnvIntTypId into eiGroup
        from item in eiGroup.DefaultIfEmpty().Where(e => e.EnvIntTyp == "Fleet")
        select new BusinessParticipant
        
            fac = f,
            sitLocInt = sli,
            envInt = ei,
            // pho = p, // not working... Phone number from table Pho.
        ; 
    var TestList = testQuery.ToList();

正在获取 EnvIntTyp“Fleet”及其所​​有相关数据,包括 Fac、SitLocInt 和 EnvInt。但是当我尝试从可能存在或不存在的 Pho 表中获取电话号码时,我遇到了麻烦。有没有一种很好的方法将所有左外连接链接在一起?感谢您的建议或指导!

编辑 我不仅需要我在 SQL 查询中编写的 Id,还需要整个对象。另外,我应该提到 EnvIntTyp 是同名表的成员。

编辑 2 以下是相关实体的(部分)。 Fac 是 Facility 的缩写。 SitLocInt 是 SiteLocationInterest 的缩写。 EnvInt 代表环境利益。 Aff 是从属关系。 Con 用于联系。 Pho 代表电话。

public partial class Facility

    public Facility()
    
        this.SiteLocationInterests = new List<SiteLocationInterest>();
    

    public int FacilityId  get; set; 
    public string FacilityIdentifier  get; set; 
    public string FacilityName  get; set; 
    public int SiteTypeId  get; set; 
    public string FacilityDescription  get; set; 
    [ForeignKey("GeographicFeature")]
    public Nullable<int> GeographicFeatureId  get; set; 
    [Display(Name = "resAddress", ResourceType = typeof(CEMResource))]
    public string AddressLine1  get; set; 
    [Display(Name = "resAddressLine2", ResourceType = typeof(CEMResource))]
    public string AddressLine2  get; set; 
    public string City  get; set; 
    [Display(Name = "resState", ResourceType = typeof(CEMResource))]
    public string StateCode  get; set; 
    [Display(Name = "resZip", ResourceType = typeof(CEMResource))]
    public string AddressPostalCode  get; set; 
    public string CountyName  get; set; 
    public virtual GeographicFeature GeographicFeature  get; set; 
    public virtual ICollection<SiteLocationInterest> SiteLocationInterests  get; set; 


public partial class SiteLocationInterest

    public int SiteLocationInterestId  get; set; 
    [ForeignKey("Facility")]
    public Nullable<int> FacilityId  get; set; 
    [ForeignKey("EnvironmentalInterest")]
    public Nullable<int> EnvironmentalInterestId  get; set; 
    public Nullable<int> EventId  get; set; 
    [ForeignKey("GeographicFeature")]
    public Nullable<int> GeographicFeatureId  get; set; 
    public System.DateTime CreateDate  get; set; 
    public string CreateBy  get; set; 
    public System.DateTime LastUpdateDate  get; set; 
    public string LastUpdateBy  get; set; 
    public virtual EnvironmentalInterest EnvironmentalInterest  get; set; 
    public virtual Facility Facility  get; set; 
    public virtual GeographicFeature GeographicFeature  get; set; 


public partial class EnvironmentalInterest

    public EnvironmentalInterest()
    
        this.Affiliations = new List<Affiliation>();
        this.SiteLocationInterests = new List<SiteLocationInterest>();
    

    public int EnvironmentalInterestId  get; set; 
    public string EnvironmentalInterestIdentifier  get; set; 
    public string EnvironmentalInterestFacilityIdentifier  get; set; 
    public string FacilityIdentifier  get; set; 
    public string EnvironmentalInterestName  get; set; 
    [ForeignKey("EnvironmentalInterestType")]
    public int EnvironmentalInterestTypeId  get; set; 
    public Nullable<int> EnvironmentalInterestSubTypeId  get; set; 
    public string EnvironmentalInterestDescription  get; set; 
    public virtual ICollection<Affiliation> Affiliations  get; set; 
    public virtual EnvironmentalInterestType EnvironmentalInterestType  get; set; 
    public virtual ICollection<SiteLocationInterest> SiteLocationInterests  get; set; 


public partial class Affiliation

    public Affiliation()
    
        this.AffiliationContacts = new List<AffiliationContact>();
    

    public int AffiliationId  get; set; 
    public string AffiliationIdentifier  get; set; 
    public string AffiliationName  get; set; 
    [ForeignKey("Entity")]
    public Nullable<int> EntityId  get; set; 
    [ForeignKey("EnvironmentalInterest")]
    public Nullable<int> EnvironmentalInterestId  get; set; 
    public int AffiliationTypeId  get; set; 
    public int StatusTypeId  get; set; 
    public virtual EnvironmentalInterest EnvironmentalInterest  get; set; 
    public virtual ICollection<AffiliationContact> AffiliationContacts  get; set; 


public partial class AffiliationContact

    public int AffiliationContactId  get; set; 
    public int AffiliationId  get; set; 
    public int ContactId  get; set; 
    public virtual Affiliation Affiliation  get; set; 
    public virtual Contact Contact  get; set; 


public partial class Contact

    public Contact()
    
        this.AffiliationContacts = new List<AffiliationContact>();
        this.Phones = new List<Phone>();
    

    public int ContactId  get; set; 
    public string ContactIdentifier  get; set; 
    public int EntityId  get; set; 
    public Nullable<int> MailAddressId  get; set; 
    public int ContactTypeId  get; set; 
    public string FirstName  get; set; 
    public string LastName  get; set; 
    public string EmailAddress  get; set; 
    public virtual ICollection<AffiliationContact> AffiliationContacts  get; set; 
    public virtual Entity Entity  get; set; 
    public virtual ICollection<Phone> Phones  get; set; 


public partial class Phone

    public int PhoneId  get; set; 
    public int ContactId  get; set; 
    public int ContactTypeId  get; set; 
    public string PhoneNumber  get; set; 
    public string PhoneExtensionNumber  get; set; 
    public virtual Contact Contact  get; set; 
    public virtual ContactType ContactType  get; set; 

【问题讨论】:

这些连接是基于外键的吗?如果是这样,那么 EF 应该已经创建了您可以使用的导航属性。 停下来。为什么,哦,为什么人们一直在跳槽,付出巨大的努力将完美的代码转换成另一种语言的基本相同的代码?您的用户并不关心您的查询是使用 C# 还是 SQL... 原SQL中的第三个JOIN似乎是不必要的。 这就是我走捷径所得到的。我真的需要整个对象,而不仅仅是我在 SQL 中写的 Id。 您可能可以执行where f.SetSitLocInt.SetEnvInt.SetEnvIntTyp.EnvIntType == "Fleet" 之类的操作并避免连接,但我们需要查看相关实体才能知道您必须使用哪些导航属性。 【参考方案1】:

让我们将问题简化为更易于理解的内容。这是一个简单的左连接:

SELECT As.AId, Bs.BId
FROM As
LEFT JOIN Bs ON As.AId = Bs.AId

在 LINQ 中:

from a in ctx.As
join b in ctx.Bs on a.AId equals b.AId into bgrp // group join -- put matching Bs
                                                 // into a group "bgrp".
from b in bgrp.DefaultIfEmpty()                  // from DefaultIfEmpty() -- if the group
                                                 // has any rows, return them. otherwise,
                                                 // return a single row with the
                                                 // entity's default value (null).
select new

    a.AId,
    b.BId

您可以通过重复来链接多个左连接:

from a in ctx.As
join b in ctx.Bs on a.AId equals b.AId into bs
from b in bs.DefaultIfEmpty()
join c in ctx.Cs on b.BId equals c.BId into cs
from c in cs.DefaultIfEmpty()
select new

    a.AId,
    b.BId,
    c.CId

【讨论】:

谢谢!那是缺失的部分。【参考方案2】:
Northwnd db = new Northwnd(@"c:\northwnd.mdf");
IEnumerable<Customer> results = db.ExecuteQuery<Customer>
(@"SELECT c1.custid as CustomerID, c2.custName as ContactName
    FROM customer1 as c1, customer2 as c2
    WHERE c1.custid = c2.custid"
);

只要表格结果中的列名与实体类的列属性匹配,LINQ to SQL 就会根据任何 SQL 查询创建对象。

您可以通过 Linq 在 C# 中执行本机 SQL 代码,因此您实际上根本不需要转换它。查看这篇 MSDN 文章:https://msdn.microsoft.com/en-us/library/bb399403(v=vs.110).aspx

【讨论】:

这不能回答问题。 没有必要按照问题的要求去做,因为 SQL 可以在没有转换的情况下执行。 主要依赖链接的第一个答案会皱眉,因为链接可能会在某个时候失效。应包括链接中的相关信息。其次是专门讨论 Linq-to-SQL,而不是 EF。 @CoryNelson 实际上,它完美地回答了这个问题。绝对没有必要转换此代码:结果(充其量)不会更清晰、更易于维护或性能更好;在所有三个标准上可能正好相反。 SQL 作为工作的工具就很好(在这种情况下)。 EF/ORM 非常适合无聊的 90%,但人们真的应该停止浪费时间尝试在圆孔中挤压方钉。也就是说,rmn36 确实可以详细说明 EF 的解决方案 我明白你的意思。感谢您的反馈,juharr。我是新来这里回答的。【参考方案3】:

我是这样做的:

var bpQuery =
    from f in _CEMDbContext.setFac
    join sli in _CEMDbContext.setSitLocInt on f.FacId equals sli.FacId
    join ei in _CEMDbContext.setEnvInt on sli.EnvIntId equals ei.EnvIntId into eiGroup
    from eig in eiGroup.DefaultIfEmpty().Where(e => e.EnvIntTyp.EnvIntTyp == "Fleet")
    join a in _CEMDbContext.setAff on eig.EnvIntId equals a.EnvIntId into aGroup
    from ag in aGroup.DefaultIfEmpty()
    join ac in _CEMDbContext.setAffCon.DefaultIfEmpty() on ag.AffId equals ac.AffId into acGroup
    from acg in acGroup.DefaultIfEmpty()
    join c in _CEMDbContext.setCon.DefaultIfEmpty() on acg.ConId equals c.ConId into cGroup
    from cg in cGroup.DefaultIfEmpty()
    join p in _CEMDbContext.setPho.DefaultIfEmpty() on cg.ContactId equals p.ConId into pGroup
    from pg in pGroup.DefaultIfEmpty()
    select new BusinessParticipant
    
        facility = f,
        sitLocInt = sli,
        envInt = eig,
        pho = pg,
    ;                        

    BusinessParticipantList = bpQuery.ToList();

【讨论】:

以上是关于具有许多表、左外连接和 where 子句的 LINQ 查询的主要内容,如果未能解决你的问题,请参考以下文章

MySQL左外连接与where子句 - 返回不匹配的行

如何使用 linq lambda 扩展方法执行带有 where 子句的左外连接

Mysql基础04-查询

左外连接和空值转换

PostgreSQL同表左外连接多表

什么是左外连接 SQLserver