Mai 2019

Volume 34, numéro 5

[Le programmeur au travail]

Codage Naked : collections Naked

Par Ted Neward | Mai 2019

Ted NewardBienvenue, NOFers. Heure du dernier, j’ai ajouté le type de domaine de l’orateur avec un nombre de propriétés, ainsi que d’un nombre de conventions sur les propriétés qui fournissent des conseils et des annotations (ou, pour être plus honnête, directions) à l’interface utilisateur dans la procédure de valider ou de présenter ces propriétés à la utilisateur. Une chose que n’a pas aborder, toutefois, est comment un objet de domaine donné peut avoir des références à plusieurs de quelque chose. Par exemple, haut-parleurs ont souvent plusieurs discussions qu’ils peuvent donner ou pouvant Professional expertise dans une ou plusieurs rubriques. N désigne comme « collections » et il existe quelques règles concernant leur fonctionnement qui sont légèrement différentes de la conversation précédente.

Nous allons voir sur l’attribution des conférenciers des discussions et les rubriques, nous sont ?

Concepts naked

Pour commencer, abandonnez tous les tableaux, tous les ans d’expérience qui Entrez ici. N ne rend pas utiliser de tableaux pour les propriétés de la collection et à la place repose entièrement sur des objets de collection (IEnumerable < T >-dérivées) pour contenir zéro-à-plusieurs d’un autre type. Le manuel NABI fortement recommande que ces collections fortement typées (l’utilisation de génériques) et NABI n’autorise pas de plusieurs associations de types de valeur (par exemple, les chaînes, les types énumérés et ainsi de suite), car NABI estime que si le type est quelque chose de « important » suffisamment pour justifier l’association, qu'il doit être un type de domaine à part entière.

