web-dev-qa-db-fra.com

Est-ce que chaque relation de données centrale doit avoir un inverse?

Disons que j'ai deux classes d'entité: SocialApp et SocialAppType

Dans SocialApp j'ai un attribut: appURL et une relation: type.

Dans SocialAppType J'ai trois attributs: baseURL, name et favicon.

La destination de la relation SocialApptype est un seul enregistrement dans SocialAppType.

Par exemple, pour plusieurs comptes Flickr, il y aurait un nombre d'enregistrements SocialApp, chaque enregistrement contenant un lien vers le compte d'une personne. Il y aurait un enregistrement SocialAppType pour le type "Flickr" auquel tous les enregistrements SocialApp pointeraient.

Lorsque je crée une application avec ce schéma, je reçois un avertissement indiquant qu'il n'existe aucune relation inverse entre SocialAppType et SocialApp.

 /Users/username/Developer/objc/TestApp/TestApp.xcdatamodel:SocialApp.type: warning: SocialApp.type -- relationship does not have an inverse

Ai-je besoin d'un inverse et pourquoi?

142
Alex Reynolds

En pratique, je n'ai pas eu de perte de données parce que je n'avais pas d'inverse - du moins à ma connaissance. Un rapide Google suggère de les utiliser:

Une relation inverse ne fait pas que rendre les choses plus propres, c'est en fait utilisé par Core Data pour gérer les données intégrité.

- Cocoa Dev Central

Vous devriez typiquement modeler relations dans les deux sens, et spécifier les relations inverses de manière appropriée. Core Data utilise ceci informations pour assurer la cohérence du graphe d'objets si un changement est made (voir “Manipulation des relations et intégrité des graphes d’objets”). Pour un discussion de certaines des raisons pour lesquelles vous voudrez peut-être ne pas modéliser un relation dans les deux sens, et certains des problèmes qui pourraient survenir sinon, voir “Unidirectionnel Des relations."

- Guide de programmation de base de données

111
Matthew Schinckel

La documentation Apple contient un bon exemple qui suggère une situation dans laquelle vous pourriez avoir des problèmes en n’ayant pas de relation inverse. Mappons-le dans cette affaire.

Supposons que vous l’ayez modélisé comme suit: enter image description here

Notez que vous avez une relation à-un appelée "type", de SocialApp à SocialAppType. La relation est non facultative et comporte une règle de suppression "refuser".

Considérons maintenant ce qui suit:

SocialApp *socialApp;
SocialAppType *appType;
// assume entity instances correctly instantiated

[socialApp setSocialAppType:appType];
[managedObjectContext deleteObject:appType];
BOOL saved = [managedObjectContext save:&error];

Nous nous attendons à ce que cette sauvegarde de contexte échoue, car nous avons défini la règle de suppression sur Deny pendant que la relation est non facultative. 

Mais ici la sauvegarde réussit.

La raison en est que nous n'avons pas défini de relation inverse. De ce fait, l'instance socialApp n'est pas marquée comme modifiée lorsque appType est supprimé. Donc, aucune validation ne se produit pour socialApp avant la sauvegarde (cela suppose qu'aucune validation n'est nécessaire car aucun changement ne s'est produit). Mais en réalité, un changement s’est produit. Mais cela ne se reflète pas. 

Si nous rappelons appType par

SocialAppType *appType = [socialApp socialAppType];

appType est nul. 

Bizarre, n'est ce pas? Nous obtenons nil pour un attribut non optionnel?

Vous n’avez donc aucun problème si vous avez configuré la relation inverse ..__ Sinon, vous devez forcer la validation en écrivant le code comme suit.

SocialApp *socialApp;
SocialAppType *appType;
// assume entity instances correctly instantiated

[socialApp setSocialAppType:appType];
[managedObjectContext deleteObject:appType];

[socialApp setValue:nil forKey:@"socialAppType"]
BOOL saved = [managedObjectContext save:&error];
127
MadNik

Je vais paraphraser la réponse définitive trouvée dans Développement sur iPhone 3 de Dave Mark et Jeff LeMarche.

Apple recommande généralement de toujours créer et spécifier l'inverse, même si vous n'utilisez pas la relation inverse dans votre application. Pour cette raison, il vous avertit lorsque vous ne parvenez pas à fournir l'inverse. 

Les relations ne sont pas obligatoires pour avoir une inverse, car il existe quelques scénarios dans lesquels la relation inverse pourrait nuire aux performances. Par exemple, supposons que la relation inverse contienne un très grand nombre d'objets. Pour supprimer l'inverse, vous devez effectuer une itération sur l'ensemble représentant l'inverse, ce qui affaiblit les performances.

Mais sauf si vous avez une raison particulière de ne pas le faire, modélisez l'inverse . Cela aide les données de base à assurer l'intégrité des données. Si vous rencontrez des problèmes de performances, il est relativement facile de supprimer la relation inverse ultérieurement.

