一些Asp.NET MVC2最佳实践,用于管理业务服务层的胖控制器

时间:2022-07-23 16:36:05

My controllers are getting large and out of hand.

我的控制器变得越来越大而且失控。

A typical controller does the following:

典型的控制器执行以下操作:

  • It determines whether a given user has access to a given resource.
  • 它确定给定用户是否可以访问给定资源。

  • It validates the ViewModel.
  • 它验证ViewModel。

  • It translates the ViewModel into the DTOModel for persistence.
  • 它将ViewModel转换为DTOModel以实现持久性。

  • It calls repositories to update/create new objects and associated other new objects.
  • 它调用存储库来更新/创建新对象和关联的其他新对象。

  • It accesses data in multiple repository helper classes
  • 它访问多个存储库帮助程序类中的数据

  • It checks whether users get notifiied.
  • 它会检查用户是否收到通知。

  • It calls helpers to send emails
  • 它呼叫助手发送电子邮件

  • It logs data to the database via other repository objects
  • 它通过其他存储库对象将数据记录到数据库

  • etc...

In short, they ORCHESTRATE a lot of things. I'd like to move everything into a Services layer, but haven't really seen any implemented patterns in code samples that I like. I've looked at some of the open source projects like KiGG, Oxite, codecampserver, etc...but none of them are really solving the problem of shrinking my Controllers. I'd like to avoid passing around a lot of the HTTPContext stuff, but maybe this isn't possible.

简而言之,他们很多东西都是ORCHESTRATE。我想把所有东西都移到一个服务层,但我还没有真正看到我喜欢的代码示例中的任何实现模式。我看过一些像KiGG,Oxite,codecampserver等开源项目......但是它们都没有真正解决缩小我的控制器的问题。我想避免传递很多HTTPContext的东西,但也许这是不可能的。

Are there some other projects, best-practices I could be looking at? I'm building a large workflow/data input application.

还有其他一些项目,我可以看到的最佳实践吗?我正在构建一个大型工作流/数据输入应用程序。

Thanks for some links and suggestions

感谢您的一些链接和建议

5 个解决方案

#1


3  

I don't know of any real examples to show off the top of my head, because I think I came up with my MVC apps's controller -> service -> repository layering scheme based on random questions and answers I found by browsing SO.

我不知道有什么真实的例子可以展示我的头脑,因为我想我想出了我的MVC应用程序的控制器 - >服务 - >基于随机问题的存储库分层方案和我通过浏览SO找到的答案。

However, what I can give you is an example of how to organize the bullets you listed into how it would fit in the way I've structured my service layer. This may not be the best way, but this is how I'm doing my large-scale mvc app. This should give you an idea how to structure it in your own application

但是,我能给你的是一个例子,说明如何组织你列出的子弹,使其符合我构建服务层的方式。这可能不是最好的方法,但这就是我正在做我的大型mvc应用程序的方式。这应该让您了解如何在自己的应用程序中构建它

My service layer combines one service class per business unit. So if my application has projects, and each project has a person I would have a ProjectService class and a PersonService class.

我的服务层为每个业务单位组合一个服务类。因此,如果我的应用程序有项目,并且每个项目都有一个人,我将拥有一个ProjectService类和一个PersonService类。

Based upon your description of how your controllers work, my controller's actions work in the following way.

根据您对控制器工作方式的描述,我的控制器的操作以下列方式工作。

1) Get the current user's information and call the appropriate service class's authorization method. So if the user is trying to modify a project's details, I would pass the user ID and project ID to the ProjectService's AuthorizeUser method. This means if I change the way I authorize users for projects I only have to change the authorization method and not every controller.

1)获取当前用户的信息并调用相应的服务类的授权方法。因此,如果用户试图修改项目的详细信息,我会将用户ID和项目ID传递给ProjectService的AuthorizeUser方法。这意味着如果我改变授权用户项目的方式,我只需要更改授权方法而不是每个控制器。

2) Viewmodels are created, saved, and destroyed in the service layer. The service layer takes the viewmodel, validates it (raises an exception or validation result if it fails), and then converts it into a data object, which it will then pass to the repository for saving. It also requests the data object from the repository, converts it into a viewmodel and returns it down to the controller.

2)在服务层中创建,保存和销毁视图模型。服务层获取视图模型,验证它(如果失败则引发异常或验证结果),然后将其转换为数据对象,然后将其传递到存储库以进行保存。它还从存储库请求数据对象,将其转换为viewmodel并将其返回给控制器。

