1

In this example, TItem.GetKeyType() is what I'd like to achieve.

Base CRUD repository interface definition:

ICrudRepository<TItem> where TItem : BaseItem where TKey : TItem.GetKeyType()
public Tkey Id { get; set; }

Base Entity definition:

class BaseItem<TKey>
this.GetKeyType = Tkey.GetType()

Entity implementation:

class Book : BaseItem<Int> //declaration of item's tkey

CRUD Interface implementation:

IBooksRepository : ICrudRepository<Book>
BookRepository : IBookRepository --> Ignorance of key struct

This way, whoever implements BooksRepository is forced to use the entity's implementation struct, int in this case. Moreover, I could define new entities using different keys using all the same base entity and crud repository, for example

class Truck : BaseItem<Guid>

Right now there are 2 problems

  1. In the CRUD interface, I cannot call TItem.GetKeyType(), even if I know that its type is BaseItem, thus it must define this method
  2. I cannot call TKey.GetType() on BaseItem, even if it must be a struct, thus defining this method

Is there a way to achieve this type of pattern, without having to define multiple BaseEntities and CRUD Repositories for each type of key/struct intended to be used, on a system where keys can be of different types?

1 Answer 1

2

Ok, I tried this and it seems to work.

The key thing you need in the repo is..

return typeof(TItem).BaseType.GenericTypeArguments[0];

In the BaseItem..

return typeof(TKey);

You cant do the

where TItem : BaseItem<???> 

but you can make BaseItem inherit from a blank RootItem class and use that instead.

However!!

Since this is software engineering rather than stack overflow, I will add that I disapprove of the Generic Repository pattern as a whole.

I think the flaw is that by having a repository per class you lose the relationships between classes and the extra specialised functionality such as GetBooksByAuthorId(string AuthorId)

Edit - full code

class Program
{
    static void Main(string[] args)
    {
        var repo = new BookRepo();
        repo.Items.Add(new Book());
        var item = repo.Items.FirstOrDefault();

        Console.WriteLine(repo.IndexType);
        Console.WriteLine(item.IndexType);
        Console.ReadLine();
    }
}
public abstract class RootItem
{
    public virtual Type IndexType
    {
        get
        {
            return null;
        }
    }
}
public class Item<TKey> : RootItem
{
    public TKey Id { get; set; }

     public override Type IndexType
    {
        get
        {
            return typeof(TKey);
        }
    }
}

public class Book : Item<int>
{

}

public class BaseRepo<TItem> where TItem : RootItem
{
    public List<TItem> Items { get; set; }
    public Type IndexType
    {
        get
        {
            return typeof(TItem).BaseType.GenericTypeArguments[0];
        }
    }
    public BaseRepo()
    {
        this.Items = new List<TItem>();
    }
}

public class BookRepo : BaseRepo<Book>
{

}
9
  • 2
    +1 This takes everything I hate about the generic repository pattern and turns it up to 11.
    – Eric King
    May 9, 2017 at 20:20
  • new myRepo<Tuple<int,int,string>>()
    – Ewan
    May 9, 2017 at 20:25
  • @Ewan I'm sorry, where would you use return typeof(TItem)...? Could you please show a bit more code? Also, I don't get your last comment about the Tuple. May 11, 2017 at 7:51
  • sorry, I have got full code but no wifi atm to upload it. ones for the Item class and one for the Repo
    – Ewan
    May 11, 2017 at 7:52
  • @EricKing What's so bad about a CRUD repository? I understed that a generic repo pattern could break the YAGNI but CRUD operations are performed on all the entity items i guess May 11, 2017 at 7:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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