Question

I've tried everything I can think of here and I'm just stuck. I have a User that has many Invitations but when I try to add an Invitation to the invitations field of the User by calling AddInvitation() a NotSupported exception is thrown because the Collection is read-only. I get the feeling that it's something in the mapping overrides but I have various other classes / relationships following the same pattern and (so far) I don't get this issue elsewhere. Why is the backing field read-only and what should I be doing to prevent the backing field becoming read-only?

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
}

I've got the actual exception message (should have added this initially)

at System.ThrowHelper.ThrowNotSupportedException(ExceptionResource resource) at System.Collections.ObjectModel.ReadOnlyCollection'1.System.Collections.Generic.ICollection.Add(T value) at NHibernate.Collection.Generic.PersistentGenericBag'1.System.Collections.Generic.ICollection.Add(T item)

It seems that even though I'm modifying the field invitations the ReadOnlyCollection wrapper added by the accessor Invitations is modifying the field itself.

Était-ce utile?

La solution

I think that the problem is coming from ReadOnlyCollection, is there any reasony why you are initializing Invitations as read-only collection?

In your mapping your are populating data into Invitation read-only colleciton and maybe your initialization of invitation field as List is hide by read-only collection.

Maybe try to not re-initialize collection of invitations every time when you want to access, but just return invitations field, you can make this field as read-only.

Autres conseils

Old question, actual problem! And it's not that hard to fix while keeping the collection read-only:

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

If NHibernate accesses the collection through the backing field, it has no idea that it's readonly, so it will instantiate a read-write collection. The public interface remains read-only.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top