2016-09-26 1 views
1

J'ai un grand modèle qui a été partiellement mis à jour via la désérialisation. Comme il n'a été que partiellement mis à jour, je voudrais ignorer toutes les valeurs nulles quand je passe cela à ma mise à jour de cadre d'entité. En fin de compte, le EntityState.Modified est défini, mais le problème est que tous les champs sont mis à jour. Cela signifie que tout ce qui était nul est maintenant effacé dans la base de données.
Est-il possible de modifier ce comportement par défaut via un paramètre ou remplacer une méthode pour vérifier null? Il semble que puisque le contexte attend le modèle complet, je ne peux pas simplement définir seulement quelques valeurs.
J'ai vérifié cela en mappant uniquement ce que j'ai besoin de modifier et le même comportement se produit.Ignorer les valeurs nulles dans la mise à jour de contexte

Répondre

0

C'est un problème un peu fastidieux que j'ai dû résoudre. Faute d'une solution plus simple, j'ai finalement décidé de ne pas vouloir essayer de le résoudre autant en aval que lorsque les SaveChanges-and-likes d'EF sont appelés (c'est-à-dire, pas besoin de "EF si tardif", mais au contraire le plus en amont/le plus tôt possible -

c'est-à-dire, juste après avoir obtenu une désérialisation satisfaisante, qui est significative pour muter le modèle, sur une entité par entité base d'instance (dans mon cas d'utilisation, aucune des propriétés modifiables ne représenterait les relations, mais uniquement les attributs indépendants, en langage E/R - YMMV)

J'ai donc opté pour une aide «Populate», sur le modèle de :

static void Populate(object from, object to) 
    { 
     var sourceType = from.GetType(); 
     foreach (PropertyInfo target in to.GetType().GetProperties()) 
     { 
      // Is the property at the target object writable and *not* marked 
      // as `[NotMapped]'? 
      var isUpdatable = 
       target.CanWrite && 
       (target.GetCustomAttribute<NotMappedAttribute>(true) == null); 
      if (isUpdatable) 
      { 
       // If so, just find the corresp. property with the same name at the source object 
       // (caller is assumed responsible to guarantee that there is one, and of the same type, here) 
       var source = sourceType.GetProperty(target.Name); 

       var @default = sourceType.IsValueType ? Activator.CreateInstance(sourceType) : null; 
       var equality = (IEqualityComparer)typeof(EqualityComparer<>).MakeGenericType(sourceType).GetProperty("Default", BindingFlags.Public | BindingFlags.Static).GetValue(null); 
       var value = source.GetValue(from); 

       // Test for <property value> != default(<property type>) 
       // (as we don't want to lose information on the target because of a "null" (or "default(...)") coming from the source) 
       if (!equality.Equals(value, @default)) 
       { 
        target.SetValue(to, value, null); 
       } 
      } 
     } 
    } 

où « de » est l'instance d'entité nouvelle qui vient de se partiellement peuplé quel que soit le code désérialisation, et où « à » est l'entité cible réelle qui vit dans le DbContext (que ce soit un proxy EF ou non) ;

et où NotMappedAttribute est l'EF habituel. Vous devez généralement appeler Populate un certain temps après la désérialisation (&/ou DTO-mapping) sur votre instance "from", mais avant que SaveChanges() ne soit appelé sur votre DbContext pour tous les "" à "entités - évidemment, nous supposons qu'il y a une correspondance 1-to-1 possible" de "..." à ", que l'appelant de Populate sait/pourrait comprendre. Note Je ne sais toujours pas s'il y a une façon plus élégante (plus directe) de le faire, sans avoir recours à la réflexion - donc, là, FWIW.

Remarques

1) au-dessus du code peuvent (ou doivent) être plus défensif de différentes manières, selon les hypothèses de l'appelant;

2) on peut vouloir mettre en cache ceux de IEqualityComparer (&/ou les PropertyInfo) pour quelque raison que ce soit (bonne) - dans mon cas, je n'ai pas eu à;

3) enfin, je crois comprendre que les librairies tiers tels que AutoMapper sont spécialement conçus pour ce genre de tâche, si vous pouvez vous permettre la dépendance supplémentaire

« HTH,

+1

j'ai quelque chose très similaire en ce que j'ai créé une usine de cartographie qui est responsable de la cartographie entre les différentes couches de l'infrastructure (par exemple, API-> Business-> Data). J'apprécie que Reflection ne soit pas une excellente option pour vous non plus. Je ne pense pas que cela ait du sens dans ce cas non plus.De plus, mon groupe de travail avait examiné AutoMapper mais l'avait rejeté pour d'autres raisons. Merci pour les commentaires. – McArthey

+0

'bienvenue et bien, oui, en plus d'utiliser quelque chose comme AutoMapper ou réflexion, j'aurais aimé trouver autre chose qui ne nécessiterait pas de piratage dans EF, mais l'assistant ci-dessus fait très bien l'affaire jusqu'ici dans mon cas. Quoi qu'il en soit, je me suis assuré qu'il pourrait être utilisé dans un endroit central, facile à refactoriser (dois-je faire différemment). – YSharp

+0

Après examen, ce code fonctionne bien (moins quelques vérifications nuls) mais peut-être que je suis mal compris quelque chose? Le modèle résultant que je suis mappage à la base de données ('to' dans ce cas) contient toujours toutes les propriétés mappées du modèle après la réflexion, ce qui signifie que les valeurs nulles causent toujours des problèmes au niveau de la couche de données. En fin de compte, j'ai besoin de supprimer toutes les propriétés qui contiennent des valeurs nulles. – McArthey