我正在开发一个ASP.net MVC应用程序,我有一个关于为我的控制器使用构造函数的问题。
我正在使用Entity Framework和linq来实现所有数据事务的实体。我需要为几乎所有的控制器操作访问我的实体模型。当我第一次开始编写应用程序时,我在每个Action方法的开头创建了一个实体对象,执行我需要的任何工作然后返回我的结果。
我意识到我正在为每个操作方法反复创建相同的对象,因此我为Entity对象创建了一个私有成员变量,并开始在每个控制器的构造函数中实例化它。现在每个方法只引用私有成员变量来完成它的工作。
我仍在质疑自己哪种方式是正确的。我想知道A.)哪种方法最合适? B.)在构造函数方法中,这些对象生存了多长时间? C.)构造函数方法是否存在性能/完整性问题?
谢谢
答案 0 :(得分:30)
RCravens有一些很好的见解。我想说明如何实施他的建议。
最好先定义数据访问类的接口来实现:
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允许您使用自己的Controller Factory代替默认值,因此您可以指定像Ninject这样的IoC框架决定如何创建控制器。您可以设置注入框架,以便知道当您要求IPostRepository时,它应该创建一个PostRepository对象。
这种方法的一大优势是它使您的控制器可以进行单元测试。例如,如果您想确保模型在没有帖子时获得Message,您可以使用像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));
这使您可以轻松地测试控制器操作所期望的特定行为,而无需设置数据库或类似的任何特殊行为。像这样的单元测试很容易编写,确定性(它们的通过/失败状态是基于代码,而不是数据库内容),而且速度很快(通常可以在一秒钟内运行其中的一千个)。
答案 1 :(得分:29)
你在问正确的问题。
一个。在每个操作方法中创建此依赖项绝对不合适。 MVC的一个主要特征是能够分离关注点。通过使用这些依赖项加载控制器,您可以使控制器变厚。这些应注入控制器。依赖注入(DI)有多种选择。通常,这些类型的对象可以注入构造函数或属性中。我的偏好是构造函数注入。
B中。这些对象的生命周期将由垃圾收集器确定。 GC不具有确定性。因此,如果您拥有与资源受限服务(数据库连接)连接的对象,那么您可能需要确保关闭自己的连接(而不是依赖于dispose)。很多时候,“终生”问题被分离为控制反转(IOC)容器。那里有很多。我的偏好是Ninject。
℃。实例化成本可能很小。数据库事务成本是您可能希望集中注意力的地方。你可能想要研究一个叫做“工作单元”的概念。实质上,数据库可以处理大于一个保存/更新操作的事务。增加事务大小可以提高数据库性能。
希望能让你开始。
鲍勃