46
Rose Perrone

La meilleure question est, "y at-il une raison pas pour avoir un inverse"? Les données de base sont vraiment un cadre de gestion de graphe d'objet, pas un cadre de persistance. En d'autres termes, son travail consiste à gérer les relations entre les objets du graphe d'objets. Les relations inverses rendent ceci beaucoup plus facile. Pour cette raison, Core Data attend des relations inverses et est écrit pour ce cas d'utilisation. Sans eux, vous devrez gérer vous-même la cohérence du graphe d'objets. En particulier, les relations entre plusieurs personnes sans relation inverse risquent fort d'être corrompues par Core Data à moins que vous ne travailliez très / ne réussissions pas à faire fonctionner les choses. Le coût en termes de taille de disque pour les relations inverses est vraiment insignifiant par rapport aux avantages qu’il vous procure.

24
Barry Wark

Il existe au moins un scénario dans lequel une relation de données centrale sans inversion peut être présentée: lorsqu'il existe déjà une autre relation de données centrale entre les deux objets, celle-ci gérera la maintenance du graphe d'objets.

Par exemple, un livre contient plusieurs pages, alors qu'une page est dans un livre. C'est une relation bidirectionnelle plusieurs-à-un. La suppression d'une page annule simplement la relation, tandis que la suppression d'un livre supprime également la page.

Cependant, vous pouvez également souhaiter suivre la page en cours de lecture pour chaque livre. Cela pourrait être fait avec un "currentPage" property sur Page, mais vous aurez alors besoin d'une autre logique pour vous assurer qu'une seule page du livre est marquée à tout moment comme la page actuelle. Si vous créez plutôt une currentPage relationship de Book sur une seule page, vous vous assurerez qu’il n’y aura toujours qu’une seule page active sélectionnée. De plus, cette page est facilement accessible avec une référence au livre simplement book.currentPage.

Graphical presentation of core data relationship between books and pages

Quelle serait la relation réciproque dans ce cas? Quelque chose en grande partie absurde. "myBook" ou similaire peut être ajouté dans l'autre sens, mais il ne contient que les informations déjà contenues dans la relation "livre" de la page, et crée donc ses propres risques. Peut-être qu'à l'avenir, la façon dont vous utiliserez l'une de ces relations sera modifiée, ce qui modifiera la configuration de vos données de base. Si page.myBook a été utilisé à certains endroits où page.book aurait dû être utilisé dans le code, il pourrait y avoir des problèmes. Une autre façon d'éviter ceci de manière proactive consiste également à ne pas exposer myBook dans la sous-classe NSManagedObject utilisée pour accéder à la page. Cependant, on peut faire valoir qu'il est plus simple de ne pas modéliser l'inverse en premier lieu.

Dans l'exemple présenté, la règle de suppression pour la relation currentPage doit être définie sur "Aucune action" ou "Cascade", car il n'existe aucune relation réciproque avec "Nullify". (Cascade implique que vous extrayez chaque page du livre au fur et à mesure que vous le lisez, mais cela pourrait être vrai si vous êtes particulièrement froid et avez besoin de carburant.)

Lorsqu'il est possible de démontrer que l'intégrité du graphe d'objet n'est pas menacée, comme dans cet exemple, et que la complexité et la maintenabilité du code sont améliorées, on peut arguer du fait qu'une relation sans inverse peut être la bonne décision.

19
Duncan Babbage

Bien que les documents ne semblent pas nécessiter d'inverse, je viens de résoudre un scénario qui aboutissait en fait à une "perte de données" en ne disposant pas d'inverse. J'ai un objet de rapport qui a une relation à plusieurs sur les objets à signaler. Sans la relation inverse, toute modification de la relation entre plusieurs personnes a été perdue lors de la relance. Après avoir examiné le débogage Core Data, il est apparu que, même si j’enregistrais l’objet de rapport, les mises à jour du graphe d’objets (relations) n’étaient jamais effectuées. J'ai ajouté un inverse, même si je ne l'utilise pas, et le tour est joué, cela fonctionne. Donc, cela ne signifie peut-être pas que c'est nécessaire, mais des relations sans inverses peuvent avoir des effets secondaires étranges.

4
greg

Les inverses sont également utilisés pour Object Integrity (pour d'autres raisons, voir les autres réponses):

L'approche recommandée consiste à modéliser les relations dans les deux sens et spécifiez les relations inverses de manière appropriée. Utilisation des données de base cette information pour assurer la cohérence du graphe d'objet si un le changement est fait

De: https://developer.Apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/HowManagedObjectsarerelated.html#//Apple_ref/doc/uid/TP40001075-CH17-SW1

Le lien fourni vous donne des idées pour lesquelles vous devriez avoir un ensemble inverse. Sans cela, vous pouvez perdre des données/intégrité. En outre, la probabilité que vous accédiez à un objet qui est nil est plus probable.

0
J. Doe