C# Entity Framework Core - LINQ 检查一个数组在另一个数组中是否存在

标签 c# sql-server linq entity-framework-core ef-core-7.0

如果您尝试从查询中查找包含流派的实体(它们还可能包含查询中未包含的其他流派),您将收到错误消息。我尝试了很多选项,但最终得到了 3 个请求。

Request.genres 是一个列表字符串。

query = query.Where(g => request.genres.All(r => g.Genres.Any(gameGenre => gameGenre.Equals(new StringForGame(r)))));

System.InvalidOperationException: The LINQ expression 'r => ShapedQueryExpression: QueryExpression: Projection Mapping: EmptyProjectionMember -> EntityProjectionExpression: StringForGame SELECT 1 FROM Games_Genres AS g ShaperExpression: EntityShaperExpression: GameProfile.Domain.Entities.GameEntites.Game.Genres#StringForGame ValueBufferExpression: ProjectionBindingExpression: EmptyProjectionMember IsNullable: False

.Where(namelessParameter{0} => object.Equals( objA: (object)EF.Property(EntityShaperExpression: GameProfile.Domain.Entities.GameEntites.Game ValueBufferExpression: ProjectionBindingExpression: EmptyProjectionMember IsNullable: False , "Id"), objB: (object)EF.Property(namelessParameter{0}, "GameId"))) .AsQueryable() .Any(o => o.GameString.Equals(new StringForGame(r)))' could not be translated.

query = query.Where(g => g.Genres.Any(gg => request.genres.All(r=> gg.GameString == r)));

System.InvalidOperationException: The LINQ expression 'r => EntityShaperExpression: GameProfile.Domain.Entities.GameEntites.Game.Genres#StringForGame ValueBufferExpression: ProjectionBindingExpression: EmptyProjectionMember IsNullable: False .GameString == r' could not be translated.

此查询有效,但没有给出我需要的结果。

query = query.Where(g => g.Genres.Any(gg => request.genres.Contains(gg.GameString)));
// and this too
query = query.Where(g => g.Genres.All(gg => request.genres.Contains(gg.GameString)));

上下文

public async Task<List<Game>> Handle(GetGamesQuery request, CancellationToken cancellationToken)
{
        var query = _context.Games.AsQueryable();

        if (request.sort == "titleAtoZ")
        {
            query = query.OrderBy(x => x.Title);
        }
        else if (request.sort == "titleZtoA")
        {
            query = query.OrderByDescending(x => x.Title);
        }
        else if (request.sort == "dateAscending")
        {
            query = query.OrderBy(x => x.ReleaseDate);
        }
        else if (request.sort == "dateDescending")
        {
            query = query.OrderByDescending(x => x.ReleaseDate);
        }

        if (request.releaseDateOf != DateTime.MinValue && request.releaseDateTo != DateTime.MinValue)
        {
            query = query.Where(x => x.ReleaseDate >= request.releaseDateOf && x.ReleaseDate <= request.releaseDateTo);
        }
        else if (request.releaseDateOf == DateTime.MinValue && request.releaseDateTo != DateTime.MinValue)
        {
            query = query.Where(x => x.ReleaseDate <= request.releaseDateTo);
        }
        else if (request.releaseDateOf != DateTime.MinValue && request.releaseDateTo == DateTime.MinValue)
        {
            query = query.Where(x => x.ReleaseDate >= request.releaseDateOf);
        }

        if (request.nsfw == "yes")
        {
            query = query.Where(x => x.Nsfw == true);
        }
        else if (request.nsfw == "no")
        {
            query = query.Where(x => x.Nsfw == false);
        }

        if (request.genres is not null && request.genres.Count > 0)
        {
            query = query.Where(g => g.Genres.Any(gg => request.genres.Contains(gg.GameString)));
            //query = query.Where(g => g.Genres.Any(gg => request.genres.All(r=> gg.GameString == r)));
            //query = query.Where(g => request.genres.All(r => g.Genres.Any(gameGenre => gameGenre.Equals(new StringForGame(r)))));
        }

        int skipGame = request.page * 50;
        query = query.Skip(skipGame).Take(50);
        var games = await query.ToListAsync(cancellationToken);

        return games;
}

