KellyTheDev
KellyTheDev

Reputation: 901

I'm sorry, but it's another: Found shared references to a collection

Recently I've been adding some features to my local project, and I'm struggling with this one part. The short of it is NHibernate give me the line:

Found shared references to a collection: Page.Menus

The simple part of it is, I only want to save the relational map that binds menus to pages, which you can see the Reference below in PageMap. I should add that loading data works great, it's the saving that's killing me.

I spent a lot of time yesterday digging through here, and the good ole web, trying to find the answer, and I just kept striking out. Maybe it's bad searching on my part, but I feel I tried everything. If you know where it is can you please supply it? (thanks)

For the actual details, I've tried to simplify what's going on. I've added the PageReposity, UnitOfWork, and the proxy objects, as well as their mappings.

Where I get hazy on is the cascade, and how to save the relationship table (many to many)

For the first part, here is what happens when I save (Add). I've actually done this a few ways within the PageRepository. Since I'm strugging with the Add(), I've included it here:

public override bool Add(Page entity)
{
    UnitOfWork.Save(entity);

    /* I have also tried doing the following below, which doesn't help 
    for (var index = 0; index < entity.Menus.Count; index++)
    {
        UnitOfWork.Save(entity.Menus[index]);
    }
    */

    UnitOfWork.Commit(); // bam, error!
    return true;
} 

In the UnitOfWork I've setup the following in the ctor (the ISession is injected each time via ninject like so:

// DomainModule ...

Bind<ISFactory>().To<NHibernateSessionFactory>()
            .InSingletonScope()
            .WithConstructorArgument("connectionString", _connectWeb);
...

// Back to the UnitOfWork

...

private ISession Session { get; set; }

...

public UnitOfWork(ISFactory sessionFactory)
{
     _sessionFactory = sessionFactory.GetSessionFactory();

     Session = _sessionFactory.OpenSession();
     Session.FlushMode = FlushMode.Never; // I have also tried FlushMode.Commit
     _transaction = Session.BeginTransaction(IsolationLevel.ReadCommitted);
} 

...

public void Save(object obj)
{
    Session.Save(obj);
}

...

public void Commit()
{
    if (!_transaction.IsActive)
    {
        throw new InvalidOperationException("Oops! We don't have an active transaction");
    }

    try
    {
        _transaction.Commit();
        Session.Flush(); // I did this FlushMode.Never was set
    }
    catch (Exception exception)
    {
        _transaction.Rollback();
        throw;
    }
}

I've got 3 classes here:

Page, Menu, and Link.

public class Page : IEntity<int>
{
    public virtual int Id { get; set; }

    public virtual IList<Menu> Menus { get; set; }
}

public class Menu : IEntity<int>
{
    public virtual int Id { get; set; }

    public virtual IList<Link> Links { get; set; }
}

public class Link : IEntity<int>
{
    public virtual int Id { get; set; }

    public virtual DateTime CreatedDate { get; set; }

    public virtual string Url { get; set; }
}

Then I also have a Mappings:

public class PageMap : ClassMap<Page>
{
    public PageMap()
    {
        Id(x => x.Id).GeneratedBy.Native();
        HasManyToMany(x => x.Menus)
                        .Table("MenuToPage")
                        .ParentKeyColumn("FkPageId")
                        .ChildKeyColumn("FkMenuId").Cascade.SaveUpdate(); // the cascade is new here just trying to see if it helps
    }
}

public class MenuMap : ClassMap<Menu>
{
    public MenuMap()
    {
        Id(x => x.Id); // I had .GeneratedBy.Native(); attached here too.
        HasManyToMany(x => x.Links)
            .Table("MenuToLinks")
            .ChildKeyColumn("FkLinksId")
            .ParentKeyColumn("FkMenuId")
            .OrderBy("MenuOrder ASC")
            .Not.LazyLoad()
            .Cascade.None(); // the cascade is new here just trying to see if it helps
    }
}

public class LinkMap : ClassMap<Link>
{
    public LinkMap()
    {
        Id(x => x.Id).GeneratedBy.Native();
        Map(x => x.Url);
        Map(x => x.CreatedDate);
        Map(x => x.ModifiedDate);
        References(x => x.MetaData, "FkMetaDataId").Not.Nullable().Not.LazyLoad();
    }
}

Can anyone help me or point me in a direction, I'd really appreciate your help.

Like always thank you,

Kelly

Upvotes: 3

Views: 2668

Answers (1)

MichaC
MichaC

Reputation: 13381

Unfortunately you have posted everything but the construction of your objects before you safe them.

Usually this error can occur if you assign the same collection of entities to different instances. For example (pseudo code)

var menuList = new List<Menu>();...

pageA.Menus = menuList;
pageB.Menus = menuList;

This would set the reference of menuList to both, pageA.Menus and pageB.Menus.

Instead, assign all items of menuList to each page with pageA.AddRange(menuList) or a loop or whatever...

Upvotes: 6

Related Questions