0

I have the fallowing entities:

Person Company

And then, there is a entity PersonCompany which makes a Relation between Person and Company

Mapping for Person is:

public class PersonMap : SubclassMap<Person> {
    public PersonMap() {
        this.Table("Person");
        this.KeyColumn("Id");
        this.HasMany(x => x.PersonCompanys).AsSet().KeyColumn("PersonId").Fetch.Select().Inverse().Cascade.Delete();
    }
}

Company:

public class CompanyMap : SubclassMap<Company> {
    public CompanyMap() {
        this.Table("Company");
        this.KeyColumn("Id");
        this.HasMany(x => x.PersonCompanys).AsSet().KeyColumn("CompanyId").Fetch.Select().Inverse().Cascade.None();
    }
}

and PersonCompany:

public class PersonCompanyMap : ClassMap<PersonCompany> {
    public PersonCompanyMap() {
        this.Table("PersonCompany");

        this.Version(x => x.ObjectVersion);
        this.Id(x => x.Id).GeneratedBy.Assigned();
        this.References(x => x.Company).Column("CompanyId").Fetch.Select();
        this.References(x => x.Person).Column("PersonId").Fetch.Select();
    }
}

SecondLevel-Caching is activated general and for each Entity with Conventions. Also the Caching for the PersonCompanys-Collection:

public class CachableConventions : IClassConvention, IHasManyConvention, IHasManyToManyConvention {
    private static readonly Type[] notCachableTypes = new[] {typeof(Word), typeof(ActivityWord), typeof(DbVersion), typeof(Deleted), typeof(ExchangeSyncState), typeof(Synchronizer), typeof(SerialletterField)};

    public void Apply(IClassInstance instance) {
        if (Array.IndexOf(notCachableTypes, instance.EntityType) >= 0) {
            return;
        }
        instance.Cache.ReadWrite();
    }

    public void Apply(IManyToManyCollectionInstance instance) {
        if (Array.IndexOf(notCachableTypes, instance.ChildType) >= 0) {
            return;
        }
        instance.Cache.ReadWrite();
    }
    public void Apply(IOneToManyCollectionInstance instance) {
        if (Array.IndexOf(notCachableTypes, instance.ChildType) >= 0) {
            return;
        }
        instance.Cache.ReadWrite();
    }
}

Now, I create a new Person and after save a new Company and then I add a PersonCompany-Record to the PersonCompanies-Collection.

When I after select the created Person, the PersonCompanys-Collection will be empty. When I do the same without SecondLevel-Caching, the PersonCompanys-Collection contains the created Entity.

Does someone know, what can going wrong here?

Edit - Additional Informations:

This steps are causing the problem:

  1. create a new Person with Session.Save(person)
  2. load the person with person = Session.Get(person.Id)
  3. get personCompany from the Person with personCompany = person.PersonCompany.FirstOrDefault() (this will be empty)
  4. Create a new PersonCompany-Entry with Session.Save(new PersonCompany(Guid.NewGuid()) {Person = person}))
  5. load the person with person = Session.Get(person.Id)
  6. now, person.PersonCompany returns an empty list.

When I do not step 3, all is working great.

BennoDual
  • 5,865
  • 15
  • 67
  • 153
  • My gut feeling is this has little to nothing to do with your caching. Without seeing what you are doing during creation of the Person, Company, and PersonCompany objects I can't help. – Matthew Brubaker Sep 13 '12 at 17:38
  • When I deactivate the secondlevelcaching, it is working correct. Perhaps the problem is how I am associate the Person to the new PersonCompany. I do personCompany.Person = person and the call Session.Insert(personCompany) – BennoDual Sep 13 '12 at 17:44
  • First of all, unless you have a really good reason to use Session.Insert, you should be using Session.SaveOrUpdate(...). If I remember correctly, Session.Insert avoids a lot of the internal caching. – Matthew Brubaker Sep 13 '12 at 17:48
  • ah sorry - my factory-method is insert which calls Session.Save() for create the new PersonCompany - and before it calls Session.Merge(person) for the person (the Person is not new). This are two different Sessions. – BennoDual Sep 13 '12 at 17:57
  • In that case, it sounds like the two sessions aren't syncing their caches. Unfortunately, this might fall outside of my expertise. One thing you can try would be to ensure your session opens and closes are lining up. Open session 1, create Person 1, close session 1, open session 2, create Company 1, close session 2, open session 3, create PersonCompany (and associate Person 1 and Company 1 to it), close session 3, open session 4, select Person 1 (should have correct associated PersonCompany), close session 4. – Matthew Brubaker Sep 13 '12 at 18:04
  • any reason you mapped Person company as its own class instead of a manytomany to Company in Person? – Firo Sep 14 '12 at 06:17
  • @Firo - Yes, because I have add additional Information to PersonCompany – BennoDual Sep 14 '12 at 09:13
  • @All - I have found the steps, how to reproduce the problem and which is causing the problem (edited in the question). Perhaps someone can give an explanation/help about that. Thanks. – BennoDual Sep 14 '12 at 09:22

1 Answers1

0
  1. see obove
  2. see obove
  3. get personCompany from the Person with personCompany = person.PersonCompany.FirstOrDefault() (this will be empty)

    this will initialise the collection to an empty collection

  4. Create a new PersonCompany-Entry with Session.Save(new PersonCompany(Guid.NewGuid()) {Person = person}))

    will create a PersonCompany record in the database

  5. load the person with person = Session.Get(person.Id)

    will just return the cached person object, so this is basicly "do nothing"

  6. now, person.PersonCompany returns an empty list

since the collection was initialised to en empty collection in step 3 it will return just that. nhibernate will not issue another select to find out if something changed. if it would it would probably discard local changes or produce conflicts and it would not be very efficient

do

  • session.Clear(); as step 3b or
  • session.Refresh(person); as step 5

then it is working as expected

Firo
  • 30,626
  • 4
  • 55
  • 94
  • When I do session.Refresh(person) as Step 5, it will work. I ask me, why is this needed, because Session is always a new opened Session. Shouldn't NHibernate-Second-Level Cache do this for me? - When I save a new PersonCompany then add this to PersonCompanies-List of cached Persons? When not, Is there a general way, to solve this problem? – BennoDual Sep 15 '12 at 08:39
  • the second lvl cache will not magically update your objects because maybe it is not what you want or there are race conditions and the like. Also it would be impossible to know where to insert into the collection when the collection is mapped with "order by", "sql", "formula" and the like. A publish-subscribe service in the application notifying everything to refresh their persons would be the best IMO – Firo Sep 17 '12 at 04:22