public sealed class Game : Entity
{
    public Game(Guid id,
                string title,
                DateTime releaseDate,
                Uri headerImage,
                bool nsfw,
                string description,
                ICollection<StringForGame> developers,
                ICollection<StringForGame> publishers,
                ICollection<StringForGame> genres,
                ICollection<UriForGame> screenshots,
                ICollection<UriForGame> shopsLinkBuyGame,
                int achievementsCount) : this(id,title,releaseDate,headerImage,nsfw,description,achievementsCount)
    {
        Developers = developers;
        Publishers = publishers;
        Genres = genres;
        Screenshots = screenshots;
        ShopsLinkBuyGame = shopsLinkBuyGame;
    }

    /// <summary>
    /// EF constructor
    /// </summary>
    private Game(Guid id,
                string title,
                DateTime releaseDate,
                Uri headerImage,
                bool nsfw,
                string description,
                int achievementsCount) : base(id)
    {
        Title = title;
        ReleaseDate = releaseDate;
        HeaderImage = headerImage;
        Nsfw = nsfw;
        Description = description;
        AchievementsCount = achievementsCount;
    }

    public string Title { get; private set; }
    public DateTime ReleaseDate { get; private set; }
    public Uri HeaderImage { get; private set; }
    public bool Nsfw { get; private set; }
    public string Description { get; private set; }

    public ICollection<UriForGame> Screenshots { get; private set; }
    public ICollection<StringForGame> Genres { get; private set; }
}

public sealed class StringForGame : ValueObject
{
    public StringForGame(string gameString)
    {
        GameString = gameString;
    }

    public string GameString { get; private init; }
    
    public override IEnumerable<object> GetAtomicValues()
    {
        yield return GameString;
    }
}

public abstract class ValueObject : IEquatable<ValueObject>
{
    public abstract IEnumerable<object> GetAtomicValues();

    public bool Equals(ValueObject? other)
    {
        return other is not null && ValuesAreEqual(other);
    }

    public override bool Equals(object? obj)
    {
        return obj is ValueObject valueObject && ValuesAreEqual(valueObject);
    }

    public override int GetHashCode()
    {
        return GetAtomicValues().Aggregate(default(int), HashCode.Combine);
    }

    private bool ValuesAreEqual(ValueObject other)
    {
        return GetAtomicValues().SequenceEqual(other.GetAtomicValues());
    }
}

我真的不明白问题是什么,我们将不胜感激。

使用的版本:EF Core 7.0.8

最佳答案

相对众所周知的是,在所有 EF(“经典”和 Core)版本中,LINQ to Entities 查询中内存集合上唯一受支持的方法是 Contains

这适用于任何类型标准,例如

query.Any(x => collection.Contains(Field(x)))

但不适用于All类型标准,例如

query.All(x => collection.Contains(Field(x)))

很可能是因为缺少相应的 SQL 构造(即使它们对其他一些 LINQ 表达式也这样做)。

通常的(也是唯一已知的)解决方法是使用相当于 LINQ All 的“计数匹配”

query.Count(x => collection.Contains(Field(x))) == collection.Count()

(在某些版本中,您可能需要将collection.Count()存储在查询外部的变量中,并在查询内部使用该变量)。

话虽如此,您的案例的解决方案类似于

query = query.Where(g => 
    g.Genres.Count(gg => request.genres.Contains(gg.GameString)) == request.Genres.Count()
);

关于C# Entity Framework Core - LINQ 检查一个数组在另一个数组中是否存在,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76549185/

相关文章:

sql - 一组记录中某些值相同的唯一约束

sql-server - sql server 导入导出向导 - 启用身份插入不起作用

c# - 我是否需要将 AsQueryable() 用于在 .NET EF 中返回 IQueryable<Type> 的方法

c# - 如何组合两个 LINQ 表达式?

C# 代码分析不喜欢 protected 静态 s_Foo(CA1709、CA1707)

c# - 从 WebApi 中的 snake case JSON 自动绑定(bind) pascal case c# 模型

c# - 使用 Tor 作为代理

c# - 这个 lambda 表达式的返回类型应该是什么?

sql - 我应该设计一个主键为varchar或int的表吗?

asp.net - 查询中的 Linq 子查询