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.

asked May 4, 2017 at 10:41
\$\endgroup\$
1
  • \$\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\$ Commented May 4, 2017 at 11:04

1 Answer 1

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()
 {
 ...
 }
}
answered May 4, 2017 at 11:07
\$\endgroup\$
4
  • 1
    \$\begingroup\$ How cool is that?! Such a good way to learn by getting feedback. Thankyou so much! I really like these ideas. \$\endgroup\$ Commented May 4, 2017 at 11:22
  • \$\begingroup\$ Just a question.. Why is the implicit operator string have to be static? \$\endgroup\$ Commented May 6, 2017 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\$ Commented May 6, 2017 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\$ Commented May 7, 2017 at 23:28

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.