C# – Repository pattern and inheritance in .net

centity-frameworkinheritancenetrepository-pattern

i am pretty new to the repository design pattern and i have reached a dead end while trying to implement it, with regards to inheritance.

I am not sure even if i started in the right direction.

So basically i will have an abstract base class Product, with id and imagePath for instance, and will have several products which inherit from this.

namespace Common
{
    public abstract class Product
    {
        public int Id { get; set; }
        public string ImgPath { get; set; }
    }

    public class Scale : Product
    {
        public int AdditionalProperty { get; set; }
    }
}

Now the repositories are as follows:

public class BaseRepository
{
    protected TEstEntities1 _dataContext = new TEstEntities1();

    public BaseRepository()
    {
        _dataContext = new TEstEntities1();
    }
}

public interface IProductRepository 
{
    Common.Product Get(int id);
    void Add(Common.Product p);
    void Update(Common.Product p);
    List<Common.Product> ListAll();
}

public class ProductRepository : BaseRepository, IProductRepository
{
    public Common.Product Get(int id)
    {
        throw new NotImplementedException();
    }

    public void Add(Common.Product p)
    {
        throw new NotImplementedException();
    }

    public void Update(Common.Product p)
    {
        throw new NotImplementedException();
    }

    public List<Common.Product> ListAll()
    {
        throw new NotImplementedException();
    }
}

My problem is as follows: how do i integrate operations regarding Scale ? It seems a bad idea to add something like Add(Common.Scale s) to the IProductRepository. It seems like a bad idea to see inside the Add(Common.Product p) which type of Product i try to add, then cast to it, then add.

I guess that if i were to describe this problem more thoroughly, I want to repeat as few code as possible, to somehow isolate base product adding/removing code in the product repository, and somehow put e.g. Scale specific code for adding/removing inside another class, or method.

A more thorough approach of mine has been this one:

public interface IProductRepository<T> where T : Common.Product
{
    T Get(int id);
    void Add(T p);
    void Delete(T p);
}

public abstract class ProductRepository : BaseRepository
{
    protected void Add(Common.Product p)
    {
        _dataContext.AddToProduct(new Product { Id = p.Id, Image = p.ImgPath });
        _dataContext.AcceptAllChanges();
    }

    protected void Delete(Common.Product p)
    {
        var c = _dataContext.Product.Where(x => x.Id == p.Id).FirstOrDefault();
        _dataContext.DeleteObject(c);
        _dataContext.AcceptAllChanges();
    }

    protected Product Get(int id)
    {
        return _dataContext.Product.Where(x => x.Id == id).FirstOrDefault();
    }
}

public class CantarRepository : ProductRepository, IProductRepository<Common.Scale>
{
    public void Add(Common.Scale p)
    {
        base.Add(p);
        _dataContext.Scale.AddObject
             (new Scale { ProductId = p.Id, AdditionalProperty = p.AdditionalProperty });
        _dataContext.AcceptAllChanges();
    }

    public void Delete(Common.Scale p)
    {
        var c = _dataContext.Scale.Where(x => x.ProductId == p.Id);
        _dataContext.DeleteObject(c);
        _dataContext.AcceptAllChanges();
        base.Delete(p);
    }

    public new Common.Scale Get(int id)
    {
        var p = base.Get(id);
        return new Common.Scale
        {
            Id = p.Id,
            ImgPath = p.Image,
            AdditionalProperty = _dataContext.Scale.Where
               (c => c.ProductId == id).FirstOrDefault().AdditionalProperty
        };
    }
}

Unfortunatelly this falls short for one reason.
If i use a factory pattern to return an IProductRepository and inside it i instantiate with IProductRepository this will not work because of covariance and contravariance, and IProductRepository can't be contravariant and covariant at the same time, and splitting the methods into two interfaces seems counterintuitive and cumbersome.

I suspect i will need the factory pattern in order to have a base class interface returned, but i am open to suggestions on this as well. As i've said, i am very newbie regarding the repo pattern.

I am curious as to what i am doing wrong, how i can solve this, and how can i implement this better.

Thanks.

Best Answer

You are using inheritance incorrectly. You cannot treat Scale as a (is-a) Product if the important difference is additional properties - that makes the exposed interface of Scale different than Product, and at that point inheritance simply gets in your way. Use inheritance to share behavior, not properties.

What problem are you trying to solve with your use of inheritance?

I want to repeat as few code as possible

Wouldn't it be better to have a little duplication in order to get things done, rather than spin your wheels trying to work with an implausible design?

Also, this is all you are sharing with your inheritance:

    public int Id { get; set; }
    public string ImgPath { get; set; }

Repeating the definition of two auto-implemented properties almost doesn't even qualify as duplication, it most certainly is not an issue to be concerned about.

Misusing inheritance, however, is fairly grievous. The next person to maintain your app will curse you for it.

So basically i will have an abstract base class Product, with id and imagePath for instance, and will have several products which inherit from this.

So when you add new types of products you will have to extend an inheritance hierarchy? That seems like a bad idea.