1

J'ai essayé tout ce que je peux penser ici et je suis juste coincé. J'ai un User qui a beaucoup Invitations mais lorsque je tente d'ajouter un Invitation au champ invitations du User en appelant AddInvitation() une exception NotSupported est lancé parce que le Collection is read-only. J'ai l'impression que c'est quelque chose dans les remplacements de mappage, mais j'ai d'autres classes/relations suivant le même modèle et (jusqu'à présent) je ne reçois pas ce problème ailleurs. Pourquoi le champ de sauvegarde est-il en lecture seule et que dois-je faire pour éviter que le champ de sauvegarde devienne en lecture seule?Comment configurer ma collection en lecture seule avec FluentNhibernate?

User.cs

public class User 
{ 
    private IList<Invitation> invitations = new List<Invitation>(); 

    public virtual IEnumerable<Invitation> Invitations 
    { 
    get { return new ReadOnlyCollection<Invitation>(this.invitations); 
    } 

    public virtual User AddInvitation(Invitation invitation) 
    { 
    if (!this.invitations.Contains(invitation)) 
    { 
     this.invitations.Add(invitation); // <-- throws System.NotSupportedException: Collection is read-only. 
    } 

    return this; 
    } 
} 

Invitation.cs

public class Invitation 
{ 
    private User sender = null; 

    public virtual User Sender 
    { 
    get { return this.sender; } 
    } 

    public virtual Invitation From(User sender) 
    { 
    this.sender = sender; 
    return this; 
    } 
} 

Database.cs

public class Database 
{ 
    // code removed for brevity 
    private static void MapAssemblies(FluentNHibernate.Cfg.MappingConfiguration config) 
    { 
    config.AutoMappings.Add(
     AutoMap.Assembly(Assembly.GetAssembly(typeof(User))) 
     .Override<User>(userMapping => 
     { 
     userMapping.HasMany<Invitation>(userType => userType.Invitations) 
     .Not.LazyLoad() 
     .Inverse() 
     .Cascade.SaveUpdate() 
     .KeyColumn("User_id"); 
     }) 
     .Override<Invitation>(invitationMapping => 
     { 
     invitationMapping.References<User>(invitationType => invitationType.Sender) 
     .Column("User_id"); 
     }); 
    ); 
    } 
    // code removed for brevity 
} 

Manager.cs

public class Manager 
{ 
    private ISession session = null; // initialised in .ctor 

    // code removed for brevity 
    public void AddInvitation(/*parameters removed*/) 
    { 
    User user = this.session.Get<User>(identifier); 
    Invitation invitation = new Invitation().From(user); 
    user.AddInvitation(invitation); 
    using (ITransaction transaction = this.session.BeginTransaction()) 
    { 
     this.session.SaveOrUpdate(invitation); 
     transaction.Commit(); 
    } 
    } 
    // code removed for brevity 
} 

J'ai le message d'exception réelle (aurait ajouté ce départ)

à System.ThrowHelper.ThrowNotSupportedException (ressource ExceptionResource) à System.Collections.ObjectModel.ReadOnlyCollection'1.System.Collections.Generic.ICollection .add (valeur T) à NHibernate.Collection.Generic.PersistentGenericBag'1.System.Collections.Generic.ICollection.Add (point T)

Il semble que même si je modifier les invitations de terrain de l'emballage ReadOnlyCollection ajouté par l'accesseur Invitations modifie le champ lui-même.

Répondre

1

Je pense que le problème provient de ReadOnlyCollection, y a-t-il une raison pour laquelle vous initialisez des invitations en tant que collection en lecture seule?

Dans votre mappage, vous remplissez des données dans la collection en lecture seule Invitation et peut-être votre champ d'initialisation de l'invitation en tant que liste est masqué par la collection en lecture seule.

Essayez peut-être de ne pas réinitialiser la collection d'invitations chaque fois que vous voulez accéder, mais renvoyez simplement le champ des invitations, vous pouvez rendre ce champ en lecture seule.

+0

La raison pour exposer publiquement les invitations en lecture seule est telle qu'elle ne peut être modifiée par aucun code client - le code client DOIT appeler AddInvitation. Lorsque le getter pour Invitations est appelé, il ne se réinitialise pas, le nouveau ReadOnlyCollection (IList éléments) ne devrait mettre un wrapper autour des éléments bien que je pense que dans ce cas les éléments sont en cours de modification ... –

+0

OK, ce problème est Certainement jusqu'à l'encapsuleur ReadOnlyCollection, s'il est supprimé, la collection n'est plus en lecture seule. –

+0

La plupart des implémentations de la bibliothèque C# semblent générer un nouveau tableau et créer une copie des données lorsque l'utilisateur le demande. Cela permet au code client d'être aussi destructeur qu'il le souhaite sans compromettre les données d'origine. Même si vous utilisez un ReadOnlyCollection, j'ai entendu dire qu'il y avait des façons sournoises de le rendre mutable - il devrait être utilisé pour montrer l'intention plus que la sécurité. –

0

Vieille question, problème réel! Et ce n'est pas difficile de fixer tout en gardant la collection en lecture seule:

userMapping.HasMany<Invitation>(userType => userType.Invitations) 
    .Not.LazyLoad() 
    .Inverse() 
    .Access.CamelCaseField() // this does the trick! 
    .Cascade.SaveUpdate() 
    .KeyColumn("User_id"); 

Si NHibernate accède à la collection à travers le champ de support, il n'a aucune idée que c'est en lecture seule, il va instancier une collection en lecture-écriture. L'interface publique reste en lecture seule.

Questions connexes