3) Logging of all actions occurs in the service layer. This can be automatic based on the action being presented (attempting to save an object to the db) or your controller can explicitly call the service layer to log the action.

3)所有操作的记录都发生在服务层中。这可以基于所呈现的操作(尝试将对象保存到数据库)自动执行,或者您的控制器可以显式调用服务层来记录操作。

The whole point of this is to consolidate common functionality into an easily maintainable layer. If you change how your viewmodel converts into a DTO, it is VERY easy to know where to make the change and to make it once. If you need to change your logging, user access, or even if you want to change how you retrieve certain data from the repositories it is one simple area to change rather than having to hunt down all your controllers and modify them directly.

这一点的重点是将常用功能整合到易于维护的层中。如果您更改视图模型转换为DTO的方式,则很容易知道在何处进行更改并将其更改为一次。如果您需要更改日志记录,用户访问权限,或者即使您想要更改从存储库中检索某些数据的方式,也只需更改一个简单的区域,而不必直接搜索所有控制器并对其进行修改。

Edit: This keeps controllers small as they only really contain a few calls to the service layer and that's it (Authorize, perform action, Show View). End Edit

编辑:这使控制器变小,因为它们实际上只包含对服务层的一些调用,而这就是它(授权,执行操作,显示视图)。结束编辑

Finally, the asp.net website has a tutorial for performing validation in a service layer. That tutorial can be found here.

最后,asp.net网站有一个在服务层执行验证的教程。该教程可以在这里找到。

#2


3  

You should check out this great MVCConf session about Putting Your Controllers On A Diet.

你应该看看这个关于把你的控制器放在饮食上的MVCConf会议。

#3


1  

It seems to me like you are relying on your controller to play too many roles.

在我看来,你依靠你的控制器来扮演太多角色。

When designing a controller, I typically think about it as controlling access to a single type of resource. This is to mean for example if I were creating this page you are reading right now, where you can post answers and comments, I would have 2 controllers, one for answers, and another for comments. I would avoid adding two actions to my question controller for unrelated resource access. There are exceptions to this, but they are few and far between.

在设计控制器时,我通常将其视为控制对单一类型资源的访问。这意味着,例如,如果我正在创建您正在阅读的页面,您可以在其中发布答案和评论,我将有2个控制器,一个用于答案,另一个用于评论。我会避免向我的问题控制器添加两个动作以获取不相关的资源访问权限。这有例外,但它们很少而且很远。

Also, the basic functionality of each controller is that it should validate the input (even when it is validated in the browser because anyone can edit a request), convert the input into the objects needed to pass to the service layer (or business logic) and validate the response from the service layer before converting it back to objects usable by the view and to serve back to the user. Everything else should be handled in the service layer, keeping the controller thin and predictable.

此外,每个控制器的基本功能是它应该验证输入(即使它在浏览器中验证,因为任何人都可以编辑请求),将输入转换为传递到服务层(或业务逻辑)所需的对象并在将服务层转换回视图可用的对象之前验证服务层的响应并返回给用户。应该在服务层处理其他所有内容,保持控制器的精简和可预测性。

#4


0  

Here at my company we have 5 projects in total:

在我公司,我们总共有5个项目:

  • View
  • Controller, in wich we only put the logic for the action methods, and it's helpers
  • 控制器,我们只把动作方法的逻辑,它的助手

  • Business Logic, where we put the specfic logic for the operations, in you case this would be were the helpers for the emails, the business validations,and where we would call the repositories to update and create objects
  • 业务逻辑,我们为操作提供了特定的逻辑,在这种情况下,这将是电子邮件的帮助者,业务验证,以及我们将存储库称为更新和创建对象的位置

  • Data Access, where we put the repositories for queries and data operations.
  • 数据访问,我们将存储库放在查询和数据操作中。

  • ORM, where we put the Database Model, and classes to exchange data between each layer
  • ORM,我们放置数据库模型,以及在每个层之间交换数据的类

In this case, all projects have a reference to the ORM, then, the view has a reference to the controller, the controller to the business logic, and the business logic to the data access.

在这种情况下,所有项目都引用了ORM,然后,视图具有对控制器的引用,对业务逻辑的控制器以及对数据访问的业务逻辑。

#5


0  