Par conséquent, par exemple, si je veux capturer la notion d’une rubrique (tels que «C#, » « Java » ou « Systèmes distribués ») dans le système de conférence, où d’autres approches de programmation peuvent vous autoriser à vous en sortir avec un simple « liste de chaînes » comme type de propriété, insiste n que rubrique soit un type d’objet de domaine complet (autrement dit, une classe publique avec des propriétés), avec ses propres règles de domaine. Il est raisonnable que la liste des rubriques peut être un ensemble fixe, toutefois, donc je vais amorcer la base de données avec l’ensemble complet de rubriques qui souhaite que mon conférence à prendre en compte.

De même, même si une conversation peut être simplement un titre, il est vraiment une série d’actions : un titre, une description, une rubrique (auquel il appartient ou fait référence) et est fourni par les haut-parleurs une (ou plus). Clairement, j’ai un peu modélisation de domaine devant me encore.

collections Naked

Dans bien des égards, le moyen le plus simple pour commencer est avec les classes de domaine de discussion et rubrique eux-mêmes, absence de toutes les connexions entre elles (ou haut-parleurs). À ce stade, une grande partie de ce que j’écris ici pour chacun d’eux doit être relativement trivial et simple, comme Figure 1 montre.

Figure 1 les Classes de domaine de discussion et de la rubrique

public class Talk
  {
    [NakedObjectsIgnore]
    public virtual int Id { get; set; }
    [Title]
    [StringLength(100, MinimumLength = 1,
       ErrorMessage = "Talks must have an abstract")]
    public virtual string Title { get; set; }
    [StringLength(400, MinimumLength = 1,
       ErrorMessage = "Talks must have an abstract")]
    public virtual string Abstract { get; set; }
  }
  public class TalkRepository
  {
    public IDomainObjectContainer Container { set; protected get; }
    public IQueryable<Talk> AllTopics()
    {
      return Container.Instances<Talk>();
    }
  }
  [Bounded]
  public class Topic
  {
    [NakedObjectsIgnore]
    public virtual int Id { get; set; }
    [Title]
    [StringLength(100, MinimumLength = 1,
       ErrorMessage = "Topics must have a name")]
    public virtual string Name { get; set; }
    [StringLength(400, MinimumLength = 1,
       ErrorMessage = "Topics must have a description")]
    public virtual string Description { get; set; }
  }
  public class TopicRepository
  {
    public IDomainObjectContainer Container { set; protected get; }
    public IQueryable<Topic> AllTopics()
    {
      return Container.Instances<Topic>();
    }
  }

Jusqu’ici, c’est assez simple. (Il y a évidemment autres choses qui pourraient et/ou doivent être ajoutés à chacun de ces deux classes, mais il obtient le point plutôt bien). Un nouvel attribut utilisé, [délimité] est une indication à NABI que la liste complète (et immuable) des instances peut et doivent être conservée en mémoire sur le client et présentée à l’utilisateur comme une liste déroulante dans laquelle choisir. En conséquence, puis, la liste complète des rubriques doit être établie dans la base de données, ce qui est effectué le plus simple dans la classe DbInitializer à partir du projet « SeedData », dans la méthode Seed (comme nous l’avons vu dans préalable de colonnes dans cette série), illustrée Figure 2 .

Figure 2 Création d’une liste de rubriques

protected override void Seed(ConferenceDbContext context)
{
  this.Context = context;
  Context.Topics.Add(new Topic() { Name = "C#",
    Description = "A classical O-O language on the CLR" });
  Context.Topics.Add(new Topic() { Name = "VB",
    Description = "A classical O-O language on the CLR" });
  Context.Topics.Add(new Topic() { Name = "F#",
    Description = "An O-O/functional hybrid language on the CLR" });
  Context.Topics.Add(new Topic() { Name = "ECMAScript",
    Description = "A dynamic language for browsers and servers" });
  Context.SaveChanges();
  // ...
}

Cela fournit une liste (assez réduite, mais utile) de rubriques à partir de laquelle travailler. Par ailleurs, si vous êtes jouer au jeu domestique et écrire le code manuellement, n’oubliez pas d’ajouter le TalkRepository au menu principal, en l’ajoutant à la méthode MainMenus dans NakedObjectsRunSettings.cs dans le projet serveur. En outre, assurez-vous que les deux types de référentiel sont également répertoriés dans la méthode de Services dans le même fichier.

L’essentiel, un Talk est fourni par un présentateur et se trouve sur une rubrique donnée. Je vais ignorer le scénario plus complexe lorsqu’une discussion est donnée par deux enceintes, ou si une discussion traversera plusieurs rubriques, pour plus de simplicité pour l’instant. Par conséquent, pour la première étape, nous allons ajouter traite de l’orateur :

private ICollection<Talk> _talks = new List<Talk>();
public virtual ICollection<Talk> Talks
{
  get { return _talks; }
  set { _talks = value; }
}

Si vous générez et exécutez le projet, vous verrez « Discussions » apparaissent en tant que collection (table) dans l’interface utilisateur, mais il sera vide. Je pourrais, bien sûr, ajouter des discussions dans SeedData, mais en général, les orateurs doivent être en mesure d’ajouter des discussions dans leurs profils.

Actions naked

Cela est possible en ajoutant une action à la classe de l’orateur : Une méthode qui s’affiche, « comme par magie, » comme un élément sélectionnable dans le menu « Actions » lorsqu’un objet de l’orateur est dans l’affichage. Comme les propriétés, actions sont découverts par le biais de la magie de la réflexion, par conséquent, toutes les besoins se produise consiste à créer une méthode publique sur la classe de l’orateur :

public class Speaker
{
  // ...
  public void SayHello()
  {
  }
}

Maintenant, quand générer et à exécuter après affichant un conférencier, le menu « Actions » s’affiche et à l’intérieur, « SayHello » s’affiche. Actuellement, elle ne fait rien ; Il serait intéressant, comme point de départ, à placer au moins un message à l’utilisateur. Dans le monde NABI, cela en rendant l’utilisation d’un service, un objet dont l’objectif est de fournir des fonctionnalités supplémentaires qui n’appartient pas à un objet de domaine particulier. Dans le cas générales « messages à l’utilisateur », cela est fournie par un service générique, défini par NABI lui-même dans l’interface IDomainObjectContainer. J’ai besoin d’une instance d’un d’eux afin de faire quoi que ce soit, toutefois, et NABI utilise l’injection de dépendances pour fournir un à la demande : Déclarez une propriété sur la classe de l’orateur de type IDomainObjectContainer et NABI garantira que chaque instance possède un :

public class Speaker
{
  public TalkRepository TalkRepository { set; protected get; }
  public IDomainObjectContainer Container { set; protected get; }

L’objet conteneur a une méthode « InformUser » utilisée pour transmettre les messages généraux à l’utilisateur, par conséquent, l’aide de l’action SayHello est aussi simple que :

public class Speaker
{
  // ...
  public void SayHello()
  {
    Container.InformUser("Hello!");
  }
}

Mais j’ai commencé avec la volonté de permettre à l’utilisateur à ajouter une discussion au répertoire d’un intervenant donné ; plus précisément, j’ai besoin de capturer le titre de la présentation, le résumé (ou la description, car « abstract » est un mot réservé dans C#), ainsi que la rubrique à laquelle appartient cette conférence. Appeler cette méthode « EnterNewTalk », puis, j’ai l’implémentation suivante :

public void EnterNewTalk(string title, string description, Topic topic)
{
  var talk = Container.NewTransientInstance<Talk>();
  talk.Title = title;
  talk.Abstract = description;
  talk.Speaker = this;
  Container.Persist<Talk>(ref talk);
  _talks.Add(talk);
}

Plusieurs choses se produisent ici, nous allons donc décompresser. Tout d’abord, j’utilise le IDomainObjectContainer pour créer une instance (non persistantes) temporaire de la présentation. Cela est nécessaire, car le n doit être en mesure d’injecter des « hooks » dans chaque objet de domaine pour pouvoir fonctionner sa magie. (C’est pourquoi toutes les propriétés doivent être virtuelles, afin que n peut gérer la synchronisation de l’interface utilisateur-to-object, par exemple.) Ensuite, les propriétés de la conférence sont définies, et le conteneur est également utilisé pour conserver la discussion ; Si ce n’est pas le cas, la discussion n’est pas un objet persistant et ne seront pas stockée lorsque j’ajoute la discussion à la liste de l’orateur de discussions.

Il est légitime de se demander, cependant, comment l’utilisateur spécifié ces informations à la méthode EnterNewTalk lui-même. Une fois encore, les merveilles de la réflexion sont au travail : NABI choisit d’utiliser les noms de paramètre et les types de paramètres de la méthode et construit une boîte de dialogue pour capturer ces éléments, y compris le sujet en lui-même. N’oubliez pas lors de la rubrique a été annotée avec « Délimité » ? L’instruction qui n pour générer la liste des rubriques dans cette boîte de dialogue d’une liste déroulante, incroyablement simple sélectionner une rubrique dans la liste. (Il doit être facile de déduire à ce stade que que j’ajoute des rubriques pour le système, ils amèneront tous être ajoutés à cette liste déroulante sans aucun travail supplémentaire requis)

À présent, il est raisonnable de suggérer que discussions de création doit être un élément pris en charge par le TalkRepository, pas sur la classe de l’orateur lui-même et, comme vous pouvez le voir dans Figure 3, il s’agit d’une refactorisation facile à faire.

Figure 3 prenant en charge la création de la discussion avec TalkRepository

public class Speaker
  {
    public TalkRepository TalkRepository { set; protected get; }
    public IDomainObjectContainer Container { set; protected get; }
    public void EnterNewTalk(string title, string description, Topic topic)
    {
      var talk = TalkRepository.CreateTalk(this, title, description, topic);
      _talks.Add(talk);
    }
  }
  public class TalkRepository
  {
    public IDomainObjectContainer Container { set; protected get; }
    public Talk CreateTalk(Speaker speaker, string title, string description,
                Topic topic)
    {
      var talk = Container.NewTransientInstance<Talk>();
      talk.Title = title;
      talk.Abstract = description;
      talk.Speaker = speaker;
      Container.Persist<Talk>(ref talk);
      return talk;
    }
  }

De plus, ce faisant, le menu « Discussions » sera automatiquement orner avec un nouvel élément de menu, « CreateTalk », qui sera, là encore, par le biais de la magie de la réflexion, créer automatiquement une boîte de dialogue pour entrer les données nécessaires pour créer une discussion. Haut-parleurs, bien sûr, ne peut pas simplement être tapés, donc NABI vais en faire un champ « inspecteur », ce qui signifie que n s’attend à recevoir un objet de l’orateur à être déplacé et déposé dans ce champ. (Pour voir cela en action dans l’interface de NABI Gemini par défaut, allumez l’application, sélectionnez un intervenant, puis cliquez sur le bouton « Volet échange », le bouton de double-transforme en bas de l’affichage. Le haut-parleur sélectionné sera décaler vers la droite de l’écran, et l’interface d’accueil s’affiche, permettant de sélectionner l’élément « Communiquer discussions/Create ». Faites glisser de champ de nom de l’orateur à l’intervenant dans la boîte de dialogue Créer parler et voilà : l’orateur est sélectionné.)

Il est absolument essentiel de comprendre ce qui se passe ici : J’ai maintenant ont deux l’interface utilisateur des chemins différents (création d’une discussion dans le menu de niveau supérieur, ou création d’une discussion à partir d’un intervenant donné) qui permettent de deux scénarios de navigation d’utilisateur différent, avec peu d’efforts et de duplication de zéro. Le TalkRepository se préoccupe toutes les choses « CRUD » et « Communiquer » liés et le haut-parleur utilise ce code, tout en conservant l’interaction utilisateur entièrement au sein de l’orateur si c’est la façon dont l’utilisateur souhaite disposer.

Pour résumer

Cela n’est pas boîte à outils de l’interface utilisateur de votre grand-père. Dans uniquement quelques lignes de code, j’ai ont une interface exploitable (et, en tenant compte de ces données est en cours stockées et récupérées à partir d’un standard back end SQL, un système de stockage de données) qui peut, au moins pour le moment, être utilisé directement par les utilisateurs. Plus important encore, rien de tout cela est dans un format propriétaire ou un langage, il s’agit droite C#, droites SQL Server et l’interface utilisateur lui-même est Angular. Il existe quelques opérations pour discuter de l’interface par défaut de n, dont je parlerai dans la partie suivante, y compris les installations de l’authentification et l’autorisation. En attendant, toutefois... Amusez-vous bien !


Ted Newardest consultant de polytechnology basés à Seattle, conférencier et un mentor. Il a écrit une multitude d’articles, créés et co-écrit une dizaine d’ouvrages et participe partout dans le monde. Vous pouvez le contacter ted@tedneward.com ou lire son blog à l’adresse blogs.tedneward.com.

Merci à l'expert technique suivant d'avoir relu cet article : Richard Pawson


Discuter de cet article sur le forum MSDN Magazine