Architecture Silverlight LOB – Concepts et technologies (Partie 3)

L’image suivante décrit comment une application peut être mise en couche dans de multiple modules. Ces modules peuvent communiquer les un avec les autre par des messages bien définis et des contrats définis dans le module de la couche d’Infrastructure.

En outre, ils récupèrent et manipulent des objets de données par les Services WCF qui sont convenus pour le DDD (appelé WCF RIA Services). Ces objets de données sont souvent appelés des entités et peuvent représenter votre « Domain Model » et sont perssister dans le data store en utilisant « Data Access Objects ».

WCF RIA Services fournit des voies pour vous pour manipuler vos entités dans le client de la même façon comme vous feriez dans le serveur , des données de changement et des changements de soumission, mettre à jour des données ou soumettre des modification.

Une telle application compte souvent sur l’infrastructure de côté de serveur pour l’authentification et l’autorisation, l’enregistrement et la gestion des erreurs. Du côté client ils comptent sur des structures que l’aide réduit, harmonise et réutilise le code dans le développement de module tant à la logique de présentation qu’aux vues de présentation. Quelques couches peuvent avoir les composants qui dépendent de composants d’autres couches.
Dans de tels cas, nous réduisons ces dépendances avec des conteneurs IoC ou des structures semblables comme MEF.

Le développement dans une architecture N-Tiers peut être ennuyeux quand vous voulez réutiliser le code qui doit être spécifié tant du côté serveur que du côté client quand il s’agit de la validation ou de la logique métier.

Notez que la couche de mapping entre les « Data Entities » et le « Domain entities ». cette couche est optionnelle et peut être utile quand vous ne voulez pas exposer vos « data entities » comme « domain model ». Cela aide à réaliser l’ignorance de persistance et vous permet aussi de créer des classes simples et légères pour transporter des données. Pour la simplicité, je ne vais pas utiliser cette couche aux postes prochains, donc mes entités de données seront mes entités de domaine.

Suite : prochain poste

PS : cet article est une adaptation en français d’une suite d’articles par Manuel Felício

Kamel DJELLAL
Chef de projet
EDIS CONSULTING – GROUPE UBISIDE

http://www.ubiside.fr/

Architecture Silverlight LOB – Concepts et technologies (Partie 4)

Dans ce post je vais commencer avec un simple « Business Domain Model » et je vais crée une couche de domain autour du modele.

Dans cet exemple notre application sera une application Web pour une société qui vend des produits et cela peut être utilisé par ses clients pour faire des commandes et ses salariés pour les traiter. Ces commandes ont les détails qui consistent en produits et des quantités. Chaque produit peut avoir une catégorie de produit. Quand un client soumet une commande, un salarié de la société peut traiter cet commande et préparer les articles pour l’expédition.

La société a quelques départements et chaque salarié appartient à un département. Cela signifie que les salariés du Service des ventes devront traiter les commandes; les salariés du département de Ressources Humain pourront recruter de nouveaux salariés; et les salariés du Département Financier pourront observer quelques diagrammes et des rapports avec la statistique comment les ventes se développent en fonction du temps, des rapports de ventes, etc.

l’authentication et l’authorization seront ajoutés plutard.

Pour faire simple, j’ai créent le modèle de données suivant :

modele

Ce modèle sera tant notre modèle de données que le modèle de domaine. C’est très simple et comme j’ai dit au poste précédent, créant un modèle de domaine séparé est facultatif parce que vous pouvez toujours faire bien avec vos entités de données. Puisque nous utilisons la « Entity FRamework » nous profiterons de LinqToEntitiesDomainService, un Service de Domaine spécifique qui traite avec les objets de l’EF.

Puisque nous commençons par notre couche de Domaine, nous définirons un Service de Domaine qui peut être utilisé de notre application Silverlight (ou d’autres clients aussi) et créer les opérations CRUD (creation, mise à jour, suppression). Je suis sûr que vous avez remarqué combien de code est produit quand vous utilisez le modèle de Service de Domaine de visual Studio. Créez juste une classe de base pour vos services de domaine et ajoutez les méthodes suivantes :

#region Basic CRUD
protected IQueryable<T> GetQuery<T>()
    where T : class
{
    return this.GetRepository<T>().GetQuery();
}
protected void Insert<T>(T entity)
    where T : class
{
    this.GetRepository<T>().Add(entity);
}
protected void Update<T>(T entity)
    where T : class
{
    this.GetRepository<T>().AttachAsModified(entity, this.ChangeSet.GetOriginal(entity));
}
protected void Delete<T>(T entity)
    where T : class
{
    this.GetRepository<T>().Delete(entity);
}
#endregion
 
protected IRepository<T> GetRepository<T>()
    where T : class
{
    return this.Container.Resolve<IRepository<T>>();
}

La propriété conteneurs est un cas IUNITYCONTAINER, que jegarde dans le Service de Domaine de base. J’ai d’habitude d’une classe singleton commune où le conteneur IoC est disponible mais je crée aussi un conteneur enfant pour chaque Service de Domaine. Cela me permet d’enregistrer des cas spécifiques dans le conteneur enfant et l’utiliser seulement dans les limites de mon Service de Domaine. C’est utile pour résoudre des repositories parce que mes repositories dépendent de l’ObjectContext qui est injecté via l’injection de dépendance. Voici comment on procède :

public abstract class DomainRepositoryService<TContext> : LinqToEntitiesDomainService<TContext>, IDbContextManager
    where TContext : ObjectContext, IDbContext, new()
{
    protected IUnityContainer Container { get; private set; }
    /// <summary>
    /// Create a child container to use THIS as the IDbContextManager implementation when resolving repositories
    /// Ensures that other threads / domain services can use the main container to resolve repositories
    /// and use its own instance as the IDbContextManager for the repositories they need
    /// </summary>
    /// <param name="context">The DomainServiceContext instance</param>
    public override void Initialize(DomainServiceContext context)
    {
        base.Initialize(context);
 
        this.Container = IoC.Current.Container.CreateChildContainer();
        this.Container.RegisterInstance<IDbContextManager>(this);
    }
 
    #region IDbContextManager Members
    public IDbContext GetDbContext()
    {
        return this.ObjectContext;
    }
    #endregion
    //...
}

Cela a dit, le Service de Domaine spécifique pour notre application Silverlight y ressemblera :

[EnableClientAccess()]
public class MyAppService : DomainRepositoryService<MyAppEntities>
{
    #region Customer
    public IQueryable<Customer> GetCustomers()
    {
        return base.GetQuery<Customer>();
    }
    public void InsertCustomer(Customer entity)
    {
        base.Insert(entity);
    }
    public void UpdateCustomer(Customer entity)
    {
        base.Update(entity);
    }
    public void DeleteCustomer(Customer entity)
    {
        base.Delete(entity);
    }
    #endregion
    //repeat for the other entities...
}

Remarquez comment ma Couche de Domaine ne se soucie pas vraiment de comment les entités sont stockées ou gérées avec EF (entity framework). Il le délègue la responsabilité à un objet qui met en oeuvre l’interface IREPOSITORY.

Cette interface peut être générique et ressemble souvent :

public interface IRepository<TEntity>
    where TEntity : class
{
    TEntity GetEntity(int id);
    IQueryable<TEntity> GetQuery();
    void Add(TEntity entity);
    void Update(TEntity entity);
    void Delete(TEntity entity);
    /// <summary>
    /// Abstraction on RIA's AttachAsModified extension method
    /// </summary>
    /// <param name="current"></param>
    /// <param name="original"></param>
    void AttachAsModified(TEntity current, TEntity original = null);
}

