not-null property references a null or transient value - understanding of Inverse and Cascade

1.9k views Asked by At

This is fragment of my database project, and this is fragment of auto generated DB by Fluent NHibernate

And these are my entities and mapping classes in Fluent NHibernate of above DB project.

Entities:

public class BaseEntity<T> where T : BaseEntity<T> {
    public virtual int Id { get; set; }
...
}

public class Person<T> : BaseEntity<T> where T : BaseEntity<T> {
    public virtual string FirstName { get; set; }
    public virtual string SecondName { get; set; }
    public virtual string LastName { get; set; }
    public virtual string PESEL { get; set; }
    public virtual DateTime BirthDate { get; set; }
    public virtual string City { get; set; }
    public virtual string PostalCode { get; set; }
    public virtual string Street { get; set; }
    public virtual string HouseNr { get; set; }
    public virtual string ApartmentNr { get; set; }
    public virtual string PhoneNr { get; set; }
    public virtual string Email { get; set; }
}

public class DrivingLicense : BaseEntity<DrivingLicense> {
    #region Relations
    public virtual IList<DrivingLicensePermissions> DrivingLicensePermissions { get; set; }
    public virtual Student Student { get; set; }
    #endregion

    public virtual DateTime IssueDate { get; set; }
    public virtual string DrivingLicenseNr { get; set; }
}

public class DrivingLicensePermissions : BaseEntity<DrivingLicensePermissions> {
    #region Relations
    public virtual DrivingLicense DrivingLicense { get; set; }
    #endregion

    public virtual DrivingLicenseCategory Category { get; set; }
}

Mappings:

class StudentMap : ClassMap<Student> {
    public StudentMap() {
        Id(x => x.Id);
        Map(x => x.FirstName).Not.Nullable().Length(25);
        Map(x => x.SecondName).Nullable().Length(25);
        Map(x => x.LastName).Not.Nullable().Length(50);
        Map(x => x.PESEL).Nullable().Length(11);
        Map(x => x.BirthDate).Not.Nullable();
        Map(x => x.City).Not.Nullable().Length(50);
        Map(x => x.PostalCode).Nullable().Length(6);
        Map(x => x.Street).Not.Nullable().Length(50);
        Map(x => x.HouseNr).Not.Nullable().Length(10);
        Map(x => x.ApartmentNr).Nullable().Length(10);
        Map(x => x.PhoneNr).Nullable().Length(20);
        Map(x => x.Email).Nullable().Length(100);

        References(x => x.DrivingLicense).Nullable().Cascade.All();
        References(x => x.User).Nullable().Cascade.All();
        HasMany(x => x.Participants).Cascade.All();
    }
}

class DrivingLicenseMap : ClassMap<DrivingLicense> {
    public DrivingLicenseMap() {
        Id(x => x.Id);
        Map(x => x.IssueDate).Not.Nullable();
        Map(x => x.DrivingLicenseNr).Not.Nullable().Length(20);

        HasMany(x => x.DrivingLicensePermissions).Cascade.All();
        References(x => x.Student).Not.Nullable();
    }
}

class DrivingLicensePermissionsMap : ClassMap<DrivingLicensePermissions> {
    public DrivingLicensePermissionsMap() {
        Id(x => x.Id);
        Map(x => x.Category).Not.Nullable();

        References(x => x.DrivingLicense).Not.Nullable();
    }
}

And my problem is this exception: not-null property references a null or transient value Model.Entities.DrivingLicense.Student, while I'm trying to persist Student object like this

session.Save(student);

which has assigned DrivingLicense object to it's DrivingLicense property.

I assume it's caused because of bad mapping - wrong Cascade or lack of Inverse. I tried many combinations and can't get it work. Also is it correct that Student table has DrivingLicense_id and the opposite, DrivingLicense has Student_id column?!

2

There are 2 answers

0
Marek Kamiński On BEST ANSWER

To solve my problem I had to change Reference-to-Reference to HasOne-to-Reference.

Now it looks like this.

Entities have not changed

Mappings:

class StudentMap : ClassMap<Student> {
    public StudentMap() {
        Id(x => x.Id);
        Map(x => x.FirstName).Not.Nullable().Length(25);
        Map(x => x.SecondName).Nullable().Length(25);
        Map(x => x.LastName).Not.Nullable().Length(50);
        Map(x => x.PESEL).Nullable().Length(11);
        Map(x => x.BirthDate).Not.Nullable();
        Map(x => x.City).Not.Nullable().Length(50);
        Map(x => x.PostalCode).Nullable().Length(6);
        Map(x => x.Street).Not.Nullable().Length(50);
        Map(x => x.HouseNr).Not.Nullable().Length(10);
        Map(x => x.ApartmentNr).Nullable().Length(10);
        Map(x => x.PhoneNr).Nullable().Length(20);
        Map(x => x.Email).Nullable().Length(100);

        HasOne(x => x.DrivingLicense).PropertyRef(x => x.Student).Cascade.All();
        References(x => x.User).Unique().Not.Nullable();
        HasMany(x => x.Participants).Cascade.All();
    }
}

class DrivingLicenseMap : ClassMap<DrivingLicense> {
    public DrivingLicenseMap() {
        Id(x => x.Id);
        Map(x => x.IssueDate).Not.Nullable();
        Map(x => x.DrivingLicenseNr).Not.Nullable().Length(20);

        HasMany(x => x.DrivingLicensePermissions).Cascade.All();
        References(x => x.Student).Unique().Not.Nullable();
    }
}

class DrivingLicensePermissionsMap : ClassMap<DrivingLicensePermissions> {
    public DrivingLicensePermissionsMap() {
        Id(x => x.Id);
        Map(x => x.Category).Not.Nullable();

        References(x => x.DrivingLicense).Nullable();
    }
}

Thank to this change, circular reference was gone. see it here

There is no DrivingLicense_id in Students table and now I'm able to persist Student entity along with DrivingLicense and it's permissions just by saving student like I did it before.

session.Save(student);
5
David Osborne On

The key to this problem is that DrivingLicense references a Student via a Not.Nullable() mapping. This means that when NH tries to persist a DrivingLicense, it's Student property must not be null.

Debug the code and pause at the Save(student) call. Check the object graph, looking at student's DrivingLicense property and its Student property. My guess is that it's null.

You'll need to review the Not.Nullable() aspect of your mapping or ensure your object graph is correctly 'wired' before persisting.