F11
F11

Reputation: 3816

Linq query to search

I have a collection of Employee class and employee class has few properties like departement,manager name,payscale,designation etc.Now in my webapi, i have a search method in which i am searching a string across all fields of webapi

like if I search Peter it'll search in all fields(departement,manager_name,payscale,designation) of all employees.For this i am using below:-

    public IEnumerable<Employee> Search(string searchstr)
    {
        if (repository != null)
        {
            var query =
                from employees in repository.GetEmployees()
                where
                    (employees.departement != null && employees.departement.Contains(searchstr)) ||
                    (employees.payscale != null && employees.payscale.Contains(searchstr))
                    (movie.designation != null && movie.designation.Contains(searchstr)) )
                select employees;

            return query.AsEnumerable().OrderBy(c => c.employeeid);
        }
        else
        {
            throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
        }
    }

Though i am getting desired result,i have not to use that query.Is there any other way to rewrite same query?

Upvotes: 0

Views: 1288

Answers (2)

mishamosher
mishamosher

Reputation: 1023

As stated by Noctis, using reflection results in a heavy task for the .NET runtime.

Here is some example code that loops trough all properties of a class and searchs a string concidence. It uses reflection ;)

Any questions, leave a comment asking!

The code in the entry point of the APP:

    [STAThread]
    private static void Main(string[] args)
    {
        var person1 = new Person {Name = "The first name", Address = "The first address"};
        var person2 = new Person {Name = "The second name", Address = "The second address"};

        var results = SearchStringTroughAllProperties("second", new List<Person> {person1, person2});
    }

The Person class:

    class Person
    {
        public string Name { get; set; }
        public string Address { get; set; }
    }

And the SearchStringTroughAllProperties method:

    private static IEnumerable<Person> SearchStringTroughAllProperties(string stringToSearch,
        IEnumerable<Person> persons)
    {
        var properties =
            typeof (Person).GetProperties()
                .Where(x => x.CanRead && x.PropertyType == typeof (string))
                .Select(x => x.GetMethod)
                .Where(x => !x.IsStatic)
                .ToList();
        return persons.Where(person =>
            properties.Select(property => (string) property.Invoke(person, null) ?? string.Empty)
                .Any(propertyValueInInstance => propertyValueInInstance.Contains(stringToSearch)));
    }

Notice that:

  • It searchs in properties, not in fields
  • It only searchs on properties that can be read (have a get defined)
  • It only searchs on properties of type string
  • It only searchs on properties that aren't static members of the class

EDIT:

For searching the string coincidence in a string or string[] property, change SearchStringTroughAllProperties method to this (it gets longer!):

    static IEnumerable<Person> SearchStringTroughAllProperties(string stringToSearch, IEnumerable<Person> persons)
    {
        var properties =
            typeof (Person).GetProperties()
                .Where(x => x.CanRead && (x.PropertyType == typeof (string) || x.PropertyType == typeof(string[])))
                .Select(x => x.GetMethod)
                .Where(x => !x.IsStatic)
                .ToList();
        foreach (var person in persons)
        {
            foreach (var property in properties)
            {
                bool yieldReturned = false;
                switch (property.ReturnType.ToString())
                {
                    case "System.String":
                        var propertyValueStr = (string) property.Invoke(person, null) ?? string.Empty;
                        if (propertyValueStr.Contains(stringToSearch))
                        {
                            yield return person;
                            yieldReturned = true;
                        }
                        break;
                    case "System.String[]":
                        var propertyValueStrArr = (string[]) property.Invoke(person, null);
                        if (propertyValueStrArr != null && propertyValueStrArr.Any(x => x.Contains(stringToSearch)))
                        {
                            yield return person;
                            yieldReturned = true;
                        }
                        break;
                }
                if (yieldReturned)
                {
                    break;
                }
            }
        }
    }

Upvotes: 1

Noctis
Noctis

Reputation: 11783

Even though work, it feels a bit dirty. I would consider maybe using reflection to get the properties of the class, and then dynamically search them.

The benefit would be: if you add a new property tomorrow, there's nothing else you need to change.

The disadvantage would be: probably not as performant since reflection is much slower than simply searching for things you know exist.

Having said that, i'm sure there are some other nifty advanced linq tricks that maybe others can point out.

I thought I have some handy code but I don't. I wouldn't like to write it of the top of my head, because it probably won't compile (you need to get the syntax right). Have a look at the above link :)

Upvotes: 0

Related Questions