Pour la simplicité j’ai inclus la méthode AttachAsModified dans l’IREPOSITORY. Cette méthode est importante parce que RIA exige que l’entité actuelle étant mise à jour soit attachée dans l’ObjectContext. Une autre option devrait créer une interface séparée qui provient d’IREPOSITORY et ajouter la méthode là. C’est que l’interface IREPOSITORY est un concept générique qui peut être utilisé avec beaucoup de technologies d’accès de données.

Notez que du moment vous créez votre Service de Domaine et exposez vos entités, imediatement une autre personne/équipe peut commencer à construire l’application utilisant les entités exposées, tandis qu’une autre équipe continue à travailler sur la couche serveur ajoutant des métadonnées et-ou la logique de validation qui sera aussi disponible pour l’application client.

Suite : prochain poste

PS : cet article est une adaptation en français d’une suite d’articles par Manuel Felício

Kamel DJELLAL
Chef de projet
EDIS CONSULTING – GROUPE UBISIDE

http://www.ubiside.fr/

News & retour vers le blog

Bonjour chers lecteurs,

Après une absence de plusieurs mois, du à ma faible disponibilité pour me consacrer au blog, je reviens en force pour vous mettre l’eau à la bouche et vous prévenir de l’arrivée de plusieurs sujets :

– Ma participation aux journées SQL Server 2012 et vous présenter mon avis dessus et les enseignements qu’on peut tirer de la présentation de « DENALI »

– Quelques articles sur Sharepoint 2007

Ces articles arrivent bientôt.
Ils sont déjà prêts sur brouillon et reste à affiner pour vous les présenter correctement.

Techniquement vôtre,

Kamel DJELLAL
Chef de projet
EDIS CONSULTING – GROUPE UBISIDE

http://www.ubiside.fr/

MVC et NHibernate

MVC vous laisse choisir la meilleure voie pour votre projet de stocker et récupérer un modele de données. Nous utilisons NHIBERNATE parce que c’est l’ORM le plus mature en .NET et il est basé sur un projet Java mûr : HIBERNATE.
Il y a une quantité remarquable de documentation en ligne et gratuite associée à NHIBERNATE et sa performance (exécution) et la fiabilité est sans égal.

NHibernate n’est pas votre seule option. « LINQ to SQL » est aussi populairement appareillé avec MVC. C’est une structure rapide et facile pour vous faire commencer. Quoi que commençant avec NHIBERNATE soient un peu plus d’exigence initialement, le compromis d’apprendre qu’ORM plus robuste dépassera rapidement cet investissement initial.

