asp.net-mvc ASP.net MVC 控制器 - 构造函数的使用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4480485/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
ASP.net MVC Controller - Constructor usage
提问by BZink
I'm working on an ASP.net MVC application and I have a question about using constructors for my controllers.
我正在开发一个 ASP.net MVC 应用程序,我有一个关于为我的控制器使用构造函数的问题。
I'm using Entity Framework and linq to Entities for all of my data transactions. I need to access my Entity model for nearly all of my controller actions. When I first started writing the app I was creating an entity object at the beginning of each Action method, performing whatever work I needed to and then returning my result.
我正在使用 Entity Framework 和 linq to Entities 来处理我的所有数据事务。我需要访问我的实体模型以获取几乎所有控制器操作。当我第一次开始编写应用程序时,我在每个 Action 方法的开头创建了一个实体对象,执行我需要的任何工作,然后返回我的结果。
I realized that I was creating the same object over and over for each action method so I created a private member variable for the Entity object and started instantiating it in the constructor for each controller. Now each method only references that private member variable to do its work.
我意识到我正在为每个操作方法一遍又一遍地创建相同的对象,因此我为 Entity 对象创建了一个私有成员变量,并开始在每个控制器的构造函数中实例化它。现在每个方法只引用那个私有成员变量来完成它的工作。
I'm still questioning myself on which way is right. I'm wondering A.) which method is most appropriate? B.) in the constructor method, how long are those objects living? C.) are there performance/integrity issues with the constructor method?
我仍在质疑自己哪种方式是正确的。我想知道 A.) 哪种方法最合适?B.) 在构造方法中,这些对象存活多久?C.) 构造函数方法是否存在性能/完整性问题?
采纳答案by rcravens
You are asking the right questions.
你在问正确的问题。
A. It is definitely not appropriate to create this dependencies inside each action method. One of the main features of MVC is the ability to separate concerns. By loading up your controller with these dependencies, you are making the controller for thick. These should be injected into the controller. There are various options for dependency injection (DI). Generally these types of objects can be either injected into the constructor or into a property. My preference is constructor injection.
A. 在每个操作方法中创建这种依赖项绝对不合适。MVC 的主要特性之一是能够分离关注点。通过使用这些依赖项加载您的控制器,您可以使控制器变厚。这些应该被注入到控制器中。依赖注入 (DI) 有多种选择。通常,这些类型的对象可以注入构造函数或属性。我的偏好是构造函数注入。
B. The lifetime of these objects will be determined by the garbage collector. GC is not deterministic. So if you have objects that have connections to resource constrained services (database connections) then you may need to be sure you close those connections your self (instead of relying on dispose). Many times the 'lifetime' concerns are separated out into an inversion of control (IOC) container. There are many out there. My preference is Ninject.
B. 这些对象的生命周期由垃圾收集器决定。GC 不是确定性的。因此,如果您的对象与资源受限服务(数据库连接)有连接,那么您可能需要确保自己关闭这些连接(而不是依赖于处理)。很多时候,“生命周期”问题被分离到控制反转 (IOC) 容器中。外面有很多。我的偏好是 Ninject。
C. The instantiation costs are probably minimal. The database transactions cost are where you probably want to focus your attention. There is a concept called 'unit of work' you may want to look into. Essentially, a database can handle transactions larger than just one save/update operation. Increasing the transaction size can lead to better db performance.
C. 实例化成本可能很小。数据库事务成本是您可能希望集中注意力的地方。您可能需要研究一个称为“工作单元”的概念。本质上,一个数据库可以处理比一个保存/更新操作更大的事务。增加事务大小可以带来更好的数据库性能。
Hope that gets you started.
希望这能让你开始。
回答by StriplingWarrior
RCravens has some excellent insights. I'd like to show how you can implement his suggestions.
RCravens 有一些出色的见解。我想向您展示如何实施他的建议。
It would be good to start by defining an interface for the data access class to implement:
最好先为数据访问类定义一个接口来实现:
public interface IPostRepository
{
IEnumerable<Post> GetMostRecentPosts(int blogId);
}
Then implement a data class. Entity Framework contexts are cheap to build, and you can get inconsistent behavior when you don't dispose of them, so I find it's usually better to pull the data you want into memory, and then dispose the context.
然后实现一个数据类。实体框架上下文的构建成本很低,并且当您不处理它们时可能会出现不一致的行为,因此我发现通常最好将您想要的数据拉入内存,然后处理上下文。
public class PostRepository : IPostRepository
{
public IEnumerable<Post> GetMostRecentPosts(int blogId)
{
// A using statement makes sure the context is disposed quickly.
using(var context = new BlogContext())
{
return context.Posts
.Where(p => p.UserId == userId)
.OrderByDescending(p => p.TimeStamp)
.Take(10)
// ToList ensures the values are in memory before disposing the context
.ToList();
}
}
}
Now your controller can accept one of these repositories as a constructor argument:
现在您的控制器可以接受这些存储库之一作为构造函数参数:
public class BlogController : Controller
{
private IPostRepository _postRepository;
public BlogController(IPostRepository postRepository)
{
_postRepository = postRepository;
}
public ActionResult Index(int blogId)
{
var posts = _postRepository.GetMostRecentPosts(blogId);
var model = new PostsModel { Posts = posts };
if(!posts.Any()) {model.Message = "This blog doesn't have any posts yet";}
return View("Posts", model);
}
}
MVC allows you to use your own Controller Factory in lieu of the default, so you can specify that your IoC framework like Ninject decides how Controllers are created. You can set up your injection framework to know that when you ask for an IPostRepository it should create a PostRepository object.
MVC 允许您使用自己的控制器工厂代替默认值,因此您可以指定像 Ninject 这样的 IoC 框架来决定控制器的创建方式。您可以设置您的注入框架以了解当您请求 IPostRepository 时,它应该创建一个 PostRepository 对象。
One big advantage of this approach is that it makes your controllers unit-testable. For example, if you want to make sure that your model gets a Message when there are no posts, you can use a mocking framework like Moq to set up a scenario where your repository returns no posts:
这种方法的一大优点是它使您的控制器可进行单元测试。例如,如果您想确保您的模型在没有帖子时收到消息,您可以使用像 Moq 这样的模拟框架来设置您的存储库不返回帖子的场景:
var repositoryMock = new Mock<IPostRepository>();
repositoryMock.Setup(r => r.GetMostRecentPosts(1))
.Returns(Enumerable.Empty<Post>());
var controller = new BlogController(repositoryMock.Object);
var result = (ViewResult)controller.Index(1);
Assert.IsFalse(string.IsNullOrEmpty(result.Model.Message));
This makes it easy to test the specific behavior you're expecting from your controller actions, without needing to set up your database or anything special like that. Unit tests like this are easy to write, deterministic (their pass/fail status is based on the code, not the database contents), and fast (you can often run a thousand of these in a second).
这使得测试您期望从控制器操作中获得的特定行为变得容易,而无需设置您的数据库或任何类似的特殊操作。像这样的单元测试易于编写、确定性(它们的通过/失败状态基于代码,而不是数据库内容)且速度快(您通常可以在一秒钟内运行一千个这样的测试)。