ASP.NETMVC的最佳存储库模式是什么?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (81)

我最近学会了ASP.NET MVC(我喜欢它)。我正在与一家使用依赖注入在每个请求中加载Repository实例的公司合作,并且我熟悉使用该存储库。

但现在我正在编写一些我自己的MVC应用程序。我并不完全了解我公司使用的存储库的方式和原因,我试图确定实现数据访问的最佳方法。

我正在使用C#和实体框架(包含所有最新版本)。

我看到了三种处理数据访问的一般方法。

  1. 每次访问数据时,使用语句中的常规数据库上下文。这很简单,它工作正常。但是,如果两个位置需要在一个请求中读取相同的数据,则必须读取两次数据。(每个请求使用一个存储库,两个地方都会使用同一个实例,并且我理解第二次读取会简单地从第一次读取返回数据。)
  2. 典型的存储库模式。由于我不明白的原因,这种典型的模式涉及到为数据库中使用的每个表创建一个包装类。这对我来说似乎是错误的。实际上,因为它们也作为接口来实现,所以我在技术上为每个表创建两个包装类。EF为我创建表格。我不相信这种方法是有道理的。
  3. 还有一个通用的存储库模式,其中创建单个存储库类来为所有实体对象提供服务。这对我来说更有意义。但是对别人有意义吗?链接是否是最好的方法?

我很想在这个主题上得到别人的意见。你是在编写自己的存储库,使用上述之一还是完全不同。请分享。

提问于
用户回答回答于

我已经使用了#2和#3的混合,但如果可能的话,我更喜欢使用严格的通用存储库(比#3的链接更加严格)。#1并不好,因为它在单元测试中表现不佳。

如果域较小或需要限制哪些实体允许查询域,我想#2或#3定义了实体特定的存储库接口,它们自己实现了一个通用存储库 - 这是合理的。然而,我发现为每个我想查询的实体编写一个接口和一个具体的实现是耗尽和不必要的。有什么好处public interface IFooRepository : IRepository<Foo>(同样,除非我需要将开发人员限制在一组允许的聚合根上)?

我只是定义我的通用仓库界面,AddRemoveGetGetDeferredCount,和Find方法(FIND返回的IQueryable界面,允许LINQ),创建一个具体的通用实现,而收工。我非常依赖FindLINQ。如果我需要多次使用特定的查询,我使用扩展方法并使用LINQ编写查询。

这涵盖了95%的持续性需求。如果我需要执行某种不能一般完成的持久性操作,则使用本地ICommandAPI。例如,假设我正在使用NHibernate,并且需要将复杂查询作为我的域的一部分,或者我需要执行批量命令。API看起来大致如下:

// marker interface, mainly used as a generic constraint
public interface ICommand
{
}

// commands that return no result, or a non-query
public interface ICommandNoResult : ICommand
{
   void Execute();
}

// commands that return a result, either a scalar value or record set
public interface ICommandWithResult<TResult> : ICommand
{
   TResult Execute();
}

// a query command that executes a record set and returns the resulting entities as an enumeration.
public interface IQuery<TEntity> : ICommandWithResult<IEnumerable<TEntity>>
{
    int Count();
}

// used to create commands at runtime, looking up registered commands in an IoC container or service locator
public interface ICommandFactory
{
   TCommand Create<TCommand>() where TCommand : ICommand;
}

现在我可以创建一个接口来表示一个特定的命令。

public interface IAccountsWithBalanceQuery : IQuery<AccountWithBalance>
{
    Decimal MinimumBalance { get; set; }
}

我可以创建一个具体的实现并使用原始SQL,NHibernate HQL,无论如何,并将其注册到我的服务定位器。

现在在我的业务逻辑中,我可以做这样的事情:

var query = factory.Create<IAccountsWithBalanceQuery>();
query.MinimumBalance = 100.0;

var overdueAccounts = query.Execute();

也可以使用Specification模式IQuery来构建有意义的,用户输入驱动的查询,而不是具有包含数百万混淆属性的接口,但是假设没有发现自己的权利混淆规范模式。

难题的最后一部分是您的存储库需要执行特定的预存储和后置存储库操作。现在,可以非常轻松地为特定实体创建通用存储库的实现,然后覆盖相关方法并执行需要执行的操作,并更新IoC或服务定位器注册并完成相应操作。

然而,有时这种逻辑通过覆盖存储库方法来实现是交叉和尴尬的。所以我创建了IRepositoryBehavior,这基本上是一个事件接收器。(下面只是我头顶的一个粗略定义)

