使用 LINQ 查询语法 EF Core C# 的左外连接

Posted

技术标签:

【中文标题】使用 LINQ 查询语法 EF Core C# 的左外连接【英文标题】:Left outer join using LINQ Query Syntax EF Core C# 【发布时间】:2021-08-25 20:47:41 【问题描述】:

我有一个关于以下问题的问题,

    未通过外键连接的两个表的左外连接。 按第二个表中匹配的结果排序。 我希望在 LINQ 查询方法语法中完成此操作,因为我根据提供的输入以及跳过和限制添加了许多条件。

如果我们有以下产品和收藏表

所以我想要的输出是:

意思是最喜欢的作为第一组的一部分,而不是最喜欢的应该放在他们后面。以下是我所做的尝试。 我能够加入表格获取输出,但不确定如何确保在第一页中获得所有收藏。

这个答案非常接近我的想法,但它得到了结果,然后进行了排序,这在我的情况下是不可能的,因为我正在进行分页并使用 IQueryable 来获取更少的数据。

Group Join and Orderby while maintaining previous query

对任何解决方案持开放态度以实现相同目标。

[Table("Product")]
public class ProductModel

    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid ProductId  get; set; 
    public string ProductName get; set;
    public bool IsFavorite  get; set; 


[Table("UserFavorite")]
public class UserFavoriteModel

    [Required]
    public Guid UserId  get; set; 
    [Required]
    public Guid Identifier  get; set; 
    [Required]
    public FavoriteType Type  get; set; 


// Gets products
private async Task<List<ProductModel>> GetProductsAsync(
    Guid categoryId, 
    Guid subCategoryId, 
    int from,
    int limit)

    var query = _context.Products.AsQueryable();

    if (!string.IsNullOrEmpty(categoryId))
        query = query.Where(product => product.CategoryId == categoryId);
    if (!string.IsNullOrEmpty(subCategoryId))
        query = query.Where(product => product.SubCategoryId == subCategoryId);

    query = query.Skip(from).Take(limit);

    var products = await query.ToListAsync();

    query = query.GroupJoin(
    _context.Favorites.AsNoTracking()
    .Where(favorite => favorite.Type == FavoriteType.FASHION)
    // This user Id will come from context just adding for overall picture.
    .Where(favorite => favorite.UserId == userId),
    //This orderby if I add will not make any difference.
    //.OrderByDescending(favorite => favorite.Identifier),
    v => v.ProductId,
    f => f.Identifier,
    (product, fav) => new  product, fav ).
    SelectMany(x => x.Fav.DefaultIfEmpty(),
                    (x, y) => SetFavorite(x.Project, y));



private static ProductModel SetFavorite(ProductModel v, UserFavoriteModel si)

    v.IsFavorite = (si != null);
    return v;

【问题讨论】:

【参考方案1】:

我会这样做:

var query =
   _context.Products.AsQueryable().Select(p => new ProductModel 
      ProductId = p.ProductId,
      ProductName = p.ProductName,
      IsFavorite =
         _context.Favorites.Any(f =>
            f.Identifier = p.ProductId &&
            f.Type == FavoriteType.FASHION &&
            f.UserId == userId
         )
   ).OrderByDescending(favorite => favorite.Identifier);

【讨论】:

AsNoTracking 内部投影且不返回记录 - 对我来说是新事物;) @Aducci 非常感谢。这看起来比我想象的要简单,要学习很多:)。有一些错别字可能对其他人有用 f.Identifier = p.ProductId(这应该有 ==),并且 order 应该是 OrderByDescending(favorite => favorite.IsFavorite)。如果我在 ProductModel 中有大量字段,我有一个小小的说明,是否有一种方法可以简化它而不是在选择中声明所有字段。我试过 Select(p => p.IsFavorite =favs condition here ; return p;) 但我想这在 Expressions 中不受支持。 @SvyatoslavDanyliv 我只是用这个方法在 SO 中发布并忘记添加返回部分:)。据我了解,AsNoTracking 仅用于读取,我什至将其用于 _context.Products.AsNoTracking,是否有我不应该使用的原因。是因为 IQueryable? AsNoTracking 用于“通知”ChangeTracker 不要注册加载的实体。如果您没有获得记录,只需 CountAny 等 - 所以没有任何更改跟踪。

以上是关于使用 LINQ 查询语法 EF Core C# 的左外连接的主要内容,如果未能解决你的问题,请参考以下文章

无法从 Linq 查询 EF Core 访问字段值

LINQ / EF Core 不能在查询中使用 string.Contains

比较 EF Core Linq 查询中的 DateTime

Linq语法

EF Core 多对多关系上的 LINQ 查询

LINQ 实体框架查询在 EF Core 中不起作用,引发异常