NHibernate est très flexible, mais nous utiliserons seulement une petite fraction de sa puissance dans cet article. Pour une couverture plus en profondeur, je suggère de lire le livre « NHibernate in Action » (http://www.lirmm.fr/~gouaich/upload/books/Manning%20NHibernate%20in%20Action.pdf), parce qu’il couvre la partie caching, batching et l’utilisation de query avancée en utilisant le Langage d’interrogation (HQL : Hibernate Query Langage).

Quand utiliser Active REcord, Query Object, ou le Repository Pattern ?

Il y a plusieurs manières pour une application de mettre en oeuvre la persistance. Active
Record, Query Object, et Repository sont parmi les design pattern les plus communs utilisés dans le développement logiciel d’entreprise. Tous ces modèles sont décrits plus en détail dans le livre : « Patterns of Enterprise Application Architecture » par « Martin Fowler ».
Le Repository pattern isole le code d’accès de données en créant une couche de classes pour traduire en arrière au code d’application. Les avantages du Repository est que ce dernier met une séparation claire entre la logique d’application et le code d’accès d’ORM/Base de données. Le Repository a été popularisé plus récemment par le « Ruby on Rails ». Au lieu d’utiliser les Repository classes pour encapsuler le code d’ORM, il utilise les classes modèles eux-mêmes pour gérer l’accès de données. NHibernate peut aussi être configuré pour utiliser l’Active Record pattern.
Query Object est un pattern qui propage un certain nombre de petites classes légères qui contiennent des Query complexes. Cela peut empêcher votre Repository ou Active Record objects de devenir trop grand mais encore plus important peut encourager la réutilisation des query.

Je vous encourage à utiliser le Repository pattern parce qu’il est plus simple à comprendre, c’est aussi le plus courament utilisé dans des applications web .NET. Le Repository pattern isole le code d’accés au données en créant une couche de classes pour traduire en code applicatif. Il vous donne une place pour mettre le code qui traite avec n’importe quelles optimisations d’accès de données ou bizarreries qui peuvent aborder en utilisant un ORM. Dans ASP.NET MVC, la classe de Repository manipule l’accès de données et la classe de contrôleur coordonne la requête d’un utilisateur.

Utilisation du Repository Pattern :

Ce modèle garde notre code d’accès de données dans un jeu discret d’objets.
Cela nous laisse tester notre code d’ORM indépendamment de notre code de contrôleur.

Avant d’utiliser le Repository Pattern on a besoin de configurer NHibernate pour qu’il fonctionne avec notre base de données.
Pour cela nous utiliserons Fluent NHIBERNATE, un projet qui aide à simplifier la configuration et le mapping des classe pour NHIBERNATE.

Configuration de NHibernate Fluently :

D’abord nous créerons une deuxième instance de base de données pour exécuter nos tests de Repository. Cela protège l’autre instance d’être écrasé comme nous exécutons des tests de Repository.

La chose suivante à faire est créent un nouveau projet de test pour holder notre persistance appelé « Test.Persistance » .
Vous devrez télécharger et ajouter des références à la dernière copie de fichiers binaires NHIBERNATE Fluient. N’oubliez pas d’ajouter une référence au projet Web parce que c’est où les Repository et le code de configuration auront leurs cycle de vie.
À l’intérieur de notre projet Web, nous allons devoir ajouter un répertoire appelé Persistence. C’est où nous stockerons des classes liées à la connexion et à la configuration pour NHIBERNATE. Nous créerons aussi le sous-répertoire « Repositories » pour tenir les classes Repository pour notre projet. Nous ajouterons encore un sous-répertoire appelé « ClassMaps » pour tenir nos fichiers database-to-class mapping, mapping avec Fluent NHibernate.
Créons la class NHibernateConfiguration pour tenir les informations de configuration :

using System;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Tool.hbm2ddl;
 
 namespace GetOrganized.Persistence
 {
 public class NHibernateConfiguration
 {
 public static ISessionFactory SessionFactory { get; private set; }
 
public static void Init(IPersistenceConfigurer databaseConfig,
 Action<Configuration> schemaConfiguration)
 {
 SessionFactory = Fluently.Configure()
 .Database(
 databaseConfig)
 .Mappings(m => m.FluentMappings.
 AddFromAssemblyOf<NHibernateConfiguration>())
 .ExposeConfiguration(schemaConfiguration)
 .BuildSessionFactory();
 }
 
 public static ISession CreateAndOpenSession()
 {
 return SessionFactory.OpenSession();
 }
 }
 }

NHibernateConfiguration sera utilisé tant par le test que par le code de production, mais avant que nous puissions initialiser SessionFactory utile, nous devrons dresser le mapping de quelques classes à la base de données.

Mapping avec Fluent NHibernate :

Pour qu’un ORM fasse son travail, il a besoin d’instructions comment vos objets mappent les tables de votre base de données. La façon la plus simple de le faire est l’automapping.
L’automapping peut être configurée pour chercher un namespaces spécifique pour que seulement vos modeles de classes réelles soient mappés.
Cela empêche des classes de contrôleur, par exemple, d’être mappé à la base de données. L’automapping fonctionne en matchant des data type .NET au type de données équivalent pour la base de données. Par exemple, l’Entier .NET Int32 se traduirait aux données int dans le Serveur SQL. L’ajout d’automapping à votre configuration Fluent NHIBERNATE est aussi simple que l’ajoutant du code suivant à notre Fluently.Configure() dans NHibernateConfiguration :

var model = AutoMap.AddFromAssemblyOf<NHibernateConfiguration>()
.Where(t => t.Namespace == "GetOrganized.Models" );
var configuration = Fluently.Configure()
.Database(databaseConfig)
.Mappings(m => m.AutoMappings.Add(model));
configuration.BuildSessionFactory();

Cependant, l’automapping vous prendra seulement jusqu’ici, comme quand vous devez spécifier des relations spécifiques et des types de données. C’est pourquoi nous travaillerons avec des classe maps. Traditionnellement ces maps existent autant que fichiers XML, un pour chaque classe. Notre classe Todo aurait un fichier XML incorporé appelé « Todo-mapping.xml ».
Fluent NHibernate nous permet de mapper des classes en utilisant des classes C# qui hérite de la class « ClassMap ».
L’utilisation des classes C# pour mapper rend plus facile la vérification des erreurs de syntaxe et rend aussi la navigation des class map files plus faciles.
On va stocker tous nos maps dans un repertoire « Persistence/ClassMaps ».
Mappont Todo en utilisant Fluent NHibernate :

using FluentNHibernate.Mapping;
using GetOrganized.Models;
 
 namespace GetOrganized.Persistence.ClassMaps
 {
 public class TodoMap : ClassMap<Todo>
 {
 public TodoMap()
 {
 Id(x => x.Title);
 Map(x => x.Completed);
 Map(x => x.Outcome);
 References(x => x.Topic).ForeignKey().Not.LazyLoad();
 }
 }
 }

Comme vous avez vu, Fluent NHIBERNATE rend le mapping facile…

Kamel DJELLAL
Chef de projet
EDIS CONSULTING – GROUPE UBISIDE

http://www.ubiside.fr/

Groupement de contrôles pour créer un UserControl (Partie 2)

Réutiliser le User Control :
Dans le poste précedent, vous avez créé un nouveau UserControl (AddressControl) pour taper des informations d’adresse. En convertissant une partie de MainPage.xaml dans un UserControl, Expression Blend créé automatiquement les références et a ajouté l’élément à la page. Mais que faire si vous voulez ajouter le même UserControl à nouveau sur la même page? Ce poste vous montre comment procéder.

Pour ajouter un UserControl, dans ce cas, le AddressControl, à MainPage.xaml, il suffit de suivre les étapes suivante :

1. Continuer à partir de la section précédente et faire en sorte que MainPage.xaml soit ouvert sur ​​le plan de travail.

2. Ouvrez le panneau Actifs (Assets panel) dans Expression Blend et allez dans la catégorie de projet (Project category) en cliquant dessus.

Le AddressUserControl apparaît dans cette catégorie, comme le montre la Figure suivante :

assets panel

Avoir l’AddressUserControl ici peut ne pas être surprenant, mais ce que vous pouvez trouver surprenant, c’est que MainPage se trouve également ici. C’est dans cette catégorie parce que MainPage, comme AddressUserControl, est également un User control. Vous pouvez ouvrir la vue XAML pour AddressUserControl et MainPage et vérifier cette hypothèse. Comme vous l’avez vu, toutes les pages que vous avez créé en XAML servent de composants réutilisables et vous permettent de les imbriquer de User Control.

3. Faites glisser et déposez sur AddressUserControl MainPage.xaml sur le plan de travail. Un autre AddressUserControl est ajouté à MainPage.

4. Appuyez sur F5 pour exécuter l’application. L’application s’exécute dans la fenêtre du navigateur et affiche les deux AddressUserControls dans la même page. Ces deux contrôles se resemblent et fonctionnent de la même façon, mais vous permettent de stocker et d’afficher deux séries d’adresses.

Il ya plusieurs avantages à mettre quelque chose comme une adresse dans un UserControl, mais l’une de nos raisons préférés est que, après que cela devient un UserControl, il peut être géré de façon indépendante et entretenus. Par exemple, si vous changez le conteneur de disposition (layout container) du StackPanel à un GridPanel et réorganiser la façon dont les champs sont affichés, ils seront automatiquement mis à jour dans chaque page dans laquelle le AddressUserControl est utilisé.

Création de propriétés pour votre UserControl :

Après avoir créé un UserControl, il serait bon de l’initialiser (operation set) et de lire quelques valeurs que vous avez tapé dans chaque champ. Un moyen d’atteindre ce but c’est de créer des propriétés pour chaque champ. (L’autre façon d’y parvenir est de faire du Data binding, peut être que je le traiterais dans un autre poste).

Pour ajouter des propriétés à chacun des champs (à savoir StreetNumber, streetName, City, State, PostalCode, et Country) de la AddressUserControl créé dans poste précedent, ouvrez le fichier AddressUserControl.xaml.cs sur le plan de travail et ajouter le code suivant dans la classse AddressUserControl :

public string StreetNumber
{
  get
  {
    return txtStreetNumber.Text;
  }
  set
  {
   txtStreetNumber.Text = value;
  }
}
 
public string StreetName
{
  get
  {
   return txtStreetName.Text;
  }
  set
  {
   txtStreetName.Text = value;
  }
}
 
public string City
{
  get
  {
   return txtCity.Text;
  }
  set
  {
   txtCity.Text = value;
  }
}
 
public string State
{
  get
  {
   return txtState.Text;
  }
  set
  {
   txtState.Text = value;
  }
}
 
public string PostalCode
{
  get
  {
   return txtPostalCode.Text;
  }
  set
  {
   txtPostalCode.Text = value;
  }
}
 
public string Country
{
  get
  {
   return txtCountry.Text;
  }
  set
  {
   txtCountry.Text = value;
  }
}

Appuyez sur Ctrl+Maj+B pour compiler l’application, puis ouvrez le fichier MainControl.xaml en mode Création. Sélectionnez la première AddressUserControl et ouvrez le panneau de Propriétés. Dans la section Divers (Miscellaneous), vous trouverez désormais toutes les propriétés que vous venez d’ajouter. Modifiez les valeurs de ces propriétés et
Appuyez sur F5 pour exécuter l’application et vérifier si les valeurs apparaissent dans la
AddressUserControl.

Kamel DJELLAL
Chef de projet
EDIS CONSULTING – GROUPE UBISIDE

http://www.ubiside.fr/

Groupement de contrôles pour créer un UserControl (Partie 1)

Lorsque vous écrivez une application, l’application se compose de certains blocs de base que vous pouvez utiliser encore et encore. Ce bloc peut être une certaine logique qui existe comme un morceau de code (peut-être sous la forme d’une classe ou une méthode) ou sous la forme d’une interface utilisateur (qui est l’objet de la discussions ici).

Par exemple, votre application peut contenir une interface utilisateur simple qui permet aux
utilisateurs de votre site Web de taper des informations d’adresse. Vous utilisez peut-être ceci dans plusieurs pages; par exemple, vous pouvez l’utiliser pour recueillir l’adresse de l’utilisateur, l’adresse de travail, l’adresse d’expédition, et ainsi de suite. L’adresse peut se composé de plusieurs champs, tels que adresse, ville, et ainsi de suite. Silverlight vous permet de créer un élément d’interface utilisateur qui contient une autre interface utilisateur pour former un élément composite appelé un UserControl. En outre aux éléments d’interface utilisateur, UserControls vous permet d’ajouter un peu de logique telles que la validation dans le code.

Dans cette section, nous allons vous montrer comment créer un tel contrôle, ainsi que la façon de l’utiliser.

Exemple : créer un UserControl d’Adresse :

Dans cet exemple, nous allons vous montrer comment créer un type de contrôle que de nombreuses applications utilisent régulièrement – un contrôle pour recueillir des informations d’adresse.

Pour créer un contrôle d’adresses, vous devez avoir les champs suivants:

✓ Numero de la rue (Street Number)
✓ Nom de la rue (Street Name)
✓ Ville (City)
✓ État (State)
✓ ZIP ou code postal (Postal code)
✓ pays (Country)

Par souci de simplicité, vous pouvez définir tous ces champs comme des champs texte. Lire la suite pour voir comment.

1. Créer un nouveau projet Silverlight en choisissant Fichier ➪ Nouveau projet à partir du menu, et quand la boîte de dialogue Nouveau projet apparaît, sélectionnez Silverlight 4 Application + site Web et lui donner un nom approprié tel que AddressUserControlExample. Puis cliquez sur OK.
Un nouveau projet est créé et le fichier MainPage.xaml est représenté sur le plan de travail.

2. Double-cliquez sur le StackPanel dans le ToolsPanel pour l’ajouter à la MainPage. Faites glisser les poignées de redimensionnement pour définir la hauteur et la largeur du StackPanel à une dimension appropriée.

3. Ajouter six paires de TextBlocks et TextBoxes dans le plan de travail pour afficher les six champs d’adresse. Remplacer le texte par défaut dans le TextBlocks en double-cliquant dessus et en tapant sur ​​eux afin qu’ils lisent le numero de la rue, Nom de la rue, ville, État, code postal, et pays, comme le montre la figure suivante :

4. Ctrl+cliquez sur le TextBlocks un après l’autre jusqu’à ce que vous avez cliqué sur tous les TextBlocks sur le plan de travail. Ceci permet de sélectionner tous les TextBlocks et vous permet de modifier toutes les propriétés simultanément.

5. Allez dans le panneau de Propriétés, puis appuyez sur le bouton Gras dans le group text afin de rendre le texte dans tous les TextBlocks gras.

6. Ctrl+cliquez sur les contrôles TextBox un après l’autre jusqu’à ce que vous avez cliqué sur tous les TextBlocks sur le plan de travail.

7. Cliquez sur l’option Propriétés avancées à côté du champ de texte dans le Panneau Propriétés, et dans le menu, choisissez Réinitialiser (reset).

Le texte par défaut dans le contrôle TextBox est effacé et le code XAML pour les opérations que vous venez de réaliser devrait maintenant ressembler à ceci (certains balisage a été remplacé par ellipses […] Par souci de concision):

 <br />
<UserControl . . .> <br />
  <Grid x:Name=”LayoutRoot” Background=”White”> <br />
    <StackPanel HorizontalAlignment=”Left” <br />
    Margin=”8,22,0,167” Width=”291”> <br />
       <TextBlock Text=”Street Number” <br />
       TextWrapping=”Wrap” FontWeight=”Bold”/> <br />
       <TextBox TextWrapping=”Wrap”/> <br />
       <TextBlock Text=”Street Name” <br />
       TextWrapping=”Wrap” FontWeight=”Bold”/> <br />
       <TextBox TextWrapping=”Wrap”/> <br />
       <TextBlock Text=”City” TextWrapping=”Wrap” <br />
       FontWeight=”Bold”/> <br />
       <TextBox TextWrapping=”Wrap”/> <br />
       <TextBlock Text=”State” TextWrapping=”Wrap” <br />
       FontWeight=”Bold”/> <br />
       <TextBox TextWrapping=”Wrap”/> <br />
       <TextBlock Text=”Postal code” <br />
       TextWrapping=”Wrap” FontWeight=”Bold”/> <br />
       <TextBox TextWrapping=”Wrap”/> <br />
       <TextBlock Text=”Country” TextWrapping=”Wrap” <br />
       FontWeight=”Bold”/> <br />
       <TextBox TextWrapping=”Wrap”/> <br />
    </StackPanel> <br />
  </Grid> <br />
</UserControl> <br />

8. Utilisation de l’outil de sélection, sélectionnez le numéro de rue control TextBox du plan de travail et définir son nom à « txtStreetNumber » dans le panneau Propriétés. De même, définir le nom de rue, ville, État, Zones de texte Code postal, et pays à « txtStreetName », « txtCity », « txtState », « txtPostalCode », et « txtCountry », respectivement.
Donner un nom à ces champs sera utile lorsque vous définissez des valeurs pour plus tard.

9. Sélectionnez le StackPanel soit en cliquant dessus à partir des objets et Panneau Montage ou en utilisant l’outil de sélection et de sélection des StackPanel du plan de travail.

10. Faites un clic droit et choisissez en faire un contrôle utilisateur (Make Into User Control). Une UserControl boîte de dialogue apparaît, comme le montre la Figure suivante :

11. Remplacer UserControl1 dans le champ Nom en entrant AddressUserControl; puis cliquez sur OK. Un nouveau fichier nommé AddressUserControl.xaml est ajouté et l’ensemble des contenu du StackPanel être déplacée à ce nouveau contrôle.

12. Cliquez sur l’onglet MainPage.xaml sur le plan de travail. Le fichier MainPage.xaml s’ouvre et affiche un point d’exclamation en haut des champs d’adresse. Ceci apparait car le projet doit être compilé. Vous pouvez compiler le projet en appuyant sur Ctrl+Maj+B, ou vous pouvez appuyer sur F5 pour exécuter le projet.

13. Si vous avez compilé le projet, fermez le navigateur, et Expression Blend, cliquez sur le bouton sur le plan de travail XAML pour ouvrir la vue XAML de la MainPage.xaml.
Dans le fichier XAML, notez que tous les champs adresse que vous avez ajoutés sont
remplacée par une ligne unique qui contient un élément appelé
local: AddressUserControl:

 <br />
<UserControl ... <br />
xmlns:local=”clr-namespace:UserControls” <br />
x:Class=”AddressUserControlExample. <br />
MainPage” <br />
Width=”640” Height=”480”> <br />
  <Grid x:Name=”LayoutRoot” Background=”White”> <br />
    <local:AddressUserControl <br />
    HorizontalAlignment=”Left” Margin=”8,8,0,181” <br />
    Width=”291”/> <br />
  </Grid> <br />
</UserControl> <br />

Notez que dans l’élément UserControl, une ligne de départ avec le texte xmlns: local a été ajouté. Cet élément permet de savoir à Silverlight où le AddressUserControl vous venez d’ajouter est situé.

Prochain poste : on verra comment réutiliser ce UserControl et créer des propriétés pour ce dernier

Kamel DJELLAL
Chef de projet
EDIS CONSULTING – GROUPE UBISIDE

http://www.ubiside.fr/

Architecture Silverlight LOB – Concepts et technologies (Partie 7)

Dans ce poste, je vais parler de la construction d’un framework MVVM.

Comme je le disais dans le poste précédent, ce poste devrait être sur le OrderView. Ce view devrait permettre aux utilisateurs de choisir des éléments d’une liste de produits, les ajouter à l’ordre courant, choisir les quantités, voir le prix courant total et soumettre la commande. En outre, l’utilisateur doit être en mesure de filtrer, trier et avoir une pagination sur la liste des produits. Que devient le produit choisi, ajoutez-le à une autre collection ou de créer un détail de la commande fondé sur lui, puis mettre à jour certaines autres données sur l’UI et, enfin, soumettre les modifications sur le serveur. Le truc, c’est que tout le monde a sa propre façon de programmation et de la sorte quand vous vous retrouvez dans une équipe, vous pouvez constater que deux personnes code la même chose d’une manière différente, et on a un bug dans la situation A et l’autre a un bug dans la situation B. Ayant un bon cadre MVVM avec une méthodologie bien définie est indispensable pour prévenir ces situations. Dans ce poste, je veux parler des éléments essentiels que vous devez avoir dnas un bon cadre MVVM. Plus tard, je vais vous décrire un cadre MVVM sur lequel je travaille qui était basée sur WCF RIA Services, mais ne dépend pas vraiment de ??ça.

Depuis que nous suivons les meilleures pratiques, nous savons que l’utilisation d’une architecture MVVM bien que nous pouvons arriver à une solution dont l’extraction, le filtrage, le tri, la logique d’échange est entièrement séparé du view, ce qui nous permet d’avoir aussi des vues différentes pour le même view model. Par exemple, nous pouvons commencer avec l’aide d’un DataGrid et un DataPager par afficher nos articles, mais plus tard, fournir une vision nouvelle qui utilise les comboboxes pour sélectionner les options de tri, une zone de liste d’albums pour afficher les éléments et les boutons personnalisés pour la pagination. En outre, nous devrions être en mesure de séparer toute cette logique de sa logique d’accès aux données réelles pour pouvoir utiliser des objets fantaisie de notre modèle et de faire des tests unitaires pour nos ViewModels. Ce n’est pas une tâche facile mais c’est ce que je veux réaliser à partir de maintenant.

Eh bien, pour commencer,.NET/Silverlight nous offre déjà quelques classes et interfaces qui sont très pratiques pour les scénarios de MVVM.

¦ INotifyPropertyChanged – Utilisé pour déclencher un événement lors d’un changement de propriété. WPF/Silverlight binding framework : utilise cette interface pour mettre à jour la vue lors d’un changement de propriété.

¦ INotifyCollectionChanged – Utilisé pour déclencher un événement lorsque l’insérer, supprimer, effacer ou remplacer l’opération a été fait contre une collection. Les contrôles WPF/Silverlight qui ont une propriété ItemsSource utilisent généralement cette interface pour créer ou supprimer des éléments visuels dans un container. Par exemple, ListBoxes affiche de nouvelle ListBoxItems, DataGrids affiche de nouvelle DataGridRows.

¦ ICollectionView – Utilisé pour fournir des descriptions sorte de filtre, des descriptions de groupe, et la sélection de l’objet pour une collection IEnumerable et ont la vue d’afficher uniquement les éléments filtrés, triés selon les descriptions de tri et de mettre en évidence l’élément sélectionné. (a plus de fonctionnalités, mais ce sont les plus pertinentes pour le bien de ce poste).

¦ IPagedCollectionView – Utilisé pour fournir des options de pagination à une collection IEnumerable. Il est utilisé par la plupart DataPagers, qui font des appels à la MoveToPage (pageIndex int) la méthode et nous permet de s’inscrire à l’événement PageChanging chercher une nouvelle page d’entités à afficher.

¦ Il existe d’autres interfaces importantes comme IEditableObject, IEditableCollectionView mais je ne vais pas les couvrir dans ces postes. Ils sont utilisés pour mettre à jour les valeurs des propriétés d’un objet de façon atomique.

Malheureusement, WCF RIA Services collections n’appliquent pas ICollectionView ou IPagedCollectionView si nous devons fournir notre propre mécanisme de filtrage, de tri et de pagination des données. Toutefois, cela est assez facile à faire avec LINQ et des expressions et nous permet de composer des requêtes avec certains opérateurs de LINQ avec Silverlight. Silverlight a sa propre implémentation de IPagedCollectionView, appelé System.Windows.Data.PagedCollectionView mais il a été conçu pour fonctionner avec les collections en mémoire et non pas côté serveur « collections ». Dans cette série, je suis mon propre fournisseur IPagedCollectionView / mise en œuvre ICollectionView que j’ai conçu pour fonctionner avec tout type de sources de données. Quelle que soit l’option utilisée est entièrement au modèle de MVVM.

Donc, voyant voir comment mon framework MVVM fonctionne.

Tout d’abord, nous avons le concept de modèle:

public interface IDomainModel : IDisposable
{
    void RejectChanges();
    ISubmitOperation SaveChanges();
    ISubmitOperation SaveChanges(Action<ISubmitOperation> callback, object userState);
 
    IDomainQuery<T> GetQuery<T>();
 
    void Add<T>(T entity);
    void Remove<T>(T entity);
    void Attach<T>(T entity);
    void Detach<T>(T entity);
 
    bool HasChanges { get; }
    bool IsSubmitting { get; }
    bool IsLoading { get; }
}

Si vous êtes familier avec l’API DomainContext, vous trouverez cette interface très facile à comprendre. Cette interface modèle est censé vous fournir des fonctionnalités de base pour la récupération de données, de le (modifier et l’enregistrer. Pour des opérations plus spécifiques, vous pouvez étendre cette interface, ajouter des opérations spécifiques et vous avez votre ViewModel en dépendent.

L’interface IDomainQuery représente une requête sur un type d’entité certaines référentiel (quelle qu’elle soit). Comme une requête, il vous permet de filtrer, trier et la page de données.

public interface IDomainQuery : IDisposable
{
    void Cancel();
    void Load();
 
    event Action Loading;
    event Action<ILoadOperation> Loaded;
    event Action Canceled;
}
 
public interface IDomainQuery<T> : IDomainQuery
{
    /// <summary>
    /// Delegate that returns a lambda expression to apply in a Where operation
    /// </summary>
    Func<Expression<Func<T, bool>>> FilterExpression { get; set; }
 
    /// <summary>
    /// SortDescription values that are applied in a OrderBy/ThenBy operation
    /// </summary>
    Func<SortDescriptionCollection> SortDescriptions { get; set; }
 
    /// <summary>
    /// Contains the required data that are applied in a Skip and Take operation
    /// </summary>
    Func<PageDescription> PageDescription { get; set; }
 
    new event Action<ILoadOperation<T>> Loaded;
}
 
public sealed class PageDescription
{
    public int PageSize { get; set; }
    public int PageIndex { get; set; }
}

En outre, il a des événements pour vous permettre de savoir quand une operation a commencé et terminé. Il ya deux choses importantes à noter dans l’interface IDomainQuery. La premiere est le fait que la façon dont vous filtrer une IDomainQuery est de savoir comment vous filtrez une RIA Services EntityQuery et comment vous filtrez un objet IQueryable. Cela signifie qu’il est très facile à faire implémentations IDomainQuery que les dépôts de requête en mémoire ou des référentiels côté serveur (noter que EntityQuery ne pas mettre en œuvre IQueryable et ce n’est pas facile d’intégrer les deux concepts). L’autre chose importante est que le filtre, de tri, le mécanisme de la page est basée sur les délégués qui signifie que chaque fois que la requête est exécutée / réexécutée ces délégués sont toujours réévalués et seront toujours le reflet de votre état ​​actuel de ViewModel.

J’ai créé des interfaces pour des opérations de « load » et de « submit ». J’ai mis en œuvre toutes ces interfaces avec des classes qui comptent sur ​​Silverlight.

Si vous êtes familier avec DomainContext Vous êtes en train de vous demandez sans doute où sont les EntitySets et où avez-vous manipuler des collections. Eh bien, depuis ma mise en œuvre s’appuie sur WCF IDomainModel RIA Services, vous pouvez étendre cette classe et de manipuler votre DomainContext à mettre en œuvre toute opération que vous souhaitez mettre à disposition dans votre étendu IDomainModel. Mais ce cadre ne sont pas censés être consulté dans votre ViewModel. Au lieu de cela, j’utilise mon application ICollectionView, que j’ai appelé DomainCollectionView.

public interface IDomainCollectionView : ICollectionView, IPagedCollectionView, INotifyPropertyChanged
{
    void Add(object item);
    void Remove(object item);
}
 
public interface IDomainCollectionView<T> : IDomainCollectionView, IEnumerable<T>
{
    void Add(T item);
    void Remove(T item);
}

l’implementation actuelle repose sur une IDomainQuery et sera en vue pour les résultats qui sont récupérées dans chaque opération. Comme vous pouvez mettre en œuvre un IDomainQuery pour toute IEnumerable vous pouvez utiliser cette mise en œuvre de toute source de données.

public class DomainCollectionView<T> : ViewModelBase, IDomainCollectionView<T>
{
    private IDomainQuery<T> query;
    private ObservableCollection<T> sourceCollection;
 
    #region DomainCollectionView Members
 
    public DomainCollectionView(IDomainQuery<T> query, bool usePaging = true)
    {
        this.query = query;
        this.sourceCollection = this.CreateSourceCollection();
        this.CurrentItem = null;
        this.CurrentPosition = -1;
 
        //Apply sort and paging
        query.SortDescriptions = () => this.SortDescriptions;
        if (usePaging)
        {
            this.canChangePage = true;
            this.pageSize = 10;// default page size to 10
            query.PageDescription = () => new PageDescription { PageSize = this.PageSize, PageIndex = this.PageIndex };
        }
 
        query.Loaded += this.OnDataRefreshed;
    }
 
    private void OnDataRefreshed(ILoadOperation<T> loadOp)
    {
        using (this.DeferRefresh())
        {
            this.sourceCollection.Clear();
            foreach (var entity in loadOp.Entities)
                this.sourceCollection.Add(entity);
 
            this.ItemCount = loadOp.TotalEntityCount;
            this.TotalItemCount = loadOp.TotalEntityCount;
        }
    }
 
    //...
}

Maintenant que j’ai introduit le modèle et le IDomainCollectionView, il est temps que nous parlions un peu de la ViewModel.

Contrairement au modèle, ViewModels ne sont pas beaucoup réutilisables, car ils dépendent généralement sur les besoins de la vue. Cependant, beaucoup de view point dans les applications métier ont des besoins communs: la recherche/listing des entités, édition des entités, créer des associations entre entités. Nous pouvons créer des ViewModels à partir de ces concepts et de les étendre seulement et les implementer à la logique métier. Toutes les infrastructures et la plomberie peuvent être centralisés et réutilisés partout.

public class ListVM<T> : ViewModelBase, IListVM<T>
{
    private IDomainModel model;
 
    public ListVM() { }
 
    protected virtual IDomainModel CreateModel()
    {
        return ServiceLocator.Current.GetInstance<IDomainModel>();
    }
 
    public void Initialize(IListVMInitializeArgs args)
    {
        this.model = args.ExistingModel ?? this.CreateModel();
        var query = model.GetQuery<T>();
        this.Entities = new DomainCollectionView<T>(query, args.RequiresPaging);
        query.Load();
    }
 
    public IDomainCollectionView<T> Entities
    {
        get;
        private set;
    }
}

Avec juste quelques lignes de code, nous avons une base du ViewModel qui permet :

■ La visualisation des résultats d’une requête qui peuvent être triés et paginée sur le serveur. Juste lier un datagrid à une collection d’entités et jouer avec lui.
■ La réutilisation d’un modèle existant ou en créer un nouveau. L’implémentation actuelle peut utiliser un modèle commun de toute l’application qui est enregistré dans le conteneur Dependency Injection actuelle ou laisser un ViewModel dérivés décider quoi faire.

A bientôt,

Bonne programmation.

PS : cet article est une adaptation en français d’une suite d’articles par Manuel Felício

Kamel DJELLAL
Chef de projet
EDIS CONSULTING – GROUPE UBISIDE

http://www.ubiside.fr/

Architecture Silverlight LOB – Concepts et technologies (Partie 6)

Dans ce poste et le suivant, je vais métaler sur le développement client et comment créer une application Silverlight modulaire et composite. Dans ce post, je vais me concentrer sur la composition UI.

Lors de la construction composite Silverlight LOB applications, vous devez savoir que la modularité est un critère important. En tant que développeur, il devrait être plus facile pour vous de mantenir votre code et d’ajouter de nouvelles fonctionnalités à votre application sans avoir à changer le reste, plus le fichier de votre application XAP grossisse. Chaque module est un fichier séparé XAP, qui peuvent être téléchargés à la demande, selon l’intention de l’utilisateur à utiliser un certain type de fonctionnalité.

Ci-dessous est une architecture possible pour réaliser ce dont nous avons besoin :

Le dossier contient les main xap, que j’ai appelé MyApp. Ce xap est le seul responsable pour le téléchargement d’autres et fournir des moyens pour eux pour afficher son contenu dans l’application principale. Le dossier contient 4 modules essentiels pour le scénario que j’ai défini dans un précédent post. Le module de commandes permet aux utilisateurs non-salarié à remplir de nouvelles commandes. Le module de vente permet aux employés de consulter les commandes en cours et de les traiter. Le module de statistiques permet aux employés d’afficher des graphiques et des rapports de l’évolution des ventes. Caractéristiques pour le département des ressources humaines sont accomplies par le module d’administration, qui devrait également permettre aux utilisateurs de l’application à créer et / ou gérées. Les modules et les Shell sont complié dans le dossier ClientBin de « MyApp.Web ».

Le projet d’infrastructure est un élément clé. Il définit les moyens de communiquer entre les modules et entre l’application principale. En d’autres termes, il s’agit d’un contrat commun partagé par les modules. Quand elle est définie ou partiellement défini, le développement de module peut commencer. Notez que le développement du module de commandes ne dépend pas de la mise au point du module de vente ou le module de statistiques, ce qui signifie que des personnes différentes peuvent travailler sur chacun d’eux.

MyApp.Data.Silverlight est un projet qui possède du WCF RIA Services généré (le code), ainsi que les clients d’autres services qui sont utilisés par les modules.

La couche Serveur correspond à l’architecture que j’ai défini dans le premier post.

Alors, comment notre Shell télécharge les modules ? Nous avons juste besoin de spécifier un catalogue de module et le donner à l’amorçage PRISM, qui est responsable de l’initialisation cadre du PRISM. Ci-dessous le catalogue de module que j’ai défini pour ce scénario :

<prism:ModuleCatalog xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    xmlns:prism="clr-namespace:Microsoft.Practices.Composite.Modularity;assembly=Microsoft.Practices.Composite">
 
    <prism:ModuleInfo Ref="MyApp.Modules.Administration.xap"
                      ModuleName="Administration"
                      ModuleType="MyApp.Modules.Administration.Module, MyApp.Modules.Administration, Version=1.0.0.0"
                      InitializationMode="WhenAvailable">
    </prism:ModuleInfo>
 
    <prism:ModuleInfo Ref="MyApp.Modules.Statistics.xap"
                      ModuleName="Statistics"
                      ModuleType="MyApp.Modules.Statistics.Module, MyApp.Modules.Statistics, Version=1.0.0.0"
                      InitializationMode="WhenAvailable">
    </prism:ModuleInfo>
 
    <prism:ModuleInfo Ref="MyApp.Modules.Sales.xap"
                      ModuleName="Sales"
                      ModuleType="MyApp.Modules.Sales.Module, MyApp.Modules.Sales, Version=1.0.0.0"
                      InitializationMode="WhenAvailable">
    </prism:ModuleInfo>
 
    <prism:ModuleInfo Ref="MyApp.Modules.Orders.xap"
                      ModuleName="Orders"
                      ModuleType="MyApp.Modules.Orders.Module, MyApp.Modules.Orders, Version=1.0.0.0"
                      InitializationMode="WhenAvailable">
    </prism:ModuleInfo>
</prism:ModuleCatalog>

Notez les attributs de InitializationMode. Ils nous permetent de spécifier les conditions dans lesquels les modules doivent être téléchargés sur demande. Pour l’instant, je veux que mes modules soit à télécharger dès que possible. plus tard, je vais vous montrer des techniques efficaces pour les télécharger sur demande. Toutefois, cela n’affecte pas la façon dont je vais les construire.

Pour commencer, notre Shell peut avoir une barre supérieure qui affiche un message « bonjour », un menu sur la gauche et une région où les modules peuvent afficher son contenu.

 <Border BorderThickness="5" CornerRadius="10">
        <tlk:DockPanel LastChildFill="True">
            <shellWelcome:WelcomeBar tlk:DockPanel.Dock="Top" Margin="5"/>
            <shellMenu:Menu tlk:DockPanel.Dock="Left" VerticalAlignment="Stretch" Margin="5,0,5,5"/>
            <shellContainer:MainContainer Margin="0,0,5,5"/>
        </tlk:DockPanel>
    </Border>

Le résultat visuel donne quelque chose comme ceci:

shell

Je voudrais que le menu et le conteneur principal soient dynamiques, en d’autres termes, avoir son contenu défini dynamiquement par des modules au lieu d’être défini dans le contrôle lui-même.

Nous pourrions créer une interface comme « imenu », mis en œuvre par notre ViewModel Menu et l’utiliser par des modules pour afficher le contenu. Bien que cela semble être une aproche découplée, car vos modules peut afficher son contenu sans en fonction de la commande de menu lui-même, il est effectivement associée à l’objet qui implémente l’interface « imenu ». Cela signifie que si vous souhaitez afficher vos options de menu dans d’autres endroits, comme une barre d’outils ou une barre de ruban, vous aurez besoin d’une autre interface pour interagir avec les contrôles. Une solution à ce problème consiste à utiliser une technique de publisher/subscriber en s’appuyant sur ​​un objet médiateur, qui est EventAggregator PRISM. Maintenant, ce que vous faites est la publication d’un message disant « Je veux afficher cette option de menu. Voyons comment nous pouvons faire ce qui suit:

public class MenuOptionMessage : CompositePresentationEvent<MenuOptionMessageArgs> { }
 
public class MenuOptionMessageArgs
{
    public ICommand Command { get; set; }
    public string Text { get; set; }
    public string ImageSourceUri { get; set; }
 
    public string Group { get; set; }
}

Le MenuOptionMessage PRISM est un événement dont la charge utile est un message MenuOptionMessageArgs. Donc, chaque fois que vous souhaitez publier un MenuOptionMessage vous créez un MenuOptionMessageArgs et dite au EventAggregator de le diffuser à tous les abonnés. Le EventAggregator peut être consulté par l’injection de dépendance ou le conteneur IoC. J’ai l’habitude de créer une classe utilitaire simple pour garder le code plus propre:

public class Messenger
{
    public static T Get<T>()
        where T : EventBase
    {
        var ev = ServiceLocator.Current.GetInstance<IEventAggregator>();
        return ev.GetEvent<T>();
    }
}

Maintenant, nous pouvons avoir nos modules publié MenuOptionMessages.

initialisation du module « Order » :

Messenger.Get<MenuOptionMessage>().Publish(new MenuOptionMessageArgs
{
    Command = Commands.NewOrderCommand,
    Text = "Create New Order",
    Group = "Orders"
});
Messenger.Get<MenuOptionMessage>().Publish(new MenuOptionMessageArgs
{
    Command = Commands.ViewOrdersCommand,
    Text = "View Orders",
    Group = "Orders"
});

Initialisation du module « Sales » :

Messenger.Get<MenuOptionMessage>().Publish(new MenuOptionMessageArgs
{
    Command = Commands.ViewPendingOrdersCommand,
    Text = "Pending Orders",
    Group = "Sales"
});
Messenger.Get<MenuOptionMessage>().Publish(new MenuOptionMessageArgs
{
    Command = Commands.ViewProcessedOrdersCommand,
    Text = "Processed Orders",
    Group = "Sales"
});

Le contrôle Menu affiche toutes les commandes groupées par son Groupe et crée une extension pour chaque groupe. Vous pouvez accéder au code source pour le confirmer.

Maintenant, ce que nous devons faire est d’avoir nos commandes d’affichage des vues.

PRISM a un concept de région, où vous de définir des régions au sein de votre Shell, leur donner un nom et ensuite vos modules injecter les vues dans ces régions en spécifiant le nom. Je ne suis pas fan de cette fonctionnalité, car elle crée un couplage étroit entre les vues et les régions. Outre cela signifie que votre logique écran fonctionne uniquement avec PRISM. J’utilise généralement une approche différente, qui nous permet d’utiliser également les régions PRISM, mais d’autres cadres de UI ainsi. En outre, il n’est pas difficile de construire votre propre cadre UI, si vous restez simple. Par exemple, au lieu de penser sur les régions, pensez à voir les view positions.

public enum ViewPosition
{
    Left,
    Right,
    Top,
    Bottom,
    Center,
    Floating
}

En utilisant la même technique que nous avons fait pour le menu, chaque fois que nous voulons afficher une vue, nous n’avons tout simplement de publier un message où nous spécifier la position et la vue et celui qui est responsable d’afficher cet vue dans la position spécifiée doit souscrire le message et agir en conséquence. Les Messages devraient être définis dans l’assembly Infastructure. Voyons comment le message est défini:

public class CreateViewMessage : CompositePresentationEvent<CreateViewMessageArgs> { }
 
public class CreateViewMessageArgs
{
    public Lazy<object> View { get; set; }
    public ViewPosition Position { get; set; }
    public string Title { get; set; } //optional
}

Notez le Lazy objcet. Cela vous permet d’avoir votre vue créés par l’abonné au lieu d’être créé par l’éditeur. Cela vous permet d’utiliser différents threads lors de la publication / abonnement, mais dans ce cas l’abonné doit utiliser le thread d’interface utilisateur.

Ci-dessous un exemple de la façon de publier un CreateViewMessage à partir du module de commande.

public class Commands
{
    public static ICommand NewOrderCommand = new ActionCommand(Commands.NewOrder);
    public static ICommand ViewOrdersCommand = new ActionCommand(Commands.ViewOrders);
 
    private static void NewOrder()
    {
        Messenger.Get<CreateViewMessage>().Publish(new CreateViewMessageArgs
        {
            View = new Lazy<object>(() => ServiceLocator.Current.GetInstance<OrderView>()),
            Position = ViewPosition.Center,
            Title = "New Order"
        });
    }
 
    private static void ViewOrders()
    {
        //to be implemented
    }
}

Notez le OrderView qui doit être créé par le ServiceLocator pour permettre l’injection de dépendance, si nécessaire.

Le contrôle MainContainer est responsable de ce message et de son inscription pour afficher la vue intérieur d’un contrôle onglet.

public partial class MainContainer : UserControl
{
    public MainContainer()
    {
        InitializeComponent();
        if (DesignerProperties.IsInDesignTool)
            return;
 
        Messenger.Get<CreateViewMessage>().Subscribe(this.HandleCreateView, ThreadOption.UIThread, true, this.CanHandleCreateView);
    }
 
    private bool CanHandleCreateView(CreateViewMessageArgs args)
    {
        return args.Position == ViewPosition.Center;
    }
 
    private void HandleCreateView(CreateViewMessageArgs args)
    {
        this.tabCtrl.Items.Add(new TabItem
        {
            Header = args.Title, Content = args.View.Value,
            VerticalContentAlignment = System.Windows.VerticalAlignment.Stretch,
            HorizontalContentAlignment = System.Windows.HorizontalAlignment.Stretch
        });
    }
}

Lorsque vous vous abonnez un message, vous pouvez spécifier un prédicat qui dit défini le mécanisme de publier que vous voulez vraiment pour traiter ce message. Dans ce cas, le conteneur principal vise seulement à des CreateViewMessages qui ont des vues à afficher dans la position centrale. J’aurais pu souscrire un autre composant du CreateViewMessage et afficher des vues à l’intérieur de fenêtres lorsque la situation est variable. Le paramètre ThreadOption est également important. Chaque message qui traite de l’UI doit être souscrit dans le thread d’interface utilisateur. Si vous voulez brancher un enregistreur de souscription/log messages vous n’avez pas besoin de vous inscrire sur le thread d’interface utilisateur. Dans ce cas, vous devez utiliser un thread d’arrière.

Il s’agit d’une aproche très simples sur la façon de faire la composition d’UI qui fonctionne et qui est très efficace.

Suite : Dans le prochain post je vais me concentrer sur ViewModels et comment nous pouvons construire le OrderView.

PS : cet article est une adaptation en français d’une suite d’articles par Manuel Felício

Kamel DJELLAL
Chef de projet
EDIS CONSULTING – GROUPE UBISIDE

http://www.ubiside.fr/

Architecture Silverlight LOB – Concepts et technologies (Partie 5)

La Spécification des métadonnées pour nos entités est la partie la plus facile et voici un bon endroit pour commencer la localisation de notre application.

La première des choses que nous avons à faire est de créer une classe buddy tel que c’est décrit dans l’exemple suivant:

[MetadataType(typeof(Customer.CustomerMetadata))]
public partial class Customer
{
    internal class CustomerMetadata
    {
        [Display(ResourceType = typeof(Metadata), Name = "MyApp_Customer_Number")]
        public int Number;
 
        [Display(ResourceType = typeof(Metadata), Name = "CommonProperties_Email")]
        public string Email;
 
        [Include]
        public EntityCollection<Order> Orders;
    }
}
[MetadataType(typeof(Employee.EmployeeMetadata))]
public partial class Employee
{
    internal class EmployeeMetadata
    {
        [Display(ResourceType = typeof(Metadata), Name = "CommonProperties_Name")]
        [StringLength(128, ErrorMessageResourceType = typeof(Metadata), ErrorMessageResourceName = "CommonErrors_StringLength")]
        public string Name;
 
        [Display(ResourceType = typeof(Metadata), Name = "CommonProperties_Email")]
        public string Email;
 
        [Include]
        public Department Department;
    }
}

L’attribut DisplayAttribute est très utile, car il annote les propriétés de votre information qui peut être consommé de différentes manières. Par exemple, lorsque vous utilisez votre entité dans un Datagrid, les en-têtes des cellules va automatiquement chercher pour DisplayAttributes et récupérer la chaîne localisée pour cette propriété. La même chose s’applique pour les labels dans un Dataform et si vous voulez créer votre propre UI framework, vous pouvez exploiter ce type d’annotation pour commencer la localisation de votre application. Remarquez comment j’ai réutilisé des noms de ressources pour les common properties comme l’email. La même chose aurait pu être fait pour le nom, mais cela dépend vraiment de chaque cas.

Vous pouvez placer les règles de validation sur vos propriétés et en utilisant des attributs comme StringLength, appliquer des expressions régulières ou même créer vos propres règles de validation personnalisées. La grande partie à ce sujet est que ces règles seront disponibles dans le client ainsi. Le DomainService fait en sorte que, avant l’exécution des opérations sur les entités de toutes les règles de validation sont appliquées. Notez que vous ne pouvez pas faire toutes sortes de validation sur vos entités en utilisant des métadonnées. Certaines validations peuvent nécessiter la connexion à une base de données, transactions, etc, et cette information n’est pas disponible dans ce type de contexte de validation. Je recommande que de telles validations soit effectuées après ExecuteChangeSet DomainService est invoquée et juste avant la méthode PersistChangeSet.

Plus de détails sur la spécification des métadonnées peuvent être trouvées dans la documentation ou autres ressources sur le web. Je veux simplement mettre l’accent sur ​​un aspect différent que peu de gens ont tendance à écrire. C’est le partage des ressources dans le client et le serveur. Le projet client ne compilera pas si les métadonnées ResourceManager n’est pas disponible. Comme il est défini sur le serveur, vous devez suivre ces étapes afin de l’activer sur le client:

■ Ajoutez les fichiers de ressources comme un lien dans le projet client
■ Renommez votre namespace par default dans votre projet client par le même nom que dans le namespace qui est défini dans le projet serveur qui contient les fichiers de ressources
■ Changer le .csproj du client où il fait référence au fichier lié Designer.cs et ajouter ces lignes de code.:

<Compile Include=”..\..\..\Server\Data\MyApp.Data\Resources\Metadata.Designer.cs”>
 
      <AutoGen>True</AutoGen>      <Link>Resources\Metadata.Designer.cs</Link>
 
      <DesignTime>True</DesignTime>
 
      <DependentUpon>Metadata.resx</DependentUpon>
 
</Compile>

■ Assurez-vous que les fichiers de ressources ont le même chemin relatif au projet client que dans le projet serveur. Dans ce cas, j’ai créé un dossier des ressources dans les deux projets.
■ Assurez-vous aussi que le fichier de ressources a marqué son modificateur d’accès comme public.

Ces étapes sont nécessaires car l’ajout de fichiers liés aux fichiers dépendants, comme Medatata.resx / Metadata.Designer.cs ne va pas les marquer comme dependant automatiquement, vous devez donc le faire manuellement.

Maintenant, vous devriez être en mesure de compiler votre application et commencer à tirer profit des métadonnées dans vos entités.

Suite : prochain poste

PS : cet article est une adaptation en français d’une suite d’articles par Manuel Felício

Kamel DJELLAL
Chef de projet
EDIS CONSULTING – GROUPE UBISIDE

http://www.ubiside.fr/