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.