HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Implementing a Property Searcher

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
propertyimplementingsearcher

Problem

I want to search some or all public properties of a class and for this requirement I have implemented a Property Searcher.

The Input for a Search is always a string. The Output contains the SearchString, the SearchContent in which the SearchString is contained and the searched Property.The Output is wrapped in a SearchResult object.

This is what i have implemented:

SearchResult.cs

public class SearchResult
{
    public PropertyInfo Property { get; private set; }

    public string SearchString { get; private set; }

    public string SearchContent { get; private set; }

    public ISearchable Item { get; private set; }

    public SearchResult(ISearchable item, 
                        PropertyInfo property, 
                        string searchString)
    {
        this.Property = property;
        this.SearchString = searchString;
        this.Item = item;
        this.SearchContent = property.GetValue(item).ToString();
    }
}


ISearchable.cs

public interface ISearchable
{
    IEnumerable Search(string searchString);
}


Extension Methods for easier usage:

Extension.cs

public static class Extension
{
    public static SearchResult Search(this ISearchable source, string searchString, PropertyInfo info)
    {
        if (info.GetValue(source).ToString().Contains(searchString, StringComparison.OrdinalIgnoreCase))
        {
            return new SearchResult(source, info, searchString);
        }

        return null;
    }

    public static IEnumerable Search(this IEnumerable source, string searchString)
    {
        return source.SelectMany(searchable => searchable.Search(searchString)).Where(x => x != null);
    }

    public static bool Contains(this string source, string toCheck, StringComparison comp)
    {
        return source.IndexOf(toCheck, comp) >= 0;
    }
}


I also want to show you the example and test code for e.g. searching in the property of a Book with a Description (the code is simplified and m

Solution

I don't have a C# compiler at hand and cannot test the following.

It looks like the Search() always returns the sum of results of these 3 things:

  • delegate the call to any property that's ISearchable.



  • search the value of some properties



  • do not search the value of some other properties



The method will likely always consist of a list of these types of code blocks:

-
the delegate type

foreach (var searchResult in this.PROPERTY_THAT_IS_ISEARCHABLE.Search(searchString))
{
    yield return searchResult;
}


-
for the direct search

yield return this.Search(searchString, this.GetType().GetProperty(nameof(this.PROPERTY_THATS_NOT_ISEARCHABLE)));


-
no code for excluded properties

This smells like copy&paste. But you are already doing reflection, so why not use it to your advantage?

I think you can create a generic Search() method, then slap that onto everything that implements ISearchable via extension, getting you rid of explicitely implementing the method in those classes. Here's how:

  • You should be able to filter out all properties that implement ISearchable and delegate the method call to them.



  • For non-ISearchable properties that should be searched, you should decide if they are the minority1. If so, define an attribute to opt-in a property. You'd then ignore all non-ISearchable by default and only search the values of those that are explicitly opted-in with the attribute.



  • For non-ISearchable properties that should not be searched, do pretty much the opposite of (2.). If they are the minority, create and add to them an attribute explicitly to opt-out of the default which would be to search all non-ISearchable properties.



1 The minority thing is only to reduce future work, minimizing the amount of attributes that you have to add. Attributes are optional, so you have to define some default behaviour anyway.

Let's say the default behaviour is to search all properties and opt-out via attribute. Here's how your classes could look like:

public class Book : ISearchable
{
    public DescriptionInfo DescriptionInfo { get; set; }
    public string Title { get; set; }
}


As you can see in the Book class: ISearchable is more or less a marker interface.

public class DescriptionInfo : ISearchable
{
    public string Description { get; set; }

    [IgnoreThisDuringSearch]
    public string ShortDescription { get; private set; }

    public DescriptionInfo(string description)
    {
        if (!string.IsNullOrEmpty(description))
        {
            this.Description = this.ShortDescription = description;
        }
    }
}


The generic extension method would perform the following two steps then:

  • delegate to every property that it can be delegated to



  • search every property that got opt-in via attribute or search every property except those opt-out via attribute



Some additional thoughts:

The attribute makes the search independent from the actual names of the properties. If you change Title to Totle in the Book class, you current code breaks. The suggested solution is solely based on reflection and existance (lack of) an attribute, which should always work, given a sensible default behaviour.

If you wish to perform the searching/delegating in a special order, you could do that, too. For example via a property of another attribute. See [DataMember(Order = n)] for example.

Code Snippets

foreach (var searchResult in this.PROPERTY_THAT_IS_ISEARCHABLE.Search(searchString))
{
    yield return searchResult;
}
yield return this.Search(searchString, this.GetType().GetProperty(nameof(this.PROPERTY_THATS_NOT_ISEARCHABLE)));
public class Book : ISearchable
{
    public DescriptionInfo DescriptionInfo { get; set; }
    public string Title { get; set; }
}
public class DescriptionInfo : ISearchable
{
    public string Description { get; set; }

    [IgnoreThisDuringSearch]
    public string ShortDescription { get; private set; }

    public DescriptionInfo(string description)
    {
        if (!string.IsNullOrEmpty(description))
        {
            this.Description = this.ShortDescription = description;
        }
    }
}

Context

StackExchange Code Review Q#119638, answer score: 2

Revisions (0)

No revisions yet.