1

In order to use dependency injection in .NET Core, we've built a bunch of repository interfaces for our controllers to use for database interactions.

We have a EntityBase class that has some methods in it, and our generic repository interface uses that base class like: IRepository<T> where T : EntityBase.

I want to add a more specific TaggedEntityBase class that extends EntityBase to represent the fact that we have some Entities which we want to filter by tags. I want TaggedEntityBase to have an abstract property which I can use in my controller so that I can abstract out and reuse the filtering method.

So what I want is something like this, but I think I want ITaggedRepository to also inherit from IRepository so that a class implementing ITaggedRepository is guaranteed to have a ListAll method and a ListWithTags method:

public class EntityBase { }

public abstract class TaggedEntityBase : EntityBase
{
    public string TagIDs { get; }
}


public interface IRepository<T> where T : EntityBase 
{
    IEnumerable<T> ListAll();
}

public interface ITaggedRepository<T> where T : TaggedEntityBase
{
    IEnumerable<T> ListWithTags(System.Linq.Expressions.Expression<Func<T, bool>> predicate);
}

I'm fairly certain that I've just thoroughly confused myself by pursuing this line of thinking, but I'm not sure how to do what I really want here. I know I need to keep things abstract for dependency injection, but I feel like I'm butting up on the edge of what's possible with interfaces.

Is there a better line of thinking that will get me where I'm trying to go?

7
  • 2
    Doesn't ITaggedRepository<T> : IRepository<T> do what you want, or am I missing something in your question? Commented Feb 8, 2017 at 18:06
  • I want to constrain ITaggedRepository to require a TaggedEntityBase Commented Feb 8, 2017 at 18:18
  • You can, becasuse TaggedEntityBase : EntityBase. Commented Feb 8, 2017 at 18:19
  • See my comments on @Mark's answer. I get an error when I try ITaggedRepository<T> : IRepository<T> where T : TaggedEntityBase Commented Feb 8, 2017 at 18:22
  • 1
    Oh I'm an idiot. We're importing 2 different versions of EntityBase, as @CodeCaster suggested, from 2 different shared libraries that are named almost identically (not my idea...) Commented Feb 8, 2017 at 18:31

2 Answers 2

2

You can go ahead and inherit from IRepository<T>:

public interface ITaggedRepository<T> : IRepository<T> where T : TaggedEntityBase
{
    IEnumerable<T> ListWithTags(Expression<Func<T, bool>> predicate);
}
Sign up to request clarification or add additional context in comments.

5 Comments

I get this error when I do that: The type 'T' cannot be used as type parameter 'T' in the generic type or method 'IRepository<T>'. There is no implicit reference conversion from 'T' to 'EntityBase'.
Did you include where T : TaggedEntityBase ?
Yep. What you have here is exactly what I have in my code, and that error is what made me come here confused to no end.
I'm also using C# 6.0
Ok, I just noticed that the compiler doesn't complain when I put the code I wrote in my question all into one file, modified with your answer. So, there's got to be something else going on.
2

At some point you may into trouble if your TaggedEntity is not really an abstraction. Say you have NamedEntities also and some are Tagged.

Now you have a INamedRepository, ITaggedRepository and a INamedTaggedRepository (you'll run into similar issues on your base entity).

You could do a more trait like thing like:

public class EntityBase {}

public interface ITagged
{
    string TagIDs { get; }
}

public interface INamed
{
    string Name { get; }
}

public class Book : EntityBase, ITagged, INamed
{
    public string TagIDs { get; set; }
    public string Name { get; }
}

public interface IRepository<T> where T : EntityBase
{
    IEnumerable<T> ListAll();
}

public interface IQueryTags<T> where T : ITagged
{
    IEnumerable<T> ListWithTags(Expression<Func<T, bool>> predicate);
}

public interface IQueryByName<T> where T : INamed
{
    T GetByName(string name);
}

public interface IBookRepository : IRepository<Book>, IQueryTags<Book>, IQueryByName<Book>
{

}

public class ConcreteBookRepository: IBookRepository
{
    public IEnumerable<Book> ListAll()
    {
        throw new NotImplementedException();
    }

    public IEnumerable<Book> ListWithTags(Expression<Func<Book, bool>> predicate)
    {
        throw new NotImplementedException();
    }

    public Book GetByName(string name)
    {
        throw new NotImplementedException();
    }
}

In the concrete implementation you could, through composition, use a ByNameQueryer, TagQueryer and some concrete Repository.

I don't really like generic repositories, so I tend to rename IRepository to IStore since it usually only contains the CRUD aspect typically.

Oh and then some entities you can't delete, some can't be updated. You will end up breaking that down to IAdd, IUpdate, IDelete etc. This is where you start to wonder if this was actually a good idea also ;-)

Comments

Your Answer

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.