0

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 Answers2

1

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.

David Osborne
  • 6,436
  • 1
  • 21
  • 35
  • Up vote for always being handy. Agree with you. It will work if he set ONLY `id` of `Student` instead of setting entire Entity. Correct me if I am wrong. – Amit Joshi Dec 14 '16 at 16:35
  • Sure. I didn't think of the case where `Student` is not null but transient. In that case, there's a cascade problem. – David Osborne Dec 14 '16 at 16:46
  • It looks like [this](http://i.imgur.com/deJNCJr.png). There is no null. But when I want to add a Student without DrivingLicense there is null in DrivingLicense property of Student and no error during persisting. So there is a cascade problem? But how to fix it? – Marek Kamiński Dec 14 '16 at 17:59
  • Looks like a cascade must be added to the driving license mapping for the student property because, although the student is not null, its id is 0. Be careful with your class design though as you appear to have a circular reference that might give you some grief. – David Osborne Dec 14 '16 at 20:36
  • Thank you David for your help. With your convenient tips I managed to find a way to solve my problem. The key to the solution was change Reference-to-Reference to HasOne-to-Reference – Marek Kamiński Dec 16 '16 at 17:52
0

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);