最佳库模式的ASP.NET MVC [英] Best Repository Pattern for ASP.NET MVC

查看:127
本文介绍了最佳库模式的ASP.NET MVC的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

笔者近日了解到ASP.NET MVC(我喜欢)。我与使用依赖注入在每个请求加载库实例的公司工作,我很熟悉如何使用存储库。

I recently learned ASP.NET MVC (I love it). I'm working with a company that uses dependency injection to load a Repository instance in each request, and I'm familiar with using that repository.

但现在我正在写几个我自己的MVC应用。我不完全了解我公司使用的仓库的方式及原因,而我试图决定最佳的方法来实现数据的访问。

But now I'm writing a couple of MVC applications of my own. I don't fully understand the hows and whys of the repository my company uses, and I'm trying to decide the best approach to implement data access.

我使用C#和实体框架(与所有最新版本)。

I am using C# and Entity Framework (with all the latest versions).

我看到三个常用的方法来处理数据访问。

I see three general approaches for handling data access.


  1. 我每次访问数据using语句中的普通DB环境。这是简单的和它的作品没关系。然而,如果两个位置需要一个请求内读出的相同的数据,该数据必须被读两次。 (每个请求的单一存储库,同一个实例将在两地使用,据我所知,第二次读取只会从第一个读取返回的数据。)

  1. Regular DB context within a using statement each time I access data. This is simple and it works okay. However, if two locations need to read the same data within one request, the data must be read twice. (With a single repository per request, the same instance would be used in both places and I understand the second read would simply return the data from the first read.)

一个典型的<一个href=\"http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application\">repository格式。至于原因,我不明白,这个典型的模式包括创建从数据库中使用的每个表的包装类。这似乎是我错了。事实上,因为它们也实现为接口,我在技术上是为每个表创建两个包装类。 EF为我创建表。我不认为这种做法是有道理的。

A typical repository pattern. For reasons I don't understand, this typical pattern involves creating a wrapper class for every table used from the database. That seems wrong to me. In fact, since they are implemented also as interfaces, I'd technically be creating two wrapper classes for each table. EF creates tables for me. I don't believe this approach makes sense.

还有一个<一个href=\"http://www.tugberkugurlu.com/archive/generic-repository-pattern-entity-framework-asp-net-mvc-and-unit-testing-triangle\">generic库格式在创建一个存储库类为所有实体对象。这使得更多的意义对我来说。但是否有意义给别人?以上是最好的办法的链接?

There is also a generic repository pattern where a single repository class is created to serve all entity objects. This makes much more sense to me. But does it make sense to others? Is the link above the best approach?

我很想得到关于这一主题与其他一些输入。你在写你自己的仓库,上面用其中的一个,或者干脆做一些不同的东西。请分享一下。

I'd love to get some input from others on this topic. Are you writing your own repository, using one of those above, or doing something different altogether. Please share.

推荐答案

我已经使用了#2,#3的混合,但我preFER严格的通用存储库(如果可能更严格,甚至比在链接的建议#3)。 #1是没有好,因为它与单元测试发挥不佳。

I have used a blend of #2 and #3, but I prefer a strict generic repository if possible (stricter than even suggested in the link for #3). #1 is no good because it plays poorly with unit testing.

如果你有一个较小的领域或需要收缩的实体您的域名允许进行查询,我猜#2或#3,它定义了自己实现一个通用repository-有道理实体库特定的接口。不过,我觉得这是辛苦的,没有必要写一个接口和我想对每一个实体的具体实现。什么是好 public接口IFooRepository:IRepository&LT;富&GT; (同​​样,除非我需要约束开发商一组允许的总根)

If you have a smaller domain or need to constrict which entities that your domain allows to be queried, I suppose #2- or #3 that defines entity specific repository interfaces that themselves implement a generic repository- makes sense. However, I find it to be exhausting and unnecessary to write an interface and a concrete implementation for every entity I want to query. What good is public interface IFooRepository : IRepository<Foo> (again, unless I need to constrain developers to a set of allowed aggregate roots)?

