Search code examples
c#asp.net-web-apiswagger.net-5

How to implement search in EF Core


I have a hiring management system app with 2 entities Person & Tag.

Person can have a list of tags.

This is my person entity:

    public int Id { get; set; }
    public string FirstName { get; set; }
    public string Family { get; set; }
    public long NationalId { get; set; }
    public DateTime BirthDate { get; set; }
    public ICollection<TagAggregate> Tags { get; set; }

and this is my tag entity:

    public int Id { get; set; }
    public string TagName { get; set; }
    public string Description { get; set; }
    public int PersonId { get; set; }
    public PersonAggregate Person { get; set; }

Everything works fine except when I want to search persons with their tag names.

For example, I have a person John, with a programmer and a designer tag.

When I search for these tags, it returns the first record and can't find the second one.

For example I created a tag name with value programmer for person id 1 (which is "john"), then I created another tag name with value designer (again for person id 1 ("john"))!

It only finds programmer (because I created it first) and returns 204 for "designer".

And when I remove "programmer" tag, then I can find john with tag name "designer".

I don't know what the problem is.

Here is my search method :

public async Task<PersonAggregate> FindByTagName(string tagName)
{
    var person = DbSet.Where(p => p.Tags
                                   .Select(t => t.TagName == tagName).FirstOrDefault())
                      .FirstOrDefault();
    return person;
}

Solution

  • You can use .Any() extension method of LINQ to implement your goal. Here is our example :

    public static async Task<PersonAggregate> FindByTagName(string tagName)
    {
        List<TagAggregate> tags = new List<TagAggregate>() { new TagAggregate() { Id=1 , TagName= "tag1" , Description= "test" , PersonId=1 },
        new TagAggregate() { Id=2 , TagName= "test2" ,Description= "tag2" ,PersonId=1 }};
        List<PersonAggregate> result = new List<PersonAggregate>()
        {
            new PersonAggregate(){ Id = 1, FirstName = "ali" , Family= "ghodsi" ,NationalId=3 ,BirthDate=DateTime.Now ,Tags=tags },
        };
        var person = result
           .Single(p => p.Tags.Any(t => t.TagName == tagName));
        return person;
    }
    

    In my case, I create a sample list to show my sample output, but in your case, you should change your method like this :

    public async Task<PersonAggregate> FindByTagName(string tagName)
        {
            var person = DbSet
                .Single(p => p.Tags.Any(t => t.TagName == tagName));
            return person;
        }
    

    But I recommend you when you use the async method it's better to use async EF Core methods, like ToListAsync(), FirstOrDefaultAsync(), SingleOrDefaultAsync(). So I recommend you to change your method like this :

    public async Task<PersonAggregate> FindByTagName(string tagName)
        {
            return await DbSet
                .SingleOrDefaultAsync(p => p.Tags.Any(t => t.TagName == tagName));
        }
    

    .Any() extension method goes into the collection and search for the condition we expected and if find our expected value, it returns that object. You can read mor about .Any() linq extention method here : Enumerable.Any Method