我在项目中使用存储库/服务设计模式,但发现可能有些多余。考虑到这一点,这是我的结构吗?

这是我的结构:

如您所见,我正在使用Generics,所以它可以是任何东西:
public class Competition
{
    public int Id { get; set; }
    [Required] public string UserId { get; set; }
    public string UserName { get; set; }
    public DateTime DateCreated { get; set; }
    public DateTime? DateModified { get; set; }
    [Required] public string Name { get; set; }
}


我的实现interface的存储库,将IDisposable指定为TEntity。此存储库查询我的数据库,因此查询提供程序。

在此存储库中,我只执行查询数据库的最少操作:
/>现在这是我的服务。例如,您可以看到IRepository方法根据我们是创建还是更新来分配值。一个Competition类可以在那里进行任何操作,例如执行上载或与数据源无关的任何操作。这允许将存储库与另一个查询不同数据源的存储库交换出去。在存储库中,其结构始终与存储库非常相似,即具有:

/> TEntity

我也实现了Save。该服务可能具有其他公共方法,但这并不重要,因为在我的控制器中,我会这样称呼它:

internal interface IRepository<TEntity> : IDisposable where TEntity : class
{
    IList<TEntity> GetAll();
    TEntity Get(int id);
    void Save(TEntity model);
    void Delete(int id);
}



认为这是一种很好的做法,还是您认为我对此有些过分了?
您认为我应该在服务上实施Service?有服务吗?

(注意:我不愿意删除该服务,因为有时有很多代码需要输入,我不想阻塞我的控制器)。

评论

为什么您不使用马丁博客martinfowler.com/eaaCatalog/unitOfWork.html中描述的UnitOfWork模式,只是从Repository接口中删除save和delete方法,并在提交工作单元时执行此工作。 >
接口不应“实现” IDisposable,而应“具体实现”,否则将是一个泄漏性的抽象。模拟回购是一次性的吗?

#1 楼

您走在正确的轨道上。将业务逻辑(服务)和数据访问逻辑(存储库)分开是一件好事,我强烈建议这样做。我创建了一个实现IRepository<TEntity>接口的通用存储库。它与您的稍有不同,但是您明白了(我有一个BaseEntity约束,您也可以使用class):使用特定存储库时重复的代码。我在以下服务中使用了它:

评论


\ $ \ begingroup \ $
是的,似乎所有答案都使用依赖项注入。我没有适当考虑,我也会对此进行调整。干杯
\ $ \ endgroup \ $
–r3plica
2013年10月10日17:12

\ $ \ begingroup \ $
您正在使用的IoC框架是什么?另外,如何配置EfRepository 注入?使用AutoFac,我完成了类似的工作:builder.RegisterAssemblyTypes(typeof(ProjectRepository).Assembly).Where(t => to.Name.EndsWith(“ Repository”))。AsImplementedInterfaces()...谁来转让?
\ $ \ endgroup \ $
– mickyjtwin
2014年3月11日在2:47



\ $ \ begingroup \ $
@mickyjtwin我使用的是简单注入器,它可以执行以下操作:container.RegisterOpenGeneric(typeof(IRepository <>),typeof(EfRepository <>));。
\ $ \ endgroup \ $
–汉克·莫利马(Henk Mollema)
2014年11月11日在8:23

\ $ \ begingroup \ $
@HenkMollema,您所解释的一切似乎都说得通,但我没什么大问题。我的存储库实现了IDisposable。我的解决方案的优势在于,存储库可以在简单的数据库操作上运行,并且可以决定何时关闭数据库连接。问题是我想在服务中使用存储库。我想在每个Service Method的最后放置IRepository。我需要某种方法来保存IRepository的类型,而不是具体实例。我考虑过使用代码反射,但是我担心性能,我担心它看起来很丑。您知道一些实现我目标的设计模式或工具吗?
\ $ \ endgroup \ $
–pt12lol
14年4月13日在22:43

\ $ \ begingroup \ $
您正在库的顶部实现库。它只会增加应用程序的复杂性。
\ $ \ endgroup \ $
– Alex Herman
19年4月10日在19:26

#2 楼

有时候我梦到我是神,但我的现实生活不可能再远了。 >首先,我永远不会摆脱您的服务层,我完全赞成让您的控制器尽可能轻巧!但是从外观上看,您的存储库类除了传递调用以外,没有做很多事情。也许制作一个处理大多数实体的通用存储库,如果您需要更具体的内容,请创建一个新的存储库?

您还可以考虑直接从服务中使用Provider并将此提供程序视为存储库。 />
关于服务层中的IRepository,我必须说我反对。如果将服务设置为存储库,则控制器将被限制为这些类似于CRUD的方法。如果您只有类似CRUD的屏幕,这可能会早日解决,但我不喜欢这种方式查看服务。

我认为服务应该为每种逻辑操作提供一种方法您想对该实体进行操作。

SaveEdit可以是其中的一部分,但是AddNewAnswerToQuestionRaiseFlagToQuestionAddTagToQuestion也可以(例如)。我担心如果将自己限制为类似于CRUD的方法,则您将不得不在if方法中创建很多Save结构(如果添加了标记,如果设置了标志),或者您的控制器最终将做更多工作(先调用Save方法,然后再调用Mail方法,因为需要在附加标签的情况下发送邮件)。

如果您采取更极端的方法,您将不再为每个实体提供服务,而是每个动作提供一项服务。

此时,您不再给它们调用服务,而开始给它们调用命令,并使用CQS(命令-查询分隔),但这取决于您:)

简而言之,我将创建一个通用存储库,或者将其全部删除,或者通过使用更多方法或创建命令而不是服务来使我的服务更加明确。

评论


\ $ \ begingroup \ $
好评论,你说了我在想的一切:)干杯
\ $ \ endgroup \ $
–r3plica
2013年10月10日17:12

#3 楼

首先:为什么不以通用方式实现您的仓库?我的意思是实现通用接口。在您的解决方案中,您必须为每个新的POCO创建一个IRepository<Entity>实现。这将使您的代码膨胀,并且您将无法使用任何通用接口。

以下是通用回购实现的示例: >您可以在界面和实现中使用不同的方法。如果不是这种情况,那么您可以像下面这样使用您的存储库:它是对存储库的包装,提供了一种在所有存储库之间共享一个上下文的方法。

这里是示例:实现:使用工作单元,在不同的存储库中进行更改后,只需调用一次Unit Of Work类的SaveChanges()方法即可。因此,这将是单个事务。

此外,也许您已经注意到,即使在db上下文实现中,我也在各处使用接口。如果需要,这将帮助您将来对逻辑进行单元测试。并使用UnitOfWork! :总结:实施通用存储库和工作单元模式作为DAL(数据访问层),然后可以使用BL(业务)构建任何服务层。逻辑)。

评论


\ $ \ begingroup \ $
很好的答案,谢谢,我已将其与其他答案一起标记为答案。我将看一下UnitOfWork模式,因为我以前没有使用过。干杯
\ $ \ endgroup \ $
–r3plica
13-10-10在17:11

\ $ \ begingroup \ $
我看过EF负责单位的工作吗?
\ $ \ endgroup \ $
– hanzolo
2014年10月8日在17:09

\ $ \ begingroup \ $
@hanzolo是的,存储库模式也是如此。检查此软件engineering.stackexchange.com/a/220126/198700
\ $ \ endgroup \ $
– vcRobe
18年4月10日在17:19