ASP.net MVC Controller-Utilisation du constructeur

je travaille sur un ASP.net MVC application et j'ai une question sur l'utilisation de constructeurs pour mes contrôleurs.

J'utilise Entity Framework et linq to Entities pour toutes mes transactions de données. J'ai besoin d'accéder à mon modèle d'Entité pour presque toutes mes actions du contrôleur. Quand j'ai commencé à écrire l'application, je créais un objet entity au début de chaque méthode D'Action, en exécutant tout le travail dont j'avais besoin et en retournant ensuite mon résultat.

j'ai réalisé que je créais le même objet encore et encore pour chaque méthode d'action donc j'ai créé une variable membre privé pour L'objet Entity et j'ai commencé à l'instancier dans le constructeur pour chaque controller. Maintenant, chaque méthode ne fait référence à cette variable membre privé que pour faire son travail.

je me pose encore des questions sur le bon chemin. Je me demande A.) Quelle méthode est la plus appropriée? B.) dans la méthode du constructeur, Quelle est la durée de vie de ces objets? C.) sont là des problèmes de performance/intégrité avec la méthode du constructeur?

Merci

32
demandé sur BZink 2010-12-19 01:25:15

2 réponses

vous posez les bonnes questions.

A. Il n'est certainement pas approprié de créer ces dépendances à l'intérieur de chaque méthode d'action. Une des principales caractéristiques de MVC est la possibilité de séparer les préoccupations. En chargeant votre controller avec ces dépendances, vous faites le controller pour thick. Ceux-ci doivent être injectés dans le contrôleur. Il existe différentes options pour l'injection de dépendance (DI). Généralement ces types d'objets peuvent être injectés dans le constructeur ou dans une propriété. Je préfère l'injection par le constructeur.

B. La durée de vie de ces objets sera déterminée par le éboueur. GC n'est pas déterministe. Donc, si vous avez des objets qui ont des connexions à des services limités en ressources (connexions de base de données), alors vous pouvez avoir besoin d'être sûr que vous fermez ces connexions vous-même (au lieu de compter sur dispose). Plusieurs fois les préoccupations de "durée de vie" sont séparées dans un conteneur d'inversion de contrôle (IOC). Il existe de nombreuses y. Ma préférence est Ninject.

J'espère que cela vous aidera à démarrer.

Bob

27
répondu rcravens 2013-04-12 20:47:28

RCravens a d'excellentes idées. J'aimerais vous montrer comment vous pouvez mettre en œuvre ses suggestions.

il serait bon de commencer par définir une interface pour la classe data access à implémenter:

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

puis implémenter une classe de données. Les contextes cadres d'Entity sont peu coûteux à construire, et vous pouvez avoir un comportement incohérent quand vous ne les disposez pas, donc je trouve qu'il est généralement mieux de tirer les données que vous voulez dans la mémoire, et puis disposer de la cadre.

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

maintenant votre controller peut accepter un de ces dépôts comme argument constructeur:

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 vous permet d'utiliser votre propre Controller Factory au lieu de la valeur par défaut, de sorte que vous pouvez spécifier que votre cadre du CIO comme Ninject décide de la façon dont les Controllers sont créés. Vous pouvez configurer votre cadre d'injection pour savoir que lorsque vous demandez un Ipostrépositaire, il doit créer un objet Postrépositaire.

Un grand avantage de cette approche c'est qu'il rend testables vos controllers. Par exemple, si vous voulez vous assurer que votre model reçoit un Message quand il n'y a pas de message, vous pouvez utiliser un framework moqueur comme Moq pour configurer un scénario où votre dépôt ne renvoie pas de message:

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

cela rend facile de tester le comportement spécifique que vous attendez de vos actions de controller, sans avoir besoin de configurer votre base de données ou quoi que ce soit de spécial comme ça. Les tests unitaires comme celui-ci sont faciles à écrire, déterministes (leur statut de réussite/échec est basé sur le code, pas le contenu de base de données), et rapide (vous pouvez souvent exécuter un millier de ces dans une seconde).

28
répondu StriplingWarrior 2010-12-19 00:18:04