Well, some of that is dependent on what you mean by "the controller is". If your using data annotations for validation and action filters for logging, that's perfectly fine. There isn't any logic in the controller there. Are you using ViewModels and strongly typed views? It seems that the model that your controller is working with should already be the simplified DTO, as opposed to having the full entity and creating a DTO to send back. I have trouble imagining a situation where a controller would send an email, or check that it was sent. That should be handed off to a service layer. I would also look hard at a controller that is operating on "associated objects". It should probably be calling a single method on the repository that handles that.

好吧,其中一些取决于你所说的“控制器是什么”。如果您使用数据注释进行验证和操作过滤器进行日志记录,那就完全没问题了。控制器中没有任何逻辑。您使用的是ViewModels和强类型视图吗?看起来你的控制器正在使用的模型应该是简化的DTO,而不是拥有完整的实体并创建一个DTO来发回。我无法想象控制器会发送电子邮件或检查它是否已发送的情况。应将其移交给服务层。我也会仔细研究一个在“关联对象”上运行的控制器。它可能应该在处理它的存储库上调用单个方法。

I haven't opened up Nerd Dinner, so I can't swear to what is in there, but it might be worth examining?

我没有打开书呆子晚餐,所以我不能发誓那里的东西,但是值得研究一下?

#1


3  

I don't know of any real examples to show off the top of my head, because I think I came up with my MVC apps's controller -> service -> repository layering scheme based on random questions and answers I found by browsing SO.

我不知道有什么真实的例子可以展示我的头脑,因为我想我想出了我的MVC应用程序的控制器 - >服务 - >基于随机问题的存储库分层方案和我通过浏览SO找到的答案。

However, what I can give you is an example of how to organize the bullets you listed into how it would fit in the way I've structured my service layer. This may not be the best way, but this is how I'm doing my large-scale mvc app. This should give you an idea how to structure it in your own application

但是,我能给你的是一个例子,说明如何组织你列出的子弹,使其符合我构建服务层的方式。这可能不是最好的方法,但这就是我正在做我的大型mvc应用程序的方式。这应该让您了解如何在自己的应用程序中构建它

My service layer combines one service class per business unit. So if my application has projects, and each project has a person I would have a ProjectService class and a PersonService class.

我的服务层为每个业务单位组合一个服务类。因此,如果我的应用程序有项目,并且每个项目都有一个人,我将拥有一个ProjectService类和一个PersonService类。

Based upon your description of how your controllers work, my controller's actions work in the following way.

根据您对控制器工作方式的描述,我的控制器的操作以下列方式工作。

1) Get the current user's information and call the appropriate service class's authorization method. So if the user is trying to modify a project's details, I would pass the user ID and project ID to the ProjectService's AuthorizeUser method. This means if I change the way I authorize users for projects I only have to change the authorization method and not every controller.

1)获取当前用户的信息并调用相应的服务类的授权方法。因此,如果用户试图修改项目的详细信息,我会将用户ID和项目ID传递给ProjectService的AuthorizeUser方法。这意味着如果我改变授权用户项目的方式,我只需要更改授权方法而不是每个控制器。

2) Viewmodels are created, saved, and destroyed in the service layer. The service layer takes the viewmodel, validates it (raises an exception or validation result if it fails), and then converts it into a data object, which it will then pass to the repository for saving. It also requests the data object from the repository, converts it into a viewmodel and returns it down to the controller.

2)在服务层中创建,保存和销毁视图模型。服务层获取视图模型,验证它(如果失败则引发异常或验证结果),然后将其转换为数据对象,然后将其传递到存储库以进行保存。它还从存储库请求数据对象,将其转换为viewmodel并将其返回给控制器。

3) Logging of all actions occurs in the service layer. This can be automatic based on the action being presented (attempting to save an object to the db) or your controller can explicitly call the service layer to log the action.

3)所有操作的记录都发生在服务层中。这可以基于所呈现的操作(尝试将对象保存到数据库)自动执行,或者您的控制器可以显式调用服务层来记录操作。

The whole point of this is to consolidate common functionality into an easily maintainable layer. If you change how your viewmodel converts into a DTO, it is VERY easy to know where to make the change and to make it once. If you need to change your logging, user access, or even if you want to change how you retrieve certain data from the repositories it is one simple area to change rather than having to hunt down all your controllers and modify them directly.

这一点的重点是将常用功能整合到易于维护的层中。如果您更改视图模型转换为DTO的方式,则很容易知道在何处进行更改并将其更改为一次。如果您需要更改日志记录,用户访问权限,或者即使您想要更改从存储库中检索某些数据的方式,也只需更改一个简单的区域,而不必直接搜索所有控制器并对其进行修改。