我只是定义我的通用库接口,可与添加删除获取 GetDeferred 计数查找方法(查找返回一个的IQueryable 接口,允许LINQ),创建一个具体的通用的实现,并收工。我倚重查找,因此LINQ。如果我需要使用特定的查询不止一次,我使用扩展方法和使用LINQ编写查询。

I just define my generic repository interface, with Add, Remove, Get, GetDeferred, Count, and Find methods (Find returns an IQueryable interface allowing LINQ), create a concrete generic implementation, and call it a day. I rely heavily on Find and thus LINQ. If I need to use a specific query more than once, I use extension methods and write the query using LINQ.

这占地面积我的坚持需要的95%。如果我需要进行某种形式的持久性作用,不能笼统做的,我用的是自家种的的ICommand API。例如,假设我与NHibernate的工作,我需要执行复杂查询我的域的一部分,或者我需要做批量命令。该API看起来大致是这样的:

This covers 95% of my persistence needs. If I need to perform some sort of persistence action that can't be done generically, I use a home-grown ICommand API. For example, say I'm working with NHibernate and I need to perform a complex query as part of my domain, or perhaps I need to do a bulk command. The API looks roughly like this:

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

现在我可以创建一个接口来重新present一个特殊的命令。

Now I can create an interface to represent a specific command.

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

我可以创建一个具体的实施和使用原始SQL,NHibernate的HQL,什么的,和我的服务定位器注册。

I can create a concrete implementation and use raw SQL, NHibernate HQL, whatever, and register it with my service locator.

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

Now in my business logic I can do something like this:

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

var overdueAccounts = query.Execute();

您也可以使用带有 IQUERY 一个规范的模式建立有意义的,用户输入驱动的查询,而不是有600万混淆性质的接口,但假定你没有找到该规范的模式在自己的权利混乱。)

You can also use a Specification pattern with IQuery to build meaningful, user-input-driven queries, rather than having an interface with million confusing properties, but that assumes you don't find the specification pattern confusing in its own right ;).

拼图的最后一条是,当你的资料库需要做具体的pre和-post库操作。现在,你可以很轻松地创建通用的存储库的实现为特定的实体,然后重写相关方法(S)和你需要做什么,并更新你的IoC或服务定位器注册,用它做。

One last piece of the puzzle is when your repository needs to do specific pre- and -post repository operation. Now, you can very easily create an implementation of your generic repository for a specific entity, then override the relevant method(s) and do what you need to do, and update your IoC or service locator registration and be done with it.

不过,有时这种逻辑是跨领域的尴尬通过重写存储库的方法来实现。因此,我创建 IRepositoryBehavior ,这基本上是一个事件接收器。 (下面是只是一个粗略的定义,把我的头顶部)

However, sometimes this logic is cross-cutting and awkward to implement by overriding a repository method. So I created IRepositoryBehavior, which is basically an event sink. (Below is just a rough definition off the top of my head)

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的集合 s和检查对当前库类型每个行为和在$ p $包住运行时的p /后处理程序为每个适用的行为。

Now, these behaviors can be anything. Auditing, security checking, soft-delete, enforcing domain constraints, validation, etc. I create a behavior, register it with the IoC or service locator, and modify my generic repository to take in a collection of registered IRepositoryBehaviors, and check each behavior against the current repository type and wrap the operation in the pre/post handlers for each applicable behavior.

下面是一个例子软删除的行为(软删除意味着,当有人问到删除一个实体,我们只是将其标记为删除,因此它不能再回来了,但从来没有真正物理删除)。

Here's an example soft-delete behavior (soft-delete means that when someone asks to delete an entity, we just mark it as deleted so it can't be returned again, but is never actually physically removed).

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的事件监听器可以在屁股真正的痛苦;)

Yes, this is basically a simplified and abstracted implementation of NHibernate's event listeners, but that's why I like it. A) I can unit test a behavior without bringing NHibernate into the picture B) I can use these behaviors outside of NHibernate (say the repository is client implementation that wraps REST service calls) C) NH's event listeners can be a real pain in the ass ;)

这篇关于最佳库模式的ASP.NET MVC的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