.net中的存储库模式和继承

时间:2011-04-27 19:14:54

标签: c# .net entity-framework inheritance repository-pattern

我对存储库设计模式很陌生,在尝试实现它时,我已经达到了死胡同,关于继承。

即使我开始朝着正确的方向发展,我也不确定。

所以基本上我会有一个抽象的基类Product,例如id和imagePath,并且会有几个继承自此的产品。

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; }
    }
}

现在存储库如下:

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();
    }
}

我的问题如下:我如何整合有关Scale的操作?将添加(Common.Scale s)之类的东西添加到IProductRepository似乎是一个坏主意。在Add(Common.Product p)中看到我尝试添加哪种类型的产品,然后转换为它,然后添加。似乎是一个坏主意。

我想如果我要更彻底地描述这个问题,我想重复尽可能少的代码,以某种方式隔离产品库中的基本产品添加/删除代码,并以某种方式放置,例如缩放用于在另一个类或方法中添加/删除的特定代码。

我的一个更彻底的方法就是这个:

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
        };
    }
}

不幸的是,这有一个原因。 如果我使用工厂模式返回IProductRepository并在其中使用IProductRepository进行实例化,由于协方差和逆变,这将无法工作,并且IProductRepository不能同时具有逆变和协变性,并且将方法拆分为两个接口似乎违反直觉而且很麻烦。

我怀疑我需要工厂模式才能返回基类接口,但我也愿意接受这方面的建议。正如我所说,我对回购模式非常新手。

我很好奇我做错了什么,我怎么能解决这个问题,以及如何更好地实现这个目标。

感谢。

3 个答案:

答案 0 :(得分:4)

您正在使用继承错误。如果重要的差异是附加属性,则不能将Scale视为(is-a)产品 - 这使得Scale的公开接口与Product不同,并且此时继承只会妨碍您。使用继承来共享行为,而不是属性

您尝试使用继承解决了什么问题?

  

我想重复尽可能少的代码   可能的

为了完成工作而进行一些重复工作是不是更好,而不是试图使用难以置信的设计?

此外,这是您与继承共享的全部内容:

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

重复两个自动实现的属性的定义几乎不具备复制的条件,它肯定不是一个值得关注的问题。

滥用继承权是相当严重的。下一个维护你的应用的人会诅咒你。

  

所以基本上我会有一个摘要   基类Product,id和   例如,imagePath,将有   几种继承自的产品   此

因此,当您添加新类型的产品时,您将不得不扩展继承层次结构?这似乎是一个坏主意。

答案 1 :(得分:3)

我不是通用存储库的忠实粉丝,但在查看您的代码后,我认为您应该使用它:

public interface IEntity
{
    int Id { get; }
}

public interface IRepository<T> where T : class, IEntity 
{
    IQueryable<T> GetQuery();
    T Get(int id);
    void Add(T entity);
    void Update(T entity);
    void Delete(T entity);
}

实现:

public Repository<T> where T : class, IEntity
{
    private ObjectSet<T> _set;          // or DbSet
    private ObjectContext _context;  // or DbContext

    public Repository(ObjectContext context) // or DbContext
    {
        _context = context;
        _set = context.CreateObjectSet<T>();  // _context.Set<T>() for DbContext                     
    }

    public IQueryable<T> GetQuery()
    {
        return _set;
    }

    public T Get(int id)
    {
        return _set.SingleOrDefault(e => e.Id == id);
    }

    public void Add (T entity)
    {
        _set.AddObject(entity);
    }

    public void Update(T entity)
    {
        _set.Attach(entity);
        context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
        // or context.Entry(entity).State = EntityState.Modified; for DbContext
    }

    public void Delete(entity)
    {
        _set.Attach(entity);
        _set.DeleteObject(entity);
    }
}

NO AcceptAllChanges 因为它会重置ObjectStateManager,您的更改将永远不会保存。没有物体的重建,因为它没有意义。

使用此存储库非常简单:

var repo = new BaseRepository<Product>(context);
repo.Add(new Product() { ... });
repo.Add(new Scale() { ... }); // yes this works because derived entities are handled by the same set
context.Save();

答案 2 :(得分:1)

我最近实施了类似的东西。 (使用您的样本类型名称)我有一个ProductRepository知道如何持久化/取消所有Product个子类型。

最终,您的支持数据存储必须能够存储各种子类型以及它们引入的任何属性。您的存储库类型还必须知道如何利用每个给定子类型的这些功能。因此,每次添加子类型时,都会涉及到工作,例如,将表列添加到后备数据存储中以保存它可能引入的属性。这意味着您还需要对存储库类型进行适当的更改。因此,最简单的方法是在传递给您的存储库类型时检查实体的类型,如果它不是受支持的类型则抛出异常,因为您的存储库无法使用它。类似地,在检索实体列表时,存储库必须知道如何检索每个实体子类型并构造实例。由于这些都来自Product,因此它们都可以在IEnumerable<Product>返回值中形成项目。