public interface IRepositoryBehavior
{
    void OnAdding(CancellableBehaviorContext context);
    void OnAdd(BehaviorContext context);

    void OnGetting(CancellableBehaviorContext context);
    void OnGet(BehaviorContext context);

    void OnRemoving(CancellableBehaviorContext context);
    void OnRemove(BehaviorContext context);

    void OnFinding(CancellableBehaviorContext context);
    void OnFind(BehaviorContext context);

    bool AppliesToEntityType(Type entityType);
}

现在,这些行为可以是任何事情。审计,安全检查,软删除,强制域约束,验证等。我创建一个行为,将其注册到IoC或服务定位器,并修改我的通用存储库以接收已注册IRepositoryBehavior的集合,并检查每个行为当前存储库类型并将操作包装在前/后处理程序中,以实现每种适用的行为。

下面是一个软删除行为的例子(软删除意味着当有人要求删除一个实体时,我们只需将其标记为已删除,以便它不能再次返回,但实际上从未实际删除)。

public SoftDeleteBehavior : IRepositoryBehavior
{
   // omitted

   public bool AppliesToEntityType(Type entityType)
   {
       // check to see if type supports soft deleting
       return true;
   }

   public void OnRemoving(CancellableBehaviorContext context)
   {
        var entity = context.Entity as ISoftDeletable;
        entity.Deleted = true; // when the NHibernate session is flushed, the Deleted column will be updated

        context.Cancel = true; // set this to true to make sure the repository doesn't physically delete the entity.
   }
}

是的,这基本上是NHibernate的事件监听器的简化和抽象实现,但这就是我喜欢它的原因。A)我可以单元测试一个行为,而不会将NHibernate带入图片B)我可以在NHibernate之外使用这些行为(比如说存储库是包装REST服务调用的客户端实现)C)NH的事件监听器可以是一个真正的痛苦

热门问答

腾讯会议,电脑版,召开会议提示发生异常(错误码:-161)),是什么问题?

推荐已采纳
-161是媒体服务器连接超时,造成该问题的原因可能有: 1. 网络异常 2. 开启了V** 3. 网络有防火墙或上网策略控制 4. 安装风行加速器进行LSP劫持 可以按照上述信息排查一下。... 展开详请

对象存储通过直传首次上传成功后怎么使直传链接失效,防止重新上传?

可酷可乐

腾讯云 · 售后工程师 (已认证)

热爱云计算的小锅一枚。
推荐
从当前的签名机制上看,并不能做到使用后即失效。有两种方式可以降低风险。 1.在web直传模式中,需要向STS申请临时账号,临时账号生效的时间是由durationInSeconds参数控制,可以尽量缩短时间配置。 2.在申请STS时,需要设置policy,确保当前客户端只能上传到C...... 展开详请

移动直播iOS端SDK使用动效触发filepath must not be nil相关crash?

腾讯视频云-ZacharyTXLiteAVSDK技术支持
推荐
移动直播TXLiteAVSDK_Enterprise_iOS在6.8及之后的版本,动效资源有改动,如果新版本还是用之前老的版本的动效资源就会导致该crash问题。参考集成文档重新导入一下动效资源即可:https://cloud.tencent.com/document/produ...... 展开详请

通过自行开发web前端从物联网平台获取数据?

DylanRichard

腾讯 · 产品经理 (已认证)

万物互联的时代,欢迎来到IoT的世界
推荐
你们可以自行开发小程序或者服务端接受数据,物联网平台了相关的API接口: 1.服务端API接口:https://cloud.tencent.com/document/product/1081/34957 2.应用端API接口:https://cloud.tencent.com/d...... 展开详请

关于文字鉴别的违法的问题?

ritchiechen

腾讯 · 后台开发工程师 (已认证)

推荐

请使用 `try catch` 捕获异常,查看堆栈信息

怎么实现存储桶设置私有读写的时候,可以直接访问图片,和复制临时链接一直的功能?

幻象丛林RESTful 服务开发者
推荐
临时链接是携带了签名参数,在后台复制时是自动算好的,可以在签名有效期内临时访问对象。 如果需要自己实现的话可以调用GET Object接口,携带Authorization签名。 GET /<ObjectKey> HTTP/1.1 Host: <BucketName-APPID>....... 展开详请

所属标签

扫码关注云+社区

领取腾讯云代金券

玩转腾讯云 有奖征文活动