3
\$\begingroup\$

I am building an application that will interact with an API. My service layer will call the repository which will request data from the API. I want to be able to pass filters to the repository and came up with the following. I am just curious if there are better ways of doing this?

First, I created an interface called IFilter:

public interface IFilter
{
    string GetFilterName();
    string GetFilterValue();
}

I then have a bunch of filters, for example:

public class CreatedAfterFilter : IFilter
{
    readonly string _name = "CreatedAfter";
    DateTime _value;

    public CreatedAfterFilter(DateTime value)
    {
        this._value = value;
    }

    public string GetFilterValue()
    {
        return _value.ToString("yyyy-MM-ddTHH:mm:ss+00:00");
    }

    public string GetFilterName()
    {
        return _name; ;
    }
}

...or

public class OrdersFilter : IFilter
{
    readonly string _name = "OrderIdList";
    List<Order> _value;

    public OrdersFilter(List<Order> value)
    {
        this._value = value;
    }

    public string GetFilterValue()
    {
        return '[' + String.Join(",", _value.Select(x => x.OrderId)) + ']';
    }

    public string GetFilterName()
    {
        return _name; ;
    }
}

When I call my repository, I do something like this:

var filters = new List<IFilter>();
filters.Add(new CreatedAfterFilter(DateTime.UtcNow.AddDays(-110)));
filters.Add(new OrderFilter(1709359));            
var orders = orderRepository.GetSingleOrder(filters);

Then in my code that calls the API, I do this to create the query string:

foreach (var filter in filters)
{
   getParams.Add(new KeyValuePair<string, string>(filter.GetFilterName(), filter.GetFilterValue()));
}

Am I on the right track here? This is my first real time in writing C# an using Interfaces.

\$\endgroup\$
  • \$\begingroup\$ You might want to put something in place that decouples the Filters from the Repository you are using. E.g. if you are interacting with a database using SQL, the translation of the filter is different than translating it to a REST call where OData filters are used. \$\endgroup\$ – koelkastfilosoof May 4 '17 at 11:04
1
\$\begingroup\$

Am I on the right track here?

I think you are. Here's how you can use a few tricks to make it even shorter.


Instead of the interface you can implement what is common in an abstract class.

public abstract class Filter
{
    public virtual string Name => Regex.Replace(GetType().Name, $"{nameof(Filter)}$", string.Empty);
    public static implicit operator string(Filter filter) => filter.ToString();
}

This means the base class will get the name of the filter from its type and it'll be able to implicitly cast it into a string by calling ToString. Now everything the derived class needs to do is to implement its own ToString:

public class CreatedAfterFilter : Filter
{
    private DateTime _value;

    public CreatedAfterFilter(DateTime value)
    {
        _value = value;
    }

    public override string ToString()
    {
        return _value.ToString("yyyy-MM-ddTHH:mm:ss+00:00");
    }    
}

So later in the loop you can just do this:

foreach (var filter in filters)
{
   getParams.Add(new KeyValuePair<string, string>(filter.Name, filter));
}

If you make the Name property virtual you can override it in a derived class if the name of the filter cannot be derived from its type name:

public class OrdersFilter : Filter
{
    public override string Name => "OrderIdList";

    public override string ToString()
    {
        ...
    }
}
\$\endgroup\$
  • 1
    \$\begingroup\$ How cool is that?! Such a good way to learn by getting feedback. Thankyou so much! I really like these ideas. \$\endgroup\$ – Lock May 4 '17 at 11:22
  • \$\begingroup\$ Just a question.. Why is the implicit operator string have to be static? \$\endgroup\$ – Lock May 6 '17 at 7:15
  • \$\begingroup\$ @Lock I'm sorry, I don't have an explanation for that other than the language requires it that way :-( sometimes it's disturbing but I don't know the design reason. However I think there is probably a logical answer somewhere :-) \$\endgroup\$ – t3chb0t May 6 '17 at 7:20
  • \$\begingroup\$ Is there a way to put in the abstract class an override of the ToString for Date types so it always does 'ToString("yyyy-MM-ddTHH:mm:ss+00:00");' on the date object? \$\endgroup\$ – Lock May 7 '17 at 23:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.