5 votes

Renvoi de POCOs spécifiquement formés aux actions ASP.NET MVC

Dans mon projet ASP.NET MVC, mes actions appellent généralement une couche de service pour obtenir des données. J'utilise la même douzaine de POCOs pour tous mes modèles. Je prévois également d'utiliser la couche Service dans les applications console et peut-être d'exposer une api web à un moment donné.

Pour rendre les opérations de ma base de données plus efficaces, ma couche de service n'hydrate que les propriétés du modèle qui sont pertinentes pour la méthode particulière (qui, à ce stade, est principalement déterminée par les besoins des actions de mon contrôleur).

Ainsi, par exemple, je pourrais avoir une classe Order avec des propriétés Id, Name, Description, Amount, Items . Pour un appel de service donné, il se peut que je n'aie besoin de remplir que les champs suivants Id, Name, Items . Un consommateur de ce service ne saura pas nécessairement que Amount est 0 uniquement parce qu'il n'a pas rempli la propriété.

De même, le consommateur ne saura pas si Items est vide parce qu'il n'y a en fait aucun élément, ou si cette méthode de service particulière ne remplit tout simplement pas cette propriété.

Et pour un troisième exemple, disons que l'une de mes vues affiche une ItemCount . Je ne veux pas remplir entièrement mon site Web. Items j'ai juste besoin d'une propriété supplémentaire sur mon "modèle". Je ne veux pas ajouter cette propriété à mon POCO que les autres méthodes de service utiliseront, car elle ne sera pas remplie ailleurs.

La solution naturelle est donc de créer une POCO conçue spécifiquement pour cette méthode, avec uniquement ces 3 propriétés. De cette façon, le consommateur peut savoir que toutes les propriétés seront remplies avec leurs valeurs réelles. L'inconvénient de cette solution est que je vais finir par écrire des tonnes de modèles de forme similaire.

Des conseils sur la méthode la plus efficace ?

10voto

sblom Points 15074

Vous pourriez utiliser des types nuls pour indiquer les propriétés manquantes avec un nul.

Par exemple :

class Order {
    public int Id {get;set;}
    public string Name {get;set;}
    public string Description {get;set;}
    public decimal? Amount {get;set;}
    public List<Item> Items {get;set;}
}

Et puis si Items == null il n'était pas réglé. Si c'est un new List<Item>() c'est un ensemble mais vide. Idem pour Amount . Si Amount.HasValue == false il n'était pas réglé. Si Amount.Value est de 0,0d, c'est réglé et l'article est gratuit.

3voto

David Hoerster Points 18815

Pourquoi n'utilisez-vous pas la projection LINQ ?

Une méthode de service fait quelque chose comme :

return DbContext.Orders.Select(o => new { Id = o.Id, Name = o.Name, Description = o.Description });

tandis que l'autre méthode de service fait quelque chose comme :

return DbContext.Orders.Select(o => o);

Je ne sais pas comment votre application est architecturée, mais cela peut être un moyen d'éviter de créer des centaines de POCO.

J'espère que cela vous aidera ! Bonne chance.

1voto

adrift Points 24386

Vous pourriez passer dans un sélecteur Func qui renvoie dynamic :

public IEnumerable<dynamic> GetOrders(Func<Order, dynamic> selector) { ... }

Je ne suis pas sûr de la manière dont vous accédez aux données, mais l'exemple suivant montre comment cela fonctionnerait en utilisant un fichier de type List<T> :

class Program
{
    static void Main(string[] args)
    {
        var service = new Service();
        var orderNames = service.GetOrders(o => new { o.Name });

        foreach (var name in orderNames)
            Console.WriteLine(name.Name);

        Console.ReadLine();
    }
}

public class Service
{
    private List<Order> _orders = new List<Order>
        {
            new Order { Id = 1, Name = "foo", Description = "test order 1", Amount = 1.23m },
            new Order { Id = 2, Name = "bar", Description = "test order 1", Amount = 3.45m },
            new Order { Id = 3, Name = "baz", Description = "test order 1", Amount = 5.67m }
        };

    public IEnumerable<dynamic> GetOrders(Func<Order, dynamic> selector)
    {
        return _orders.Select(selector);
    }
}

public class Order
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public decimal Amount { get; set; }
}

1voto

L'utilisation de valeurs nulles est une bonne solution, mais elle présente l'inconvénient que vous n'avez aucun moyen de faire correspondre les champs obligatoires. En d'autres termes, vous ne pouvez pas utiliser d'attribut obligatoire sur une propriété quelconque. Ainsi, si un champ est obligatoire dans certaines vues, vous n'avez aucun moyen de le représenter. Si vous n'avez pas besoin de la validation des champs obligatoires, cela ne pose pas de problème. Sinon, vous avez besoin d'un moyen de représenter les fichiers qui sont effectivement utilisés, puis d'écrire un fournisseur de validation personnalisé.

Une façon simple de le faire est d'utiliser une classe "Mask" avec les mêmes noms de propriétés que la classe originale, mais avec tous les champs booléens : une valeur vraie signifie que le champ est utilisé.

J'ai utilisé une solution similaire dans un système où les propriétés à afficher sont configurées dans un fichier de configuration... c'était donc l'unique option pour moi puisque je n'avais pas la possibilité de représenter toutes les combinaisons de propriétés. CEPENDANT, j'ai utilisé la classe "Mask" également dans la vue, ce qui m'a permis de faire tout le travail avec une seule vue... avec beaucoup de "si".

Maintenant, si vos 150 méthodes de service et probablement environ 150 vues... sont toutes différentes, alors il est peut-être plus simple d'utiliser aussi plusieurs classes ... c'est-à-dire dans le pire des cas 150 classes... le travail supplémentaire pour les écrire est négligeable si on le compare à l'effort de préparer 150 vues différentes. Cependant, cela ne signifie pas que vous avez besoin de 150 classes POCO. Vous pouvez utiliser une classe POCO unique qui est copiée dans une classe adéquate juste dans la couche de présentation. L'avantage de cette approche est que vous pouvez mettre différents attributs de validation sur les différentes classes et vous n'avez pas besoin d'écrire un fournisseur de validation personnalisé.

1voto

contactmatt Points 3953

Retourner le POCO entier avec les types nullables comme mentionné par @sbolm. Vous pouvez ensuite créer un ViewModel par vue de page MVC qui reçoit un modèle avec les propriétés spécifiques dont il a besoin. Cela demandera plus de performance (insignifiante) et de code, mais cela permet de garder votre couche de service propre, et de garder vos vues "muettes" dans le sens où elles ne reçoivent que ce dont elles ont besoin et n'ont pas de relation directe avec la couche de service.

C'est-à-dire (exemple de classe de @sbolm)

class Order {
    public int Id {get;set;}
    public string Name {get;set;}
    public string Description {get;set;}
    public decimal? Amount {get;set;}
    public List<Item> Items {get;set;}
}

// MVC View only needs to know the name and description, manually "map" the POCO properties into this view model and send it to the view

class OrderViewModel {
    public string Name {get;set;}
    public string Description {get;set;}
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X