0

I want to declare a class to search some topics with specific key. First, I've declared an interface ISearch

interface ISearch
{
    [Required(ErrorMessage = "Search key must be required.")]
    string Key { get; set; }
    Task<IEnumerable<TopicViewModels>> Search();
}

Then, I want to check Key is null or not via using RequiredAttribute.

My question: How to get the error message to throw to user if Key is null?

I don't want to use this way:

Task<IEnumerable<TopicViewModels>> Search(string key)
{
   if (!string.IsNullOrEmpty(key))
   {
      // start searching....
   }
   // throw error message
}
Tân
  • 1
  • 15
  • 56
  • 102
  • I am curious why you dont want to do it like - `Task> Search(string key)`? – Carbine Mar 11 '16 at 11:29
  • 1
    Is there a reason you don't want to use the second way? It seems to be more direct and user friendly and a lot less work than what you are trying. – nvoigt Mar 11 '16 at 11:29
  • @nvoigt I'm trying to check `Key` is valid or not in `another place` before putting it into `Search` method :) – Tân Mar 11 '16 at 11:35
  • 2
    Its misleading when you store the key where its supposed to just search. If you are going to validate key before searching, you still dont have to store it in ISearch. Because after search you wont need it anyways. Its better to stick to Option 2. You are violating Single responsibility principle by naming the interface as search and then storing the query input. – Carbine Mar 11 '16 at 11:43

3 Answers3

1

Is an abstract class appropriate for your case?

public abstract class AbstractSearch
{
    string _key = null;

    public string Key
    {
        get 
        {
            if (_key == null) throw new Exception("Key has not been set.");
            return _key; 
        }
        set 
        { 
            if (value == null) throw new ArgumentNullException("Key");
            _key = value;
        }
    }

    public abstract Task<IEnumerable<TopicViewModels>> Search();
}

public class MySearch : AbstractSearch
{
    public override Task<IEnumerable<TopicViewModels>> Search()
    {
        string key = Key;
        // Start searching...
    }
}
TVOHM
  • 2,740
  • 1
  • 19
  • 29
  • What if Key Property is never set? You can never catch that. – Carbine Mar 11 '16 at 11:39
  • Updated the answer - It is possible to catch it the moment the key is used. – TVOHM Mar 11 '16 at 11:47
  • 1
    You probably can do this using interface and move the implementation to the MySearch class. There is no need for an abstract class. – Carbine Mar 11 '16 at 11:52
  • ya. I think same to @CarbineCoder's comment. We don't need a base class in this case. – Tân Mar 11 '16 at 11:54
  • @HappyCoding I still urge you not to store the Key :) – Carbine Mar 11 '16 at 12:00
  • Fair enough - another suggestion is using CodeContracts - `Contract.Requires(!string.IsNullOrEmpty(Key));` – TVOHM Mar 11 '16 at 12:05
  • @CarbineCoder No, no. It's called: `Looking for a solution to do it better` :)) – Tân Mar 11 '16 at 12:06
  • @TVOHM Thanks for your help. I will try again with your suggestion (not your solution) :) – Tân Mar 11 '16 at 12:11
  • I might be wrong, but its just my opinion. That you might be over engineering and adding a new issue while solving the existing one. Anyway Happy coding @HappyCoding – Carbine Mar 11 '16 at 12:24
  • @CarbineCoder Thanks! What I want to archive: validation will be excuted automatically by `data annotation`. And if `Key` is null or empty, how can I get the error message? No need to declare more class/method to validate. Based on 2 answers and all comments, I think I cannot do it :)) – Tân Mar 11 '16 at 12:33
1

Interface Member Attributes Are Not Inherited! See Attribute on Interface members does not work

If you want to get the error message itself for display (rather than how you want to display it) you can use reflection.

((RequiredAttribute)((PropertyInfo)typeof(ISearch).GetMember("Key")).GetCustomAttribute(typeof(RequiredAttribute))).ErrorMessage;

Don't use that code snippet, but it should give you the idea.

Community
  • 1
  • 1
Isaac van Bakel
  • 1,772
  • 10
  • 22
  • No problem, I was having the same issue with interface attributes myself a while ago, so I thought it was worth mentioning before you had to start scratching your head over it as well. – Isaac van Bakel Mar 11 '16 at 14:49
0

You will have to add the annotation to your concrete class i.e. your view model. You cannot declare data annotations on your interface.

interface ISearch
{
    string Key { get; set; }
    Task<IEnumerable<TopicViewModels>> Search();
}

class MyViewModel : ISearch
{
    [Required(ErrorMessage = "Search key must be required.")]
    public string Key { get; set; }
    public Task<IEnumerable<TopicViewModels>> Search()
    {
    }
}
James Dev
  • 2,979
  • 1
  • 11
  • 16