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.
-
\$\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– koelkastfilosoof2017年05月04日 11:04:58 +00:00Commented May 4, 2017 at 11:04
1 Answer 1
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()
{
...
}
}
-
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– Lock2017年05月04日 11:22:41 +00:00Commented May 4, 2017 at 11:22
-
\$\begingroup\$ Just a question.. Why is the implicit operator string have to be static? \$\endgroup\$Lock– Lock2017年05月06日 07:15:01 +00:00Commented 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\$t3chb0t– t3chb0t2017年05月06日 07:20:16 +00:00Commented 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\$Lock– Lock2017年05月07日 23:28:41 +00:00Commented May 7, 2017 at 23:28