Edit: This keeps controllers small as they only really contain a few calls to the service layer and that's it (Authorize, perform action, Show View). End Edit

编辑:这使控制器变小,因为它们实际上只包含对服务层的一些调用,而这就是它(授权,执行操作,显示视图)。结束编辑

Finally, the asp.net website has a tutorial for performing validation in a service layer. That tutorial can be found here.

最后,asp.net网站有一个在服务层执行验证的教程。该教程可以在这里找到。

#2


3  

You should check out this great MVCConf session about Putting Your Controllers On A Diet.

你应该看看这个关于把你的控制器放在饮食上的MVCConf会议。

#3


1  

It seems to me like you are relying on your controller to play too many roles.

在我看来,你依靠你的控制器来扮演太多角色。

When designing a controller, I typically think about it as controlling access to a single type of resource. This is to mean for example if I were creating this page you are reading right now, where you can post answers and comments, I would have 2 controllers, one for answers, and another for comments. I would avoid adding two actions to my question controller for unrelated resource access. There are exceptions to this, but they are few and far between.

在设计控制器时,我通常将其视为控制对单一类型资源的访问。这意味着,例如,如果我正在创建您正在阅读的页面,您可以在其中发布答案和评论,我将有2个控制器,一个用于答案,另一个用于评论。我会避免向我的问题控制器添加两个动作以获取不相关的资源访问权限。这有例外,但它们很少而且很远。

Also, the basic functionality of each controller is that it should validate the input (even when it is validated in the browser because anyone can edit a request), convert the input into the objects needed to pass to the service layer (or business logic) and validate the response from the service layer before converting it back to objects usable by the view and to serve back to the user. Everything else should be handled in the service layer, keeping the controller thin and predictable.

此外,每个控制器的基本功能是它应该验证输入(即使它在浏览器中验证,因为任何人都可以编辑请求),将输入转换为传递到服务层(或业务逻辑)所需的对象并在将服务层转换回视图可用的对象之前验证服务层的响应并返回给用户。应该在服务层处理其他所有内容,保持控制器的精简和可预测性。

#4


0  

Here at my company we have 5 projects in total:

在我公司,我们总共有5个项目:

  • View
  • Controller, in wich we only put the logic for the action methods, and it's helpers
  • 控制器,我们只把动作方法的逻辑,它的助手

  • Business Logic, where we put the specfic logic for the operations, in you case this would be were the helpers for the emails, the business validations,and where we would call the repositories to update and create objects
  • 业务逻辑,我们为操作提供了特定的逻辑,在这种情况下,这将是电子邮件的帮助者,业务验证,以及我们将存储库称为更新和创建对象的位置

  • Data Access, where we put the repositories for queries and data operations.
  • 数据访问,我们将存储库放在查询和数据操作中。

  • ORM, where we put the Database Model, and classes to exchange data between each layer
  • ORM,我们放置数据库模型,以及在每个层之间交换数据的类

In this case, all projects have a reference to the ORM, then, the view has a reference to the controller, the controller to the business logic, and the business logic to the data access.

在这种情况下,所有项目都引用了ORM,然后,视图具有对控制器的引用,对业务逻辑的控制器以及对数据访问的业务逻辑。

#5


0  

Well, some of that is dependent on what you mean by "the controller is". If your using data annotations for validation and action filters for logging, that's perfectly fine. There isn't any logic in the controller there. Are you using ViewModels and strongly typed views? It seems that the model that your controller is working with should already be the simplified DTO, as opposed to having the full entity and creating a DTO to send back. I have trouble imagining a situation where a controller would send an email, or check that it was sent. That should be handed off to a service layer. I would also look hard at a controller that is operating on "associated objects". It should probably be calling a single method on the repository that handles that.

好吧,其中一些取决于你所说的“控制器是什么”。如果您使用数据注释进行验证和操作过滤器进行日志记录,那就完全没问题了。控制器中没有任何逻辑。您使用的是ViewModels和强类型视图吗?看起来你的控制器正在使用的模型应该是简化的DTO,而不是拥有完整的实体并创建一个DTO来发回。我无法想象控制器会发送电子邮件或检查它是否已发送的情况。应将其移交给服务层。我也会仔细研究一个在“关联对象”上运行的控制器。它可能应该在处理它的存储库上调用单个方法。

I haven't opened up Nerd Dinner, so I can't swear to what is in there, but it might be worth examining?

我没有打开书呆子晚餐,所以我不能发誓那里的东西,但是值得研究一下?