Partage
  • Partager sur Facebook
  • Partager sur Twitter

Liaison Model et ViewModel

c#

Sujet résolu
    3 novembre 2018 à 0:01:20

    Bonsoir Nisnor, merci de continuer à essayer de m'expliquer les choses. Mais soit j'ai la comprenette difficile, soit on ne tirera rien de moi, mais je n'ai pas compris tes dernières explications.

    Ok CollectionChanged s'occupe des changements dans la collection (ça j'avais compris). Mais ensuite le flou s'installe. 

    Pourrais tu m'expliquer ces histoires d'instances. Pour moi il y avait la collection et les items de la collection mais j'ai loupé une marche...

    Ensuite j'ai l'impression que tu me parles de deux datagrid alors que je n'en ai qu'un.

    Le this.ItemEntree.Remove inutile je ne comprends pas non plus. D'où vient l’événement de suppression dont tu me parles?

    Tu vois bien des lacunes :(.

    Je te fais suivre le Xaml, le ViewModel, et le model CollectionEntree pour que tu puisses voir un peu plus clair, si tu as le temps bien sur.

    Le Xaml:

    <UserControl
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 xmlns:local="clr-namespace:Compte_Bancaire.Views"
                 xmlns:ViewModel="clr-namespace:Compte_Bancaire.ViewModel" x:Class="Compte_Bancaire.Views.UcEntree"
                 mc:Ignorable="d" 
                 d:DesignHeight="300" d:DesignWidth="450">
        <UserControl.DataContext>
            <ViewModel:UcEntreeViewModel/>
        </UserControl.DataContext>
        
        
        <Border x:Name="BrdEntree" Width="400">
            <Grid x:Name="GridEntree">
                <Grid.RowDefinitions>
                    <RowDefinition Height="30"/>
                    <RowDefinition Height="*"/>
                </Grid.RowDefinitions>
    
                <Label x:Name="LabEntree" Foreground="{DynamicResource TxtEntree}" Content="Entrées" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="16" FontWeight="Bold" Background="{x:Null}" />
    
                <DataGrid x:Name="DataEntree" Grid.Row="1" Grid.Column="1" Foreground="{DynamicResource TxtEntree}" Width="360" Height="250" FontFamily="Comic Sans MS" FontStyle="Italic" HorizontalGridLinesBrush="{DynamicResource TxtEntree}" VerticalGridLinesBrush="{DynamicResource TxtEntree}" AlternatingRowBackground="{DynamicResource BackEntree}" MaxHeight="350" HeadersVisibility="Column" AlternationCount="1" FontSize="11" AutoGenerateColumns="False" FrozenColumnCount="1" FontWeight="Bold" 
                          ItemsSource="{Binding ItemEntree}" SelectedItem="{Binding ItemSelectionne, Mode=TwoWay}" AreRowDetailsFrozen="True"
                          ScrollViewer.VerticalScrollBarVisibility="Visible"  SelectionMode="Single" Style="{DynamicResource DataGridStyle1}" >
                    <DataGrid.Background>
                        <SolidColorBrush Color="#FF20D10E" Opacity="0.3"/>
                    </DataGrid.Background>
                    <DataGrid.Columns>
                        <DataGridCheckBoxColumn  Header="Valid."/>
                        <DataGridTextColumn  Foreground="{DynamicResource TxtEntree}" FontFamily="Comic Sans MS" Header="Opérations" Width="200" 
                                             Binding="{Binding OperationEntree, Mode=TwoWay}"/>
                        <DataGridTextColumn  Foreground="{DynamicResource TxtEntree}" FontFamily="Comic Sans MS" Header="Crédit" Width="100" 
                                             Binding="{Binding CreditEntree, Mode=TwoWay, StringFormat=0.00;;#}"/>
                    </DataGrid.Columns>
                    <DataGrid.RowBackground>
                        <SolidColorBrush Color="#FF0CA005" Opacity="0.3"/>
                    </DataGrid.RowBackground>
                    <DataGrid.BorderBrush>
                        <SolidColorBrush Color="#FF20D10E" Opacity="0.5"/>
                    </DataGrid.BorderBrush>
                </DataGrid>
            </Grid>
    
        </Border>
    </UserControl>
    

    Le ViewModel:

    namespace Compte_Bancaire.ViewModel
    {
        public class UcEntreeViewModel : Notifiable
        {
            #region Constructeur
            public UcEntreeViewModel()
            {
                ItemEntree = new ObservableCollection<CollectionEntree>(ServiceOperationMensuelle.GetOperationEntree());
                ItemEntree.CollectionChanged += ItemEntree_CollectionChanged;
            }
            
            #endregion
    
            #region Propriétés
            private ObservableCollection<CollectionEntree> itemEntree;
            public ObservableCollection<CollectionEntree> ItemEntree
            {
                set
                {
                    itemEntree = value;
                    RaisePropertyChanged(nameof(UcEntreeViewModel.ItemEntree));
    
                }
                get
                {
                    return itemEntree;
                }
            }
    
            private CollectionEntree itemSelectionne;
            public CollectionEntree ItemSelectionne
            {
                set
                {
                    itemSelectionne = value;
                    RaisePropertyChanged(nameof(UcEntreeViewModel.ItemSelectionne));
                }
                get
                {
                    return itemSelectionne;
                }
            }
    
            public decimal TotalEntree
            {
                get { return this.ItemEntree.Sum(e => e.CreditEntree);}
                 
            }
            #endregion
    
            private void ItemEntree_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
            {
                // Détection de l'ajout d'un item dans la collection
                if (e.NewItems != null)
                {
                    foreach (CollectionEntree entree in e.NewItems)
                    {
                        entree.PropertyChanged += Entree_PropertyChanged;
                    }
                }
                // Détection de la suppression d'un item dans la collection
                if (e.OldItems!=null)
                {
                    foreach (CollectionEntree entree in e.OldItems)
                    {
                        entree.PropertyChanged -= Entree_PropertyChanged;
                        this.ItemEntree.Remove(this.ItemSelectionne);
                        RaisePropertyChanged(nameof(UcEntreeViewModel.TotalEntree));
                        MediatorCalcul.Mediator.CalculEntree = TotalEntree;
                    }
                }
                RaisePropertyChanged(nameof(UcEntreeViewModel.TotalEntree));
                MediatorCalcul.Mediator.CalculEntree = TotalEntree;
            }
            private void Entree_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
            {
                // Sauvegarde des données dans l'observablecollection
                {
                    var entree = new CollectionEntree()
                    {
                        OperationEntree = ItemSelectionne.OperationEntree,
                        CreditEntree = ItemSelectionne.CreditEntree
                    };
                }
                RaisePropertyChanged(nameof(UcEntreeViewModel.TotalEntree));
                MediatorCalcul.Mediator.CalculEntree = TotalEntree;
                
            }
        }
    }

    Et le Model

    namespace Compte_Bancaire.Models
    {
        public class CollectionEntree : Notifiable
        {
            #region Constructeur
            public CollectionEntree()
            {
    
            }
            #endregion
    
            #region Propriétés
    
            private bool changementEntree = false;
            public bool ChangementEntree
            {
                set
                {
                    changementEntree = value;
                    RaisePropertyChanged(nameof(CollectionEntree.ChangementEntree));
                }
                get
                {
                    return changementEntree;
                }
            }
    
            private bool validEntree;
            public bool ValidEntree
            {
                set
                {
                    validEntree = value;
                    RaisePropertyChanged(nameof(CollectionEntree.ValidEntree));
                }
                get
                {
                    return validEntree;
                }
            }
    
            private string operationEntree;
            public string OperationEntree
            {
                set
                {
                    operationEntree = value;
                    RaisePropertyChanged(nameof(CollectionEntree.OperationEntree));
                }
                get
                {
                    return operationEntree;
                }
            }
    
            private decimal creditEntree;
            public decimal CreditEntree
            {
                set
                {
                    creditEntree = value;
                    RaisePropertyChanged(nameof(CollectionEntree.CreditEntree));
                }
                get
                {
                    return creditEntree;
                }
            }
            #endregion
        }
    }
    

    Merci pour ton aide.





    • Partager sur Facebook
    • Partager sur Twitter
      5 novembre 2018 à 9:40:12

      Bonjour Nisnor,

      Après beaucoup de réflexion j'ai transformé mon ViewModel et je crois avoir compris le fonctionnement de l'observablecollection et réussi à obtenir le fonctionnement désiré sur le changement d'une donnée.

      Je te fourni mon nouveau ViewModel. Peux-tu y jeter un coup d’œil pour me dire si la démarche et l'écriture sont correct?

      Merci à toi.

      using Compte_Bancaire.Models;
      using System.Collections.ObjectModel;
      using Compte_Bancaire.Utils;
      using System.Linq;
      
      namespace Compte_Bancaire.ViewModel
      {
          public class UcEntreeViewModel : Notifiable
          {
              #region Constructeur
              public UcEntreeViewModel()
              {
                  ItemEntree = new ObservableCollection<CollectionEntree>(ServiceOperationMensuelle.GetOperationEntree());
                  ItemEntree.CollectionChanged += ItemEntree_CollectionChanged;
              }
              
              #endregion
      
              #region Propriétés
              private ObservableCollection<CollectionEntree> itemEntree;
              public ObservableCollection<CollectionEntree> ItemEntree
              {
                  set
                  {
                      itemEntree = value;
                      RaisePropertyChanged(nameof(UcEntreeViewModel.ItemEntree));
      
                  }
                  get
                  {
                      return itemEntree;
                  }
              }
      
              private CollectionEntree itemSelectionne;
              public CollectionEntree ItemSelectionne
              {
                  set
                  {
                      if(itemSelectionne!=value)
                      {
                          itemSelectionne = value;
                          itemSelectionne.PropertyChanged += ItemSelectionne_PropertyChanged;
                      }
      
                      RaisePropertyChanged(nameof(UcEntreeViewModel.ItemSelectionne));
                  }
                  get
                  {
                      return itemSelectionne;
                  }
              }
      
              public decimal TotalEntree
              {
                  get { return this.ItemEntree.Sum(e => e.CreditEntree);}
                   
              }
              #endregion
      
              private void ItemEntree_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
              {
                  // Détection de l'ajout d'un item dans la collection
                  if (e.NewItems != null)
                  {
                      foreach (CollectionEntree entree in e.NewItems)
                      {
                          entree.PropertyChanged += Entree_PropertyChanged;
                      }
                  }
                  // Détection de la suppression d'un item dans la collection
                  if (e.OldItems!=null)
                  {
                      foreach (CollectionEntree entree in e.OldItems)
                      {
                          entree.PropertyChanged -= Entree_PropertyChanged;
                          this.ItemEntree.Remove(this.ItemSelectionne);
                          RaisePropertyChanged(nameof(UcEntreeViewModel.TotalEntree));
                          MediatorCalcul.Mediator.CalculEntree = TotalEntree;
                      }
                  }
                  RaisePropertyChanged(nameof(UcEntreeViewModel.TotalEntree));
                  MediatorCalcul.Mediator.CalculEntree = TotalEntree;
              }
      
              // Sauvegarde des données dans l'observablecollection
              private void Entree_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
              {
                  {
                      var entree = new CollectionEntree()
                      {
                          OperationEntree = ItemSelectionne.OperationEntree,
                          CreditEntree = ItemSelectionne.CreditEntree
                      };
                  }
                  RaisePropertyChanged(nameof(UcEntreeViewModel.TotalEntree));
                  MediatorCalcul.Mediator.CalculEntree = TotalEntree;
                  
              }
      
              // Remplacement d'une donnée dans l'observablecollection
              private void ItemSelectionne_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
              {
                  var entree = new CollectionEntree()
                  {
                      OperationEntree = ItemSelectionne.OperationEntree,
                      CreditEntree = ItemSelectionne.CreditEntree
                  };
                  RaisePropertyChanged(nameof(UcEntreeViewModel.TotalEntree));
                  MediatorCalcul.Mediator.CalculEntree = TotalEntree;
      
              }
          }
      }

      • Partager sur Facebook
      • Partager sur Twitter
        5 novembre 2018 à 10:44:48

        "Ensuite j'ai l'impression que tu me parles de deux datagrid alors que je n'en ai qu'un."

        Ok, j'ai p-e mal compris alors.

        "Le this.ItemEntree.Remove inutile je ne comprends pas non plus. D'où vient l’événement de suppression dont tu me parles?"

        Ce problème est encore présent dans ta 2eme version du VM.

        Pour voir le problème, "déroule le programme dans ta tête" (aide toi d'un papier si nécessaire) :

        • A un instant T, tu as une instance de collection qui existe.
        • A T+1, sur cette instance, est réalisé un appel pour supprimer un élément (un appel à Remove ou RemoveAt). Peu importe la provenance, c'est pas la question, le but étant d'analyser ce que fait ton code, pas de savoir qui l'a déclenché.
        • A T+2, l'instance de la collection traite la demande, l'élément n'existe plus en son sein.
        • A T+3, cette même instance de collection lève un événement (CollectionChanged). Dans ton code, tu t'es abonné à cet événement, c'est à ce moment que ton code va être exécute. Vu qu'il s'agit d'une suppression, on s'attend logiquement à avoir en argument NewItems == null et OldItems contenant une collection d'élément avec 1 seul élément : l'élément supprimé.
        • A T+4, ton code va itérer sur OldItems.
        • A T+5, on est dans ta boucle. Tu as dans la variable locale "entree", l'instance de l'élément qui a été supprimé. Pour rappel, cet élément n'existe déjà plus dans la collection. Si on racole ça avec le reste de l'application, c'est censé être la même instance que celle qui se trouve dans "ItemSelectionne", mais rien ne nous le garanti. Déjà là, ça pose problème : "entree" et "ItemSelectionne", pourraient (même si peu probable) être différents et il n'y a aucune vérification de faite là dessus donc il ne serait pas impossible que parce que un élément A a été supprimé, tu supprime par erreur un élément B.
        • Admettons, le cas d'avant ne se produit jamais, tu arrives sur la ligne "ItemEntree.Remove(ItemSelectionne)". A quoi sert-elle? Puisque l'élément que tu cherche à supprimer n'existe déjà plus dans la collection qui t'a envoyé l'événement. Au mieux, c'est une ligne de code qui ne fera rien (ce qui sera le cas avec l'implémentation d'ObservableCollection)...Au pire, ca pourrait être une ligne de code qui ferait un appel récursif infini (suppress->event->suppress->event->suppress->event, ...) et ça finirait par péter avec une StackOverflowException.

        En l'état, les cas que j'ai cité ici ne sont pas vraiment intéressant, parce que le framework .NET et les composants qu'il contient sont quand même (très) robuste et qu'ils ont justement été pensé pour éviter les cas d'erreur simple (exemple : dans l'implémentation de ObservableCollection, il y a surement une vérification qui est faite avant d'exécuter le remove et lever l'événement...donc la boucle infinie ne se produira jamais). Cela dit, faire cette gymnastique quand on développe, ça augmente les chances de faire un code sans superflus.

        "Je te fourni mon nouveau ViewModel. Peux-tu y jeter un coup d’œil pour me dire si la démarche et l'écriture sont correct?"

        • A mon sens, ta propriété "ItemEntree" de type "ObservableCollection<CollectionEntree>" devrait être en lecture seule. D'un point de vue conceptuel, personne, en dehors de UcEntreeViewModel n'a besoin de jongler avec l'instance de cette collection. Jongler avec le contenu de la collection, pourquoi pas, mais l'instance elle même, à mon sens, c'est risqué (parce que tu pourrais écrire ailleurs "monInstanceUcVm.ItemEntree = new ObservableCollection<CollectionEntree>();", le compilateur sera incapable de voir que c'est une erreur.).
        • Il y a 2 problèmes avec le set de ItemSelectionne. Le premier, c'est des risques de fuite mémoire, le second, c'est un comportement "aléatoire". Si tu sélectionne plein de fois d'affilé des items différents, tu vas te retrouver bombardé de notifications indésirable dans "ItemSelectionne_PropertyChanged".
        • Si le code de "Entree_PropertyChanged" et de "ItemSelectionne_PropertyChanged" est identique, pourquoi veux-tu les séparer dans 2 méthodes séparées? Tu n'as d'autant plus pas besoin de séparer ce code, que les instances d'objet "CollectionEntree" qui génèrent ces événements sont les même, elles proviennent toute de la collection "ItemEntree".
        • N'hésite pas à factoriser les petits bouts de code identique, ça te simplifiera la maintenance et l'évolutivité de ton code. Y'a 3 endroits différents où tu fais un RaiseProperty de TotalEntree suivi d'une assignation de Mediator.CalculEntree.
        • Pour plus tard, si tu t'en sors, il y a un truc sur lequel tu pourrais bosser. Là, globalement, la propriété "CalculEntree" de ton mediateur statique est systématiquement assignée avec le contenu de "TotalEntree". Il y aurait surement moyen de recabler ta vue directement vers le mediateur. Mais ce point est très proche de celui où je te disais qu'un Mediateur n'est surement pas nécessaire pour ton cas d'usage actuel. A voir plus tard donc.

        -
        Edité par Nisnor 5 novembre 2018 à 10:45:21

        • Partager sur Facebook
        • Partager sur Twitter
          5 novembre 2018 à 12:03:04

          Merci pour toutes tes explications, je continue à bosser...
          • Partager sur Facebook
          • Partager sur Twitter
            6 novembre 2018 à 12:14:43

            Bonjour Nisnor,

            En suivant, comme d'habitude, tes conseils j'ai remanié mon ViewModel et je pense être arrivé à quelque chose de mieux écrit.

            Pour le mediator, il y aura plusieurs contenus donc je garde cette structure.

            J'ai compris la démarche pour la suppression d'un élément.

            Encore une question, si tu connais EntityFramework j'aimerai savoir si les migrations s'appliquent qu'au changements du modèle de la base de données ou s'ils s'appliquent aussi à une mise à jour des données. Dans les tutos ils ne parlent que des changements de modèle.

            Merci à nouveau.

            • Partager sur Facebook
            • Partager sur Twitter
              6 novembre 2018 à 17:12:46

              Si je ne me plante pas, la migration ne concerne que le schéma de données (donc le modèle uniquement).

              Je crois me rappeler que par le passé, si on modifiait le schéma en ajoutant une colonne non nulle, ça pétait lorsqu'on tentait d'appliquer ça à une base existante, parce que logiquement, sans valeur par défaut, la migration ne sait pas quelle valeur placer pour les données existantes.

              Il me semble aussi que, d'une manière plus générale, la migration sous EF, vaut mieux s'en passer autant que possible, tellement c'est bancal.

              • Partager sur Facebook
              • Partager sur Twitter
                6 novembre 2018 à 19:10:10

                Bonsoir Nisnor, merci pour ta réponse. Je n'utiliserai pas les migrations. En fait je suis à la recherche d'un moyen pour mettre à jour ma table de données. Dans les tutos ils mettent à jour dès que la donnée a changée. Comme tu m'as conseillé de le faire à la fin de l'utilisation (appui sur un bouton enregistrer) cette technique ne m’intéresse pas. Donc pour l'instant pas d'évolution de ce coté.

                Tu avais raison (comme toujours), l'utilisation d'ItemSelectionne_PropertyChanged. Le fonctionnement n'est pas au top. Par contre, je ne vois pas d'autre moyen de savoir qu'une ou plusieurs propriétés de la collection ont changées alors que la collection elle même n'a pas changée.

                Je continue à réfléchir et à chercher mais as-tu une piste?

                Merci et à bientôt.

                • Partager sur Facebook
                • Partager sur Twitter
                  7 novembre 2018 à 10:35:18

                  Attention : la migration au sens EF n'a strictement rien à voir avec les [manipulations de] données. La migration ne concerne que la structure des données. La mécanique de détection des changements de données sur les tables, c'est une autre partie d'EF qui le gère.

                  Cette "autre partie d'EF" rend la manipulation des données très simples. Toi (développeur), tu requête un contexte EF (qui représente ta base de données) à travers des propriétés qui exposent des DbSet<T> où T est un type d'entité. Derrière, c'est EF qui se charge d'établir la connexion entre ce DbSet<T> et la base de données + table correspondante. Sur ce DbSet<T>, tu peux faire (grâce à Linq-To-Entities) des select. Derrière, c'est toujours EF qui se charge de transformer ta requête LINQ en requête adaptée à ton moteur de données. Si ton moteur de données est un SQL Server, ta requête LINQ de select sera transformée en requête SQL du genre "SELECT * FROM shema.uneTable [WHERE ...]". Lorsque ta requête LINQ se termine, "tu crois" (développeur) avoir un objet .NET classique, mais ce n'est pas le cas : EF va te retourner une instance d'un objet-proxy qui se comporte tout comme ton objet, mais qui reste un proxy. Autrement dit, quand tu fais "tonInstanceDEntite.Prix = 0;", c'est le proxy qui va intercepter le fait qu'une instance du type de ton entité a subit une modification de sa propriété "Prix" pour la valeur 0. C'est ce même proxy qui va indiquer à EF que, lorsque sur le contexte EF, tu feras un Save();, une requête (si ton moteur reste SQL Server) "UPDATE schema.uneTable SET (Prix) VALUES (0) WHERE [lacléici...ID par exemple]="valeurdel'id"" devra être émise. Il en va de même pour les ajouts : Quand tu fais tonContext.TesEntities.Add(new TonEntite()), EF va conserver quelque part que quand tu fais un Save(), une requête "INSERT INTO schema.uneTable VALUES(...)" doit être réalisée.

                  Tout ça, c'est EF qui le fait automatiquement pour toi.

                  Et la magie réside dans le fait qu'EF ne translate pas directement ton code en requête lui même, il délègue ça à un connecteur. Ca veut dire qui si ton connecteur est pour SQL Server, dépendant de l'implémentation, ça transformera en requête SQL...Pareil si ton connecteur est pour MySQL ou Oracle. En revanche, si ton connecteur est pour Twitter (a supposer que ça existe), ça transformera en requête HTTP vers l'API Twitter.

                  "l'utilisation d'ItemSelectionne_PropertyChanged. Le fonctionnement n'est pas au top."

                  A quel endroit ça te bloque?

                  • Partager sur Facebook
                  • Partager sur Twitter
                    7 novembre 2018 à 11:10:01

                    Bonjour Nisnor, après des séries d'essais j'ai enfin trouvé pourquoi je tourne en rond depuis si longtemps. J'ai trouvé mais je ne comprends pas.

                    Je t'explique en essayant d'être clair:

                    - A l'ouverture, dans mon datagrid je charge les valeurs présentes dans la base de données et je les affichent, pas de problème.

                    - Ensuite je rentre, je supprime des données, pas de problème.

                    - Ensuite, et là les soucis, si je change une donnée que j'ai rentrée ça fonctionne mais si je change une donnée issue de la base de départ ça ne fonctionne pas. Pourtant toutes les données sont dans l'observablecollection.

                    Pourquoi? As tu une idée sur ce comportement que je qualifierais de bizarre? 

                    Merci.

                    • Partager sur Facebook
                    • Partager sur Twitter
                      7 novembre 2018 à 11:26:22

                      Dans le constructeur, tu construis une nouvelle instance d'ObservableCollection en l'alimentant avec une liste de donnée.

                      Une fois cette liste construite, tu t'abonnes à l'événement CollectionChanged

                      C'est tout.

                      Ca veut aussi dire que tout donnée présente avant l'abonnement à CollectionChanged (au moment où tu construis ta collection donc) ne se verra jamais surveillée pour ses changements. Si ton save intervient au moment où l'une des données change, ça implique aussi que ces données existante ne seront jamais sauvegardées.

                      -
                      Edité par Nisnor 7 novembre 2018 à 11:27:33

                      • Partager sur Facebook
                      • Partager sur Twitter
                        7 novembre 2018 à 17:00:01

                        Merci Nisnor pour cette mise au point. Je ne savais pas et je ne pensais pas que la surveillance s'effectuait que sur les nouveaux éléments. Je croyais que cette surveillance s'appliquait sur la collection entière.

                        J'essaie et je te tiens au courant.

                        A bientôt.

                        • Partager sur Facebook
                        • Partager sur Twitter
                          8 novembre 2018 à 18:54:54

                          "Dans le constructeur, tu construis une nouvelle instance d'ObservableCollection en l'alimentant avec une liste de donnée.

                          Une fois cette liste construite, tu t'abonnes à l'événement CollectionChanged"

                          C'est ce que j'ai fait, mais j'espère qu'il est possible de surveiller la collection valeurs chargées au départ comprises?

                          Je te donne le bout de code concerné placé dans le constructeur de ma classe:

                          {

                          ItemEntree = new ObservableCollection<CollectionEntree>(ServiceOperationMensuelle.GetOperationEntree());

                          ItemEntree.CollectionChanged += ItemEntree_CollectionChanged;

                          ItemEntree.Sum(e => e.CreditEntree);

                          RaisePropertyChanged(nameof(UcEntreeViewModel.TotalEntree));

                          MediatorCalcul.Mediator.CalculEntree = TotalEntree;

                          }

                          Merci encore.

                          • Partager sur Facebook
                          • Partager sur Twitter
                            9 novembre 2018 à 9:49:56

                            "C'est ce que j'ai fait"

                            Oui, "je décodais pour toi ce que faisais ton code".

                            Ca sous-entendais qu'il manque une étape. Il manque une ligne du style "Pour chaque élément pré-chargé dans la liste, si il en existe, il faut appliquer le code qui se trouve dans ton handler de CollectionChanged, lorsqu'un nouvel élément est ajouté" ^^

                            Et comme dupliquer du code, c'est caca boudin, un truc comme ça ne serait pas déconnant :

                            public UcEntreeViewModel()
                            {
                            	ItemEntree = new ObservableCollection<CollectionEntree>(ServiceOperationMensuelle.GetOperationEntree());
                            	this.OnNewCollectionEntreeAdded(this.ItemEntree);//C'était ça qui manquait à ton constructeur : Quand tout le monde est chargé, UcEntreeViewModel doit considérer ces item "comme des nouveaux".
                            	ItemEntree.CollectionChanged += ItemEntree_CollectionChanged;
                            }
                            
                            private void ItemEntree_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
                            {
                            	// Détection de la suppression d'un item dans la collection
                            	if (e.OldItems!=null)
                            	{
                            		foreach (CollectionEntree entree in e.OldItems)
                            		{
                            			entree.PropertyChanged -= Entree_PropertyChanged;
                            			this.ItemEntree.Remove(this.ItemSelectionne);//Cette ligne reste curieuse, voir risquée.
                            			//Ces 2 lignes de code sont inutiles puisqu'elles sont exécutées plus bas
                            			//RaisePropertyChanged(nameof(UcEntreeViewModel.TotalEntree));
                            			//MediatorCalcul.Mediator.CalculEntree = TotalEntree;
                            		}
                            	}
                            	//Inversion de l'ordre de traitement : On traite d'abord les Add puis les Remove, pour profiter de ce qui est fait
                            	//en + lors d'un Add
                            	// Détection de l'ajout d'un item dans la collection
                            	this.OnNewCollectionEntreeAdded(e.NewItems?.Cast<CollectionEntree>());
                            }
                            
                            //La méthode de factorisation du code qui va traiter les nouveaux éléments ajoutés.
                            private void OnNewCollectionEntreeAdded(IEnumerable<CollectionEntree> entries)
                            {
                            	if(entries != null)
                            	{
                            		foreach(CollectionEntree e in entries)
                            		{
                            			e.PropertyChanged += this.Entree_PropertyChanged;
                            		}
                            	}
                            	RaisePropertyChanged(nameof(UcEntreeViewModel.TotalEntree));
                                MediatorCalcul.Mediator.CalculEntree = TotalEntree;
                            }


                            Dans les lignes de code que tu as posté en dernier, ton ItemEntree.Sum n'est jamais utilisé. Il est inutile donc. Les méthodes d'extension Linq permettent de rapprocher le C# d'un style de programmation fonctionnelle. Dans "fonctionnelle", il y a "fonction". La définition est strictement identique à celle donnée en mathématique : Une fonction prend "des trucs" en entrée et sort "quelque chose" en sortie. LINQ marche de la même façon : Ca prend des entrées (généralement une collection de chose + parfois, d'autres paramètres) et ça sort un truc sortie. Dépendant de la fonction, la sortie peut être une collection d'autre chose (Where, Select, SelectMany par exemple) ou un élément unique (si c'est une fonction d'aggrégation comme Aggregate, Sum, Min, Max ou une fonction de sélection comme Single, First, Last, ...)

                            Là, ta ligne applique une aggrégation pour calculer la somme, mais le résultat n'est jamais conservé où que ce soit : cet appel ne sert à rien.

                            -
                            Edité par Nisnor 9 novembre 2018 à 9:58:55

                            • Partager sur Facebook
                            • Partager sur Twitter
                              9 novembre 2018 à 12:55:21

                              Encore une fois un grand merci, mais je m'aperçois que je n'aurais jamais pu m'en sortir tout seul. Le "jonglage" entre les ObservableCollection et les Inumerable est encore bien loin de mes connaissances.

                              Par contre je ne comprends pas bien la ligne:

                              this.OnNewCollectionEntreeAdded(e.NewItems?.Cast<CollectionEntree>());

                              J'ai remis tout en forme et ça fonctionne c'est super.

                              Bravo à toi.

                              • Partager sur Facebook
                              • Partager sur Twitter
                                9 novembre 2018 à 16:13:21

                                "Le "jonglage" entre les ObservableCollection et les Inumerable est encore bien loin de mes connaissances."

                                Il est pourtant "assez simple".

                                Ca fait parti de ce qu'on appelle le polymorphisme.

                                IEnumerable, c'est une interface. Ce type d'élément en C# ne te proposera JAMAIS une implémentation concrète. La seule chose que ça te permettra, c'est de savoir qu'est ce qui est disponible sur "un objet", sans connaitre exactement l'objet en question. C'est exactement la même chanson avec INotifyPropertyChanged dont on parlait avant dans cette discussion et qui est utilisée par WPF pour intercepter les changements de propriétés. WPF ne connaitra JAMAIS les objets que tu pourrais lui donner à manger, et pourtant, il arrive à savoir si tes objets implémentent ce INotifyPropertyChanged et si oui, il va s'abonner à l'événement PropertyChanged, puisque c'est la seule chose que cette interface te permet de connaitre.

                                Si tu regarde la déclaration de IEnumerable<T>, il y a GetEnumerator de disponible, qui retourne un IEnumerator<T>. Encore une interface! Mais plus complète cette fois-ci : Current, Reset, MoveNext, Dispose. De manière générale, un énumérateur en C#, c'est "une bande magnétique" (comme les cassettes VHS ou audio) : T'as une liste d'objet rangés les uns après les autres et dont le parcours ne peut être fait que dans un seul sens, avec possibilité de rembobiner l'intégralité de la cassette.

                                Ces interfaces sont utilisées par les boucles foreach. Comprendre aussi : "foreach", c'est un sucre syntaxique du langage C# qui t'épargne d'avoir à écrire quelque chose comme ça à chaque fois que tu veux parcourir l'intégralité d'une collection :

                                IEnumerator enumerateur = taCollectionIEnumerable.GetEnumerator();
                                while(enumerateur.MoveNext())
                                {
                                   //Le contenu entre { } de la boucle foreach ici avec enumerateur.Current contenant l'objet en cours
                                }

                                LINQ-To-Object aussi se sert massivement de cette interface IEnumerable<T> pour fonctionner. En fait, toutes les méthodes LINQ-To-Object sont des méthodes d'extensions dont l'objet de travail source est un IEnumerable<T>.

                                Pour en revenir au jonglage...Une ObservableCollection...C'est une liste d'objet un peu spéciale. Qui dit "liste d'objet", dit que c'est surement parcourable de façon séquentielle et d'ailleurs, si tu peux faire un foreach sur une ObservableCollection, ce n'est pas pour rien.ObservableCollection implémente IEnumerable<T>...Donc toute instance de ObservableCollection<T> est intrinsèquement "une instance de IEnumerable<T>" : c'est polymorphique. Un objet A est "du même type que toutes les interfaces qu'il implémente en même temps", en plus de Object, puisqu'en .NET, TOUT est Object (même les entiers).

                                Sinon, pour savoir si tu peux ou non considérer tel ou tel objet du framework comme autre chose, il te suffit de lire sa documentation : ObservableCollection<T> :

                                public class ObservableCollection<T> : System.Collections.ObjectModel.Collection<T>, System.Collections.Specialized.INotifyCollectionChanged, System.ComponentModel.INotifyPropertyChanged

                                et en cas d'héritage, remonter sur les classes parentes. Collection<T> :

                                public class Collection<T> : System.Collections.Generic.ICollection<T>, System.Collections.Generic.IEnumerable<T>, System.Collections.Generic.IList<T>, System.Collections.Generic.IReadOnlyCollection<T>, System.Collections.Generic.IReadOnlyList<T>, System.Collections.IList

                                Attention : Parfois, il faut aussi intuiter. Une interface peut en cacher plusieurs! (une interface peut "hériter" de plusieurs autres interfaces).

                                "Par contre je ne comprends pas bien la ligne"

                                e.NewItems peut être null.

                                Or, la méthode d'extension Linq Cast ne peut pas fonctionner sur null. Ca aurait pété une exception.

                                Pour palier à ça, soit j'utilisais la technique classique du "if(e.NewItems != null)"...Soit je fais appel aux nouveaux sucres syntaxique apportés par C# 6 : L'opérateur null-conditional (désolé pour le franglais, c'est chiant à traduire en Français ^^). Cet opérateur remplace tous les appels réalisés sur un membre de gauche, par des if leftMember != null avant de traiter l'expression de droite et si le membre de gauche vaut null, ça retourne null (au lieu de claquer une NullReferenceException).

                                Dans ce cas précis, e.NewItems?.Cast<CollectionEntree>() me retournera soit l'instance IEnumerable<CollectionEntree> issue de e.NewItems si c'est pas null, ou null sinon. L'usage du Cast<> est nécessaire, parce que ma méthode de factorisation travaille avec des IEnumerable<CollectionEntree> et e.NewItems est de type IList. A un moment ou à un autre, je dois "convertir" (caster) ce IList en IEnumerable<CollectionEntree>. Ce cast est réalisé automatiquement par foreach, avec les même risques : Si c'est pas castable dans le type désiré, ça pètera une exception "InvalidCastException".

                                -
                                Edité par Nisnor 9 novembre 2018 à 16:17:36

                                • Partager sur Facebook
                                • Partager sur Twitter
                                  9 novembre 2018 à 23:29:57

                                  Merci Nisnor pour toutes ces explications que je vais relire à tête reposé. Je trouve tes exemples très pédagogiques et assez facilement compréhensibles.

                                  Par contre je pense que tu dois commencer à trouver lourdingue d'être obligé de tout me détailler ainsi. Je te trouve d'ailleurs très patient.

                                  Pourrais tu me conseiller un(des) site(s) ou un(des) livre(s) pour pouvoir comprendre tout cela en partant d'un assez bas niveau. Par contre le problème est que l'anglais et moi on est fâché...

                                  Je pense que cela te libérerait du temps et je pourrais revenir pour te poser des questions bien plus inintéressantes.

                                  Merci encore de passer autant de temps pour moi. 

                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    11 novembre 2018 à 22:01:26

                                    Passer du temps, c'est pas le problème. Tant que l'interlocuteur comprend quelque chose et qu'on avance, c'est le principal ^^

                                    Pour les livres, malheureusement non, je n'ai pas de référence. Personnellement, je me suis auto-formé et j'ai appris par expérience.

                                    En revanche, que l'anglais soit un problème est doublement problématique, parce que les très bonnes ressources abondent en Anglais, et sont plus rare en FR.

                                    -
                                    Edité par Nisnor 11 novembre 2018 à 22:01:44

                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                      12 novembre 2018 à 9:59:11

                                      Merci Nisnor, je vais essayé de faire comme toi et continuer l'auto-formation. Je pense qu'il va me falloir pas mal de temps pour arriver à un niveau "convenable". Il y a énormément de choses à voir et je crois que j'ai quelques soirées d'hiver qui seront bien remplies :D.

                                      Pour l'anglais, si tu as des sites assez bien fait, le traducteur permet une lecture à peu près correcte.

                                      Je continue en travaillant sur des applis personnelles ce qui me permet, je pense, d'apprendre en ayant l'impression de faire quelque chose qui pourrait me servir.

                                      Je pense que nous n'avons pas fini nos contacts :lol:.

                                      A bientôt...

                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                        21 novembre 2018 à 0:27:30

                                        Bonsoir Nisnor, je suis de retour car je pense que je devais te manquer :lol:.

                                        Mon application commence à prendre forme et je suis content car j'avance pas mal. Juste la sauvegarde dans la base de données que j'effectue à chaque changement car je n'arrive pas à trouver ou plutôt à comprendre comment je peux faire une sauvegarde globale sous Entity Framework en utilisant un bouton. Peut-être as-tu une idée bien que j'ai cru comprendre que tu n'utilises pas EF. Mais sais-t-on jamais...

                                        J'ai un autre souci qui n'est pas de la programmation. Je travaille avec Visual Studio Community 2017. Dans les tutos beaucoup parle de DataGridView mais dans ma boite à outils je n'ai qu'un DataGrid. Serais-tu pourquoi? Apparemment l'utilisation des datagridview est plus simple et plus souple.  

                                        Merci d'avance pour ta réponse.

                                        • Partager sur Facebook
                                        • Partager sur Twitter
                                          21 novembre 2018 à 11:00:32

                                          "DataGridView", c'est plutôt du Winforms, pas du WPF.
                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                          Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
                                            21 novembre 2018 à 11:46:55

                                            Hello,

                                            Et bon retour parmi les "qui rapportent pas" :p

                                            Pour la DGV, tout est dit ^^ .

                                            "j'ai cru comprendre que tu n'utilises pas EF"

                                            Au contraire : Aujourd'hui, me passer d'EF serait compliqué. Quand j'ai à refaire de l'ADO.NET, c'est pas très facile ^^ .

                                            Que tu fasse de la sauvegarde au changement ou de la sauvegarde avec bouton, c'est faisable dans les 2 cas, mais pas de la même façon. Pour ça, il suffit de comprendre comment marche EF.

                                            Le point d'entrée d'EF, c'est le contexte d'entité. C'est "ce truc" qui fait le lien entre la connexion à une source de données et des types & objets, appelés entités. Quand un contexte émet une instance d'une entité, cette entité est rattachée au contexte. Voila. C'est tout.

                                            Maintenant, que se passe-t-il quand tu fais un using(tonContext[...]) {} et que tu gardes une référence à une entité dont le contexte a été fermé? Simple également : Cette entité, anciennement attachée, devient détachée. Elle reste utilisable pour "formaliser la manipulation de tes données dans ton code", mais tu ne pourras naturellement pas l'utiliser en l'état pour persister les modifications dans ta source de données (puisque le lien instance d'entité <-> source de données, représenté par le contexte, est rompu).

                                            Pour pouvoir utiliser cette entité détachée pour sauvegarder son contenu, il te faut d'abord la ré-attacher à un nouveau contexte ouvert. Mais si tu réalises cette modification de la sorte, tu perd toute forme de tracking intelligent des modifications (ce tracking est également lié au contexte). En temps normal, ce tracking fait en sorte que, par exemple, sur une entité qui a 3 propriétés, si tu ne modifie le contenu que d'1 seule des 3, la requête de mise à jour ne portera que sur cette propriété et pas les autres. De même, si tu add ou update une nouvelle entité, la gestion est plus fine et présente moins de risque que si tu pars du principe que si ton entité dispose d'une clé renseignée, c'est un update, sinon, c'est un add.

                                            Juste en sachant ça, libre à toi de tourner ta façon de sauvegarder comme tu l'entends.

                                            Personnellement, j'ai plus tendance à jongler avec des entités détachées et ne communiquer avec la source de données que quand c'est nécessaire...Mais rien ne t'empeche de conserver une instance unique du contexte dans ton unité métier (viewmodel) et de reléguer la gestion de tes entités à ce contexte (plutôt que d'avoir à les gérer toi même à la main).

                                            • Partager sur Facebook
                                            • Partager sur Twitter
                                              21 novembre 2018 à 12:07:01

                                              Merci pour ta réponse je vais continué.

                                              Bon courage

                                              • Partager sur Facebook
                                              • Partager sur Twitter
                                                22 novembre 2018 à 19:00:05

                                                Bonsoir Nisnor,

                                                Je suis déjà de retour... 

                                                Pour tout avouer j'avoue ne pas comprendre comment réaliser les persistances de mais données en utilisant un bouton.

                                                Dans un ViewModel (VM1) je permets la saisie des données (rien a changé). Le bouton de sauvegarde se trouve dans un autre ViewModel (VM2). J'ai cru comprendre que je dois travailler avec des entités détachées. J'ai regardé mais je ne pense pas comprendre comment faire... De plus dois-je me servir là aussi du mediator et si oui que dois-je écrire?

                                                Je te fait suivre le morceau de programme du VM1 dans lequel je fais une persistance à caque saisie, mais je pense que ça ralentit le traitement.

                                                private void ItemEntree_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
                                                        {
                                                            // Détection de l'ajout ou du changement d'un item dans la collection
                                                            if (e.NewItems != null)
                                                            {
                                                                foreach (CollectionEntree entree in e.NewItems)
                                                                {
                                                                    entree.PropertyChanged += Entree_PropertyChanged;
                                                                }
                                                            }
                                                            // Détection de la suppression d'un item dans la collection
                                                            if (e.OldItems!=null)
                                                            {
                                                                foreach (CollectionEntree entree in e.OldItems)
                                                                {
                                                                    entree.PropertyChanged -= Entree_PropertyChanged;
                                                                    OnNewCollectionEntreeAdded(e.NewItems?.Cast<CollectionEntree>());
                                                                    Mediator.MediatorCalcul.CalculEntree = TotalEntree;
                                                
                                                                    var ControlEntree = BddOperationMensuelle.Entrees.Find(Cle);
                                                                    BddOperationMensuelle.Entrees.Remove(ControlEntree);
                                                                    BddOperationMensuelle.SaveChanges();
                                                                }
                                                            }
                                                        }
                                                
                                                        // Mise à jour de l'observablecollection
                                                        private void Entree_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
                                                        {
                                                                var entree = new CollectionEntree()
                                                                {
                                                                    OperationEntree = ItemSelectionne.OperationEntree,
                                                                    CreditEntree = ItemSelectionne.CreditEntree
                                                                };
                                                
                                                            // Enregistrement dans la base d'une nouvelle opération
                                                            if (Cle== 0 && ItemSelectionne.OperationEntree != null && ItemSelectionne.CreditEntree != 0)
                                                                {
                                                                    var ControlEntree = new Entree();
                                                                    {
                                                                        ControlEntree.Validation = ItemSelectionne.ValidEntree;
                                                                        ControlEntree.Operation = ItemSelectionne.OperationEntree;
                                                                        ControlEntree.Credit = ItemSelectionne.CreditEntree;
                                                                    }
                                                                BddOperationMensuelle.Entry(ControlEntree).State = EntityState.Added;
                                                                BddOperationMensuelle.Entrees.Add(ControlEntree);
                                                                }
                                                
                                                                // Enregistrement dans la base de la modification d'une opération
                                                                if (Cle!=0 && ItemSelectionne.OperationEntree != null && ItemSelectionne.CreditEntree != 0)
                                                                {
                                                                var ControlEntree = BddOperationMensuelle.Entrees.Find(Cle);
                                                                    {
                                                                        ControlEntree.Validation = ItemSelectionne.ValidEntree;
                                                                        ControlEntree.Operation = ItemSelectionne.OperationEntree;
                                                                        ControlEntree.Credit = ItemSelectionne.CreditEntree;
                                                                    }
                                                                }
                                                            RaisePropertyChanged(nameof(UcEntreeViewModel.TotalEntree));
                                                            Mediator.MediatorCalcul.CalculEntree = TotalEntree;
                                                            BddOperationMensuelle.SaveChanges();
                                                
                                                        }
                                                    

                                                Pour l'instant la procédure du bouton de sauvegarde dans VM2 est vide. J'ai essayé des choses mais rien ne fonctionne.

                                                Merci pour un nouveau coup de main.

                                                • Partager sur Facebook
                                                • Partager sur Twitter
                                                  23 novembre 2018 à 14:10:32

                                                  Le bouton se trouve dans VM2 et la donnée dans VM1? Bon...Et bien trouve un moyen de (au choix) :

                                                  • Partager la donnée entre les 2 pour que le 1 modifie les données (en mémoire) et le 2 lise ces données (de la mémoire) pour les persister ailleurs (sur le disque en l’occurrence). N'oublie pas de raisonner "instance" et "référence". Si VM2 récupère une instance de "MonObject" et la conserve dans une variable d'instance "_vm2MonObject" et que VM1 récupère cette même instance depuis VM2 et la conserve dans une variable d'instance "_vm1MonObject", alors, "_vm2MonObject" et "_vm1MonObject" pointent vers le même espace mémoire, ces deux objets sont strictement égaux. Ca induit que tout modification réalisée depuis VM1 sur "_vm1MonObject" sera instantanément disponible dans "_vm2MonObject" de VM2. "_vm1MonObject" et "_vm2MonObject" sont 2 "pointeur mémoire différents" mais qui "pointent vers le même espace mémoire". En .NET, on dit simplement que ce sont 2 références vers le même objet (et y'a vraiment pas à interpréter les mots. Ce serait pareil dans une bibliothèque qui disposerait de 2 entrées séparées avec 2 index des livres, un pour chaque entrée. Si EntréeA t'indique allée B rangée 32 et que EntréeB t'indique allée B rangée 32, les 2 index "référencent" le même emplacement physique). La différence entre .NET et C++, c'est que .NET dispose d'un mécanisme qui va détecter le moment où l'espace mémoire pointé n'est plus pointé par personne et si c'est le cas, il va désallouer automatiquement la mémoire, chose qui n'est pas faite en C++ (sauf cas des pointeurs automatique apportés par C++11 : Un new sans un delete, c'est une fuite mémoire!)
                                                  • Informer VM1, depuis VM2, qu'il doit persister les données qu'il détient.
                                                  • Partager sur Facebook
                                                  • Partager sur Twitter
                                                    23 novembre 2018 à 16:58:09

                                                    Merci Nisnor pour ta réponse.

                                                    Je sais je suis un boulet mais je n'y arrive pas. J'ai ajouté dans le VM1, celui qui s'occupe de la persistance, les lignes suivantes:

                                                    public MainViewModel()
                                                    		{        
                                                    			Table = new BddContext("BddOperationMensuelle.mdf");
                                                                SaveCommand = new BasicAction(Enregistrer);
                                                            }
                                                    
                                                            private BddContext table;
                                                            public BddContext Table
                                                            {
                                                                set
                                                                {
                                                                    table = value;
                                                                    RaisePropertyChanged(nameof(MainViewModel.Table));
                                                    
                                                                }
                                                                get
                                                                {
                                                                    return table;
                                                                }
                                                    		}
                                                            private void Enregistrer()
                                                            {
                                                                Table.SaveChanges();
                                                            }

                                                    Dans le VM2 j'ai supprimé la ligne BddOperationMensuelle.SaveChanges(); mais bien sur ça ne fonctionne pas mais ça ne m'étonne pas non plus car je n'y arrive que très difficilement.

                                                    Où je dérape pour rester poli? 



                                                    • Partager sur Facebook
                                                    • Partager sur Twitter
                                                      25 novembre 2018 à 10:27:02

                                                      Là, sans le code XAML qui va avec, je peux rien dire d'autre que "es-tu sûr que l'action SaveCommand est bindée quelque part dans le XAML pour que ça déclenche l'appel à la méthode Enregistrer?."
                                                      • Partager sur Facebook
                                                      • Partager sur Twitter
                                                        26 novembre 2018 à 23:57:24

                                                        Après avoir relu ta dernière réponse et une bonne réflexion, j'y suis arrivé.

                                                        Je suis assez content de m'en être sorti presque tout seul, je dis bien presque...

                                                        J'ai utilisé le médiator et ça fonctionne nickel.

                                                        Encore un grand merci et je crois que ton aide commence doucement à porter ces fruits. Je sais c'est long :lol:

                                                        • Partager sur Facebook
                                                        • Partager sur Twitter
                                                          28 novembre 2018 à 11:18:00

                                                          Bonjour Nisnor,

                                                          J'ai juste une petite question, peut-on définir une variable contenant le nom d'une table de base de données? Et si oui comment?

                                                          Merci pour ta réponse.

                                                          • Partager sur Facebook
                                                          • Partager sur Twitter
                                                            29 novembre 2018 à 13:58:15

                                                            Rien ne t'en empêche...

                                                            Mais clairement, EF n'est pas prévu pour fonctionner de la sorte. Tu vas t'exposer à de nombreux problèmes a fonctionner de la sorte.

                                                            • Partager sur Facebook
                                                            • Partager sur Twitter
                                                              29 novembre 2018 à 15:13:05

                                                              Bonjour Nisnor et merci pour ta réponse.

                                                              Je t'explique, j'ai une base de données contenant des tables correspondants aux mois de l'année. Je choisis le mois à l'aide d'un expander. Comment puis-je demander à EF de travailler avec la bonne table? 

                                                              J'ai essayé de créer une variable et de m'en servir en utilisant cette écriture: BddContext.Nom de la variable mais j'ai une erreur car Visual ne reconnait pas la variable.

                                                              Aurais-tu une idée sur la façon de faire? Merci d'avance.

                                                              • Partager sur Facebook
                                                              • Partager sur Twitter

                                                              Liaison Model et ViewModel

                                                              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                                              × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                                              • Editeur
                                                              • Markdown