10
votes

Mettre à jour l'entité de ViewModel en MVC à l'aide de MODApper

J'ai un fournisseur.cs code> entité et sa viewModel fournisseurvm.cs code>. Je tente de mettre à jour un fournisseur existant, mais je reçois l'écran jaune de la mort (YSOD) avec le message d'erreur:

L'opération a échoué: la relation n'a pas pu être modifiée car une ou plusieurs des propriétés de clé étrangère ne sont pas nullables. Lorsqu'une modification est faite à une relation, la propriété Key de l'étranger associée est définie sur une valeur null. Si la clé étrangère ne prend pas en charge les valeurs NULL, une nouvelle relation doit être définie, la propriété-clé étrangère doit être attribuée à une autre valeur non nulle ou l'objet non lié doit être supprimé. P> blockQuote>

i pense strong> Je sais pourquoi cela se produit, mais je suis Je ne sais pas comment le réparer fort>. Voici un Screencast de ce qui se passe. Je pense que la raison pour laquelle je reçois l'erreur est dû au fait que cette relation est perdue lorsque MODAPPER fait sa chose em>. P>

code stry> P> P> P> P> P > Voici les entités fortes> que je pense être pertinentes: p> xxx pré>

et voici mon ViewModel strong>: p>: p>: p>

public void UpdateSupplier(SupplierVm supplier)
{
  var updatedSupplier = _supplierRepository.Find(supplier.Id);
  Mapper.Map(supplier, updatedSupplier); // I lose navigational property here
  _supplierRepository.Update(updatedSupplier);
  _supplierRepository.Save();
}


16 commentaires

Vous devriez vérifier ce que mis à jour y a été mis à jour, a-t-il des propriétés enfants. L'erreur en fait que les articles de votre enfant deviennent nuls et d'une erreur clé étrangère.


À quelle étape? Voulez-vous dire après que MODApper fait sa chose ou avant? Si vous voulez dire avant cela, tout semble bien, tout est comme il devrait être aussi loin que je peux dire.


Ok qui suggère que la cartographie ne définit pas la propriété de navigation. Essentiellement .Formember (d => D.Adresses, O => O.UtilisationStinulationValue ()) ne fonctionne pas. Vous pourriez avoir besoin de créer explicitement une configuration de mappage.


Jetez un coup d'œil à la deuxième réponse sur Cet article . Traite de la cartographie et de la préservation de classes imbriquées comme si vous faites


Merci @big_water, qu'est-ce que .Formember (dest => dort.id, opt => {opt.usetinationValue (); opt.ignore ();}); faire? C'est comme si vous lui avez dit d'utiliser la destinationValue, mais en même temps pour l'ignorer, ce qui n'a pas de sens pour moi!


@Ciwan, il ignore la clé d'identité car cela pourrait être null. Si vous utilisez les valeurs de destination dans votre mappage, il n'y aura pas de mises à jour des propriétés de navigation. Ainsi, l'ignorant peut être la solution car elle ressemble aux propriétés de navigation pouvant avoir des valeurs nuls.


@Ciwan, vous avez des mappages définis pour votre adresse et des classes de contact à leurs modèles de vue respectifs, non?


@big_water, oui je viens d'ajouter ceux à la question


@Ciwan, on dirait que vous pourriez avoir une référence circulaire qui cause-t-elle à la faille de la faille. Pouvez-vous poster votre adresse et contacter des cours?


@Big_water, pas de problème. J'ai ajouté ceux aussi


@Ciwan, vous avez une référence circulaire de l'entreprise aux adresses, puis de retour du modèle d'adresse à l'entreprise. Même avec le contact. Essayez de commenter ce business commercial virtuel public {obtenir; ensemble; } de votre adresse et de votre classe de contact et de l'exécuter.


Laissez-nous Continuez cette discussion dans le chat .


@Ciwan I Suspect MOWORAPPPER peut jouer sur le iCollection <-> IList de conversion. Pouvez-vous mettre à jour votre modèle d'entité de sorte que adresses et contacts sont ilistes ? Ou autrement, examinez cette question, qui suggère de créer un mappage entre les deux types: Stackoverflow.com/questions/13479208/...


Salut .. Je pense que le problème de la perte de propriété de la navigation est que vous faites la carte à l'extérieur du référentiel .. Donc, il ne peut donc pas revenir DinamycCaly (LAZYLOADING) votre propriété de navigation. Si vous voulez le faire à l'extérieur de la répétition. Vous devez Elevé charger les entités associées (navigation) que vous souhaitez être mappée sur votre viewModel


Voulez-vous mettre à jour les propriétés fournisseur uniquement, ou aussi les propriétés des adresses, etc.?


Un fournisseur a une liste d'adresses et de contacts. Mais une seule adresse et un contact peut être actif ( isdeletted == false ) à la fois. Lorsque je modifie un fournisseur, je modifie le fournisseur et son adresse active et son contact. Je veux donc toutes les 3 entités mises à jour.


4 Réponses :


0
votes

J'ai eu cette question plusieurs fois et est normalement ceci:

L'ID FK sur la référence des parents ne correspond pas à la PK sur cette entité FK. C'est-à-dire que si vous avez une table de commande et une table orderstatus. Lorsque vous chargez à la fois dans des entités, commandez a orderstatusid = 1 et l'orderstatus.id = 1. Si vous changez d'orderstatusid = 2 mais ne mettez pas mettre à jour Orderstatus.Id à 2, vous obtiendrez cette erreur. Pour le réparer, vous devez soit charger l'ID de 2 et mettre à jour l'entité de référence, soit simplement définir l'entité de référence Orderstatus sur commande à NULL avant d'économiser.


0 commentaires

0
votes

Je ne suis pas sûr que cela va correspondre à votre exigence, mais je suggérerais de suivre.

de votre code, il semble sûrement que vous perdiez une relation pendant la cartographie.

Pour moi, cela ressemble à cela dans le cadre de la mise à jour des mises à jour, vous ne mettez pas en train de mettre à jour l'un des détails de l'enfant du fournisseur.

Si tel est le cas, je suggérerais d'UpdAdate uniquement des propriétés modifiées du fournisseur vers la classe fournisseur de domaine. Vous pouvez écrire une méthode distincte dans laquelle vous allez attribuer des valeurs de propriété à partir de fournisseurs à l'objet fournisseur (cela ne doit modifier que les propriétés non-enfants telles que le nom, la description, le site Web, le téléphone, etc.).

puis effectuez la mise à jour de dB. Cela vous évitera de la messpure possible des entités suivies.

Si vous modifiez les entités d'enfants du fournisseur, je suggère de les mettre à jour indépendamment des fournisseurs car la récupération d'un graphe d'objet entier à partir de la base de données nécessiterait de nombreuses requêtes à exécuter et de mettre à jour qu'il exécutera également des requêtes de mise à jour inutiles sur la base de données.

Mise à jour des entités permettrait de sauver de manière indépendante des opérations de base de données et d'ajouter aux performances de l'application.

Vous pouvez toujours utiliser la récupération du graphe d'objet entier si vous devez afficher tous les détails sur le fournisseur dans un écran. Pour les mises à jour, je ne recommanderais pas la mise à jour de l'ensemble du graphique d'objet.

J'espère que cela aiderait à résoudre votre problème.


0 commentaires

9
votes

la cause

la ligne ... p> xxx pré>

... Est-ce que beaucoup plus que la rencontre de l'œil. P>

  1. Au cours de l'opération de mappage, updatedatedateUPLIPLIER code> charge ses collections ( adresses code>, etc.) paresseusement parce que SOPAPPER (AM) les accède. Vous pouvez vérifier cela en surveillant les instructions SQL. LI>
  2. am remplace em> ces collections chargées par les collections Il mesure du modèle de vue. Cela se produit malgré le paramètre code> EveluStinyStizeValue. (Personnellement, je pense que ce paramètre est incompréhensible.) Li> ol>

    Ce remplacement a des conséquences inattendues: p>

    1. Il laisse les éléments d'origine dans les collections attachées au contexte, mais ne sont plus dans la portée de la méthode de votre choix. Les éléments sont toujours dans les collections code> locales code> (comme contexte .Addresses.Local code>) mais maintenant privé de leur parent, car ef a exécuté réparation de relations em>. Leur état est modifié code>. Li>
    2. Il attache les éléments du modèle d'affichage au contexte dans un état code> ajouté. Après tout, ils sont nouveaux dans le contexte. Si, à ce stade, vous attendez 1 adresse code> dans context.addresses.local code>, vous verriez 2. Mais vous ne voyez que les éléments ajoutés dans le débogueur. li> OL>

      Ce sont ces éléments «modifiés» moins parents qui provoquent l'exception. Et si ce n'est pas le cas, la prochaine surprise aurait été que vous ajoutez de nouveaux éléments à la base de données pendant que vous attendez uniquement des mises à jour attendues. P>

      OK, maintenant quoi? H3>

      Alors comment Fixez ceci? P>

      a. strong> J'ai essayé de rejouer votre scénario aussi étroitement que possible. Pour moi, une solution possible consistait en deux modifications: p>

      1. Désactiver le chargement paresseux. Je ne sais pas comment vous l'organiseriez avec vos référentiels, mais quelque part il devrait y avoir une ligne comme p>

        context.Entry(updatedSupplier).CurrentValues.SetValues(supplier);
        


3 commentaires

Merci Gert, c'était une belle réponse!


Salut @gert, je vois dans la question, le point de vue de l'OP sur la couche de service? Est-ce bon design?


@Willy la plupart du temps, un modèle de vue n'est qu'un dto. Pour moi, il est correct de passer des DTO à partir d'une couche de service - en fait, c'est ce qu'ils sont pour. Si un modèle de vue est vraiment un modèle de vue (contenant InotifyPropertyChanged et / ou tout le shebang de diriger la vue), non, je ne le ferais pas, car il saigne probablement les dépendances de la bibliothèque d'interface utilisateur dans la couche de service.



1
votes

J'ai recherché toutes les réponses de Stackoverflow et Google Recherches. Enfin, j'ai juste ajouté 'db.configuration.lazyladingenabled = false;' ligne et cela fonctionnait parfaitement pour moi.

 var message = JsonConvert.DeserializeObject<UserMessage>(@"{.....}");
        using (var db = new OracleDbContex())
        {
            db.Configuration.LazyLoadingEnabled = false;
            var msguser = Mapper.Map<BAPUSER>(message);
            var dbuser = db.BAPUSER.FirstOrDefault(w => w.BAPUSERID == 1111);
            Mapper.Map(msguser, dbuser);
            //  db.Entry(userx).State = EntityState.Modified;

            db.SaveChanges();
        }


0 commentaires