【问题标题】:EF Core, DI, Repository pattern and issue with base repository structureEF Core、DI、存储库模式和基本存储库结构问题
【发布时间】:2020-03-05 17:09:03
【问题描述】:

我有 .net core rest api,它包含混合结构,其中只包含存储库而不包含服务层。

现在,我在基本存储库和主要结构方面面临一个问题。让我先解释一下这个问题。

所以,考虑一个实体。假设 Product 和下面是该实体的定义。该实体有一个名为 FullAuditedEntity 的基类。

[Table(name: "Products")]
public class Product : FullAuditedEntity
{
    public string Name { get; set; }
}

public class FullAuditedEntity: IFullAuditedEntity
{
    public FullAuditedEntity() { }

    [Key]
    public virtual int Id { get; set; }
}

public interface IFullAuditedEntity
{
    int Id { get; set; }
}

基础库及其接口如下。

public class EntityBaseRepository<T> : IEntityBaseRepository<T> where T : class, IFullAuditedEntity, new()
{
    private readonly ApplicationContext context;

    public EntityBaseRepository(ApplicationContext context)
    {
        this.context = context;
    }

    public virtual IEnumerable<T> items => context.Set<T>().AsEnumerable().OrderByDescending(m => m.Id);

    public virtual T GetSingle(int id) => context.Set<T>().FirstOrDefault(x => x.Id == id);
}

public interface IEntityBaseRepository<T> where T : class, new()
{
    IEnumerable<T> items { get; }
    T GetSingle(int id);
}

所以,我的产品存储库将是这样的。

public interface IProductRepository : IEntityBaseRepository<Product> { }

public class ProductRepository : EntityBaseRepository<Product>, IProductRepository
{
    private readonly ApplicationContext context;

    public ProductRepository(ApplicationContext context) : base(context: context)
    {
        this.context = context;
    }
}

现在,到目前为止一切都很好,我可以在控制器中访问这个存储库,并且可以执行基类中列出的操作。

我面临的问题:因此,对于这种结构,如果我尝试添加任何没有 FullAuditedEntity 的新实体(请参阅上面的产品实体,我在那里有基类完整的审计实体),我的结构存储库失败并给出错误。

假设我尝试添加新实体实现,并且这个新实体有一个随机 Id,所以我不想继承 FullAuditedEnitity 基类。现在在这种情况下,大多数事情都可以正常工作,但是当我尝试为实现实体创建存储库时,它会给出一般错误。请参阅下面的快照。

到目前为止我尝试了什么......

我正在考虑创建一个并行 Base 存储库,它不会将 FullAuditedEntity 作为泛型类继承,但我不确定这是否是最佳实践。我还担心如果我在当前的存储库模式和依赖注入结构中犯了任何错误怎么办?

任何帮助世界都是最好的,真的很感激。

提前感谢您抽出宝贵时间。

【问题讨论】:

    标签: c# dependency-injection repository-pattern asp.net-core-2.1 ef-core-2.1


    【解决方案1】:

    存储库通常映射到数据库表。数据库表应该总是有一些列可以唯一标识表中的行,通常的做法是将此列称为Id。所以你正确地实现了你的FullAuditedEntity,因为有Id 属性。但是,您的Id 的类型始终为int。我建议您使用以下构造,然后您的Id 将是任何类型的结构,例如intdecimalGuid 等:

    /// <summary>
    /// Abstraction of the Entity
    /// </summary>
    public interface IEntity
    {
        object Id { get; set; }        
    }
    
    
    /// <summary>
    /// Base class for IDs
    /// </summary>
    public abstract class Entity<T>: IEntity where T: struct
    {        
        public T Id { get; set; }
    
        object IEntity.Id
        {
            get { return Id; }
            set {                
                Id = (T)value;
            }
        }
    }
    
    public class EntityBaseRepository<T> : IEntityBaseRepository<T> where T : class, IEntity, new()
    { 
        // The code is omitted for the brevity
    }
    

    此外,尽量避免没有Id 的实体,例如Implementation,因为将来您必须弄清楚如何在数据库表中查找没有Id 的行。

    更新:

    如果不想继承FullAuditedEntity,那么可以创建BaseRepository&lt;T&gt;,然后在EntityBaseRepository中派生出来。

    public abstract class BaseRepository<T> : IEntityBaseRepository<T> where T : class, new()
    {
        public virtual IEnumerable<T> items => throw new NotImplementedException();
    
        public virtual T GetSingle(int id)
        {            
            throw new NotImplementedException();
        }
    }
    
    public class EntityBaseRepository<T> : BaseRepository<T> where T : class
                                                 , IFullAuditedEntity, new()
    {
        public override IEnumerable<T> items => base.items;
    
        public override T GetSingle(int id)
        {
            return base.GetSingle(id);            
        }
    }
    

    然后是您的 Implementation 存储库:

    public interface IImplementationRepository : IEntityBaseRepository<Implementation> { }
    
    
    public class ImplementationRepository: BaseRepository<Implementation>
        , IImplementationRepository
    {
        public override Implementation GetSingle(int id)
        {
    
            return base.GetSingle(id);
        }
    }
    

    更新 1:

    在我看来,最好使用消耗ITRepository&lt;T&gt; 的服务(Service layer)。 因为它为您提供了新的能力,例如:

    1. 对Repository获取的数据添加一些计算

    2. 重新映射由存储库提取的实体

    3. 这是一个额外的去耦层。所以当你编辑你的服务层时,你不需要编辑存储库层然后重新编译你的程序集

    【讨论】:

    • 我同意你对密钥的看法。最终实现类也将以 Guid 作为键,但假设我不想为我的某些实体继承这个 FullAuditedEntity 类,在这种情况下,这个结构对我不起作用并给出错误(参见添加的快照这个错误有问题)。你能在这一点上指导我吗?您对这种无服务结构有何看法?
    • @Bharat 尽量避免没有 ID 的实体,因为可能很难弄清楚如何更新这个实体。你能写一个不需要Id属性的实体的例子吗?
    • @Bharat 哦,太好了,很高兴它对您有所帮助! Repository with Service Design Pattern 此外,您可以使用 UnitOfWork 模式改进您的代码存储库模式。 Read more about UnitOfWork more。但是,关于 Repository 模式和 UnitOfWork 模式的意见很多。
    • @Bharat 您能否创建另一个问题来简化其他用户的未来搜索?每个帖子提出一个问题是一种很好的做法。
    • 当然。这是相同的问题,感谢您的快速回复...stackoverflow.com/questions/58909782/…
    猜你喜欢
    • 2016-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多