如果您尝试从查询中查找包含流派的实体(它们还可能包含查询中未包含的其他流派),您将收到错误消息。我尝试了很多选项,但最终得到了 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/