ASP.net MVC 控制器 - 构造函数的使用

发布于 2024-10-07 23:31:24 字数 386 浏览 0 评论 0原文

我正在开发 ASP.net MVC 应用程序,并且有一个关于为控制器使用构造函数的问题。

我正在使用 Entity Framework 和 linq to Entities 来处理所有数据事务。我需要访问我的实体模型来执行几乎所有控制器操作。当我第一次开始编写应用程序时,我在每个 Action 方法的开头创建一个实体对象,执行我需要的任何工作,然后返回结果。

我意识到我正在为每个操作方法一遍又一遍地创建相同的对象,因此我为实体对象创建了一个私有成员变量,并开始在每个控制器的构造函数中实例化它。现在,每个方法仅引用该私有成员变量来完成其工作。

我还在问自己哪条路是对的。我想知道 A.) 哪种方法最合适? B.) 在构造函数方法中,这些对象的存活时间是多长? C.) 构造函数方法是否存在性能/完整性问题?

I'm working on an ASP.net MVC application and I have a question about using constructors for my controllers.

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.

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.

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

陪你搞怪i 2024-10-14 23:31:24

你问的是正确的问题。

答:在每个操作方法中创建这种依赖关系绝对是不合适的。 MVC 的主要功能之一是能够分离关注点。通过加载具有这些依赖项的控制器,您可以使控制器变得更厚。这些应该被注入到控制器中。依赖注入 (DI) 有多种选项。通常,这些类型的对象可以注入构造函数或属性中。我的偏好是构造函数注入。

B. 这些对象的生命周期将由垃圾收集器决定。 GC 不是确定性的。因此,如果您的对象与资源受限服务(数据库连接)有连接,那么您可能需要确保自己关闭这些连接(而不是依赖于 dispose)。很多时候,“生命周期”问题被分离到控制反转 (IOC) 容器中。那里有很多。我最喜欢的是 Ninject。

C. 实例化成本可能是最小的。数据库事务成本是您可能需要关注的地方。您可能需要研究一个称为“工作单元”的概念。本质上,数据库可以处理比一次保存/更新操作更大的事务。增加事务大小可以带来更好的数据库性能。

希望这能让你开始。

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.

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.

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.

Hope that gets you started.

鸢与 2024-10-14 23:31:24

RRavens 有一些出色的见解。我想向您展示如何实施他的建议。

最好首先为要实现的数据访问类定义一个接口:

public interface IPostRepository 
{
    IEnumerable<Post> GetMostRecentPosts(int blogId);
}

然后实现一个数据类。实体框架上下文的构建成本很低,并且当您不处理它们时,您可能会得到不一致的行为,因此我发现通常最好将所需的数据提取到内存中,然后处理上下文。

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(); 
        }
    }
}

现在您的控制器可以接受这些存储库之一作为构造函数参数:

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 允许您使用自己的控制器工厂代替默认的控制器工厂,因此您可以指定您的 IoC 框架(如 Ninject)决定如何创建控制器。您可以设置注入框架,以了解当您请求 IPostRepository 时,它应该创建一个 PostRepository 对象。

这种方法的一大优点是它使您的控制器可进行单元测试。例如,如果您想确保模型在没有帖子时收到消息,您可以使用 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));

这使得测试特定行为变得容易您期望控制器执行操作,而无需设置数据库或任何类似的特殊操作。像这样的单元测试很容易编写、具有确定性(它们的通过/失败状态基于代码,而不是数据库内容)并且速度快(您通常可以在一秒钟内运行一千个这样的测试)。

RCravens has some excellent insights. I'd like to show how you can implement his suggestions.

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.

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:

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).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文