Home > Software design >  Entity Framework, entity .Contains multiple choices
Entity Framework, entity .Contains multiple choices

Time:07-26

Following code works for a single .Contains(tag)

public async Task<List<CreateVideoWithTagNamesDTO>> FindVideosByTag(Tag tag)
{
    var result = await _db.Where(p => p.Tags.Contains(tag))
        .Select( item => new CreateVideoWithTagNamesDTO
        {
            Description = item.Description,
            Title = item.Title,
            Url = item.Url,
            Tags = _mapper.Map<IList<CreateTagDTO>>(item.Tags)
        }).ToListAsync();
            
    return result;
}

How would I query Tags.Contains for multiple Tags ? Lets say this function will take in array of Tags, Tag[] tags How do I include .Contains to include all tags? So it would select videos which have each tag present ?

CodePudding user response:

For multiple tags you need to combine Any and Contains methods like below :

 public async Task<List<CreateVideoWithTagNamesDTO>> FindVideosByTag(ICollection<Tag> tags)
 {
    var result = await _db.Where(p => p.Tags.Any(tag=> tags.Contains(tag)))
       .Select( item => new CreateVideoWithTagNamesDTO
            {
                Description = item.Description,
                Title = item.Title,
                Url = item.Url,
                Tags = _mapper.Map<IList<CreateTagDTO>>(item.Tags)
            })
            .ToListAsync();


    return result;
 }

As @Panagiotis Kanavos pointed this may not work for complex objects EF will fail to convert this into SQL query but you can always fall back to this :

public async Task<List<CreateVideoWithTagNamesDTO>> FindVideosByTag(ICollection<Tag> tags)
 {
    var tagIds  = tags.Select(r=> r.Id).ToArray();
    var result = await _db.Where(p => p.Tags.Select(r=> r.Id).Any(tag=> tagIds.Contains(tag)))
       .Select( item => new CreateVideoWithTagNamesDTO
            {
                Description = item.Description,
                Title = item.Title,
                Url = item.Url,
                Tags = _mapper.Map<IList<CreateTagDTO>>(item.Tags)
            })
            .ToListAsync();


    return result;
 }

CodePudding user response:

You can use Intersect function in list to get matched items and use that u can refer to below question Check if one IEnumerable contains all elements of another IEnumerable

CodePudding user response:

You could also do something along the lines of

public async Task<List<CreateVideoWithTagNamesDTO>> FindVideosByTag(List<Tag> inputTags)
    {
        var result = await _db.Where(p => p.Tags.ToList().Intersect(inputTags).Any()) {
                Description = item.Description,
                Title = item.Title,
                Url = item.Url,
                Tags = _mapper.Map<IList<CreateTagDTO>>(item.Tags)
            })
            .ToListAsync();


        return result;
    }

See Check of two list have colliding element?

  • Related