linq:在不同类型的条件下左加入 linq

Posted

技术标签:

【中文标题】linq:在不同类型的条件下左加入 linq【英文标题】:linq: left join in linq with different type of conditions 【发布时间】:2018-04-30 20:10:22 【问题描述】:

我有两张表,例如 PromotionalOffers 和 PromotionalOffersUsed 我正在使用连接获取匹配记录,现在我想包含不同的条件,例如 PromotionalOffers.ISVCSPId =10 和 PromotionalOffersUsed.OfferId 为 null

我已经使用 left join 在 sql 中编写查询,但我无法在 linq 中编写相同的查询

这里是我的 sql 查询

SELECT * 
FROM PromotionalOffers
left JOIN PromotionalOffersUsed ON PromotionalOffers.Id = PromotionalOffersUsed.OfferId where PromotionalOffers.ISVCSPId =10 and PromotionalOffersUsed.OfferId is null

我的 linq 查询是

(from s in db.PromotionalOffers
 join e in db.PromotionalOffersUsed on s.Id equals e.OfferId
 where s.ISVCSPId == iSVCSPData.Id
 select s).ToListAsync();

我无法在此处包含左连接

【问题讨论】:

【参考方案1】:

对于左连接,你必须这样做,以下是 linq 中左外连接的示例

var leftFinal =
        from l in lefts
        join r in rights on l equals r.Left into lrs
        from lr in lrs.DefaultIfEmpty()
        select new  LeftId = l.Id, RightId = ((l.Key==r.Key) ? r.Id : 0 ;

查询

SELECT * 
FROM PromotionalOffers
left JOIN PromotionalOffersUsed ON PromotionalOffers.Id = PromotionalOffersUsed.OfferId where PromotionalOffers.ISVCSPId =10 and PromotionalOffersUsed.OfferId is null

linq 中的试探性查询

var leftFinal =
        (from l in PromotionalOffers.Where(p=> p.ISVCSPId ==10) 
        join r in PromotionalOffersUsed on l.ID equals r.OfferId  into lrs
        from lr in lrs.DefaultIfEmpty()
        select 
          new  LeftId = l.Id, RightId = ((l.ID==r.OfferId  ) ? r.OfferId   : -1 
        ).where(d=> d.RightID != -1);

【讨论】:

感谢您的快速回复,但在选择我需要检查条件的字段之前。【参考方案2】:

这样试试;

(from s in db.PromotionalOffers
 join e in db.PromotionalOffersUsed on s.Id equals e.OfferId into joinT
 from e in joinT.DefaultIfEmpty()
 where s.ISVCSPId == 10 && (e == null || e.OfferId == null)
 select new  PromotionalOffers = s, PromotionalOffersUsed = joinT  ).ToListAsync();

【讨论】:

感谢您的快速回复,但我无法让 e 处于检查状态【参考方案3】:

显然,PromotionalOffers 之间存在一对多关系 和PromotionalOffersUsed:每个PromotionalOffer 有零个或多个PromotionalOffersUsed,每个PromotionalOffersUsed 恰好属于一个PromotionalOffer

如果您遵循entity framework conventions to design this one-to-many 关系,则不需要进行连接,也不需要左外连接,而是使用 ICollection 类:

class PromotionalOffer

    public int Id set; set;
    // a PromotionalOffer has zero or more PromotionalOfferUsed
    public virtual ICollection<PromotionalOfferUsed> PromitionalOffersUsed get; set;
    ...

class PromotionalOfferUsed

    public int Id set; set;
    // every PromotionalOffersUsed belongs to exactly one PromotionalOffer
    // using foreign key:
    public int PromotionalOfferId get; set;
    public PromotionalOffer PromotionalOffer get; set;
    ...

class MyDbContext: DbContext

    public DbSet<PromotionalOffer> PromotionalOffers get; set;
    public DbSet<PromotionalOfferUsed> PromotionalOffersUsed get; set;

通常,这足以让实体框架知道您设计了一对多关系。

我们需要一些流畅的 API 的唯一原因是奇怪的复数:

protected override void OnModelCreating(DbModelBuilder modelBuilder)

    // PromotionalOffersUsed are stored in a table with name PromotionOffersUsed:
    modelBuilder.Entity<PromotionalOfferUsed>().ToTable("PromotionalOffersUdsed");

    // Every PromotionalOffer has zero or more PromotionalOfferUsed
    // every PromotionalOfferUsed belongs to exactly one (required) PromotionalOffer
    // using foreign key PromtionalOfferId
    modelBuilder.Entity<PromotionalOffer>
        .HasMany(promotionalOffer => promotionalOffer.PromotionalOffersUsed)
        .WithRequired(promotionalOfferUsed => promotionOfferUsed.PromotionalOffer)
         .HasForeignKey(post => post.BlogId);

    base.OnModelCreating(modelBuilder);

再一次,如果您有标准的单数和复数(Person/Persons;Account/Accounts,Order/Orders),则不需要这个流畅的 API。

现在您的查询:考虑集合,而不是联接。

给我所有 ISVCSPId 等于 10 的 PromotionalOffers 他们所有的 PromotionalOffersUsed 的 OfferId 等于 null

using (var dbContext = new MyDbContext())

    var result = dbContext.PromotionalOffers
        .Where(promotionalOffer => promotionalOffer.ISVCSPID == 10)
        .Select(promotionalOffer => new
           // select only the properties you will be using:
            ISVCSPID = promotionalOffers.ISVSPID,
            ... // other properties from promotionalOffers

            PromotionalOffersUsed = promotionalOffers.PromtionalOffersUsed
                .Where(promotionalOffersUsed => promotionalOffersUsed.OfferId == null)
                .Select(promotionalOffersUsed => new
                
                     // again: select only the properties of PromotionalOffersUsed
                     // that you will be using
                     Id = promotionalOffersUsed.Id,
                     ... // other properties
                )
                .ToList();
        

因为实体框架知道有一个外键的一对多,它会为你做正确的左外连接。

【讨论】:

以上是关于linq:在不同类型的条件下左加入 linq的主要内容,如果未能解决你的问题,请参考以下文章

一个文本框怎么输入不同类型字段查询怎么实现?

linq 加入 case 条件

在 linq 中加入不同的字段类型

C# LINQ 与两个不同数据集上的条件 where 子句连接

关于LINQ字段不同类型做映射问题!

.NET深入解析LINQ框架1