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.
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 ... –
OK, ce problème est Certainement jusqu'à l'encapsuleur ReadOnlyCollection, s'il est supprimé, la collection n'est plus en lecture seule. –
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é. –