Wierd issue with not null fields.

Maxus's Avatar

Maxus

27 Oct, 2010 03:39 AM via web

Hi Fluent People,

I have a really wierd error, not sure at all whats causing it, could be me :)

using NH 3.0 Aplha 1, Fluent NH build 1.1.0.695, nh Validator 1.2.0.GA, In memory DB.

When I save the user, I recieve a NHibernate.PropertyValueException : not-null property references a null or transient value User.Account

if I remove the .Not.Nullable() from the user mapping it works fine, but the problem is the account is being set, so the null error should never be thrown (as proven by the NH output when it is disabled at the bottom), Any ideas?

entities:

public abstract class DomainEntity
{
    public virtual Guid Id { get; private set; }
    public virtual bool IsTransient
    {
        get { return Id == Guid.Empty; }
    }
    public virtual DateTime UpdatedAt { get; set; }
    public virtual string UpdatedBy { get; set; }
    public virtual DateTime CreatedAt { get; set; }
    public virtual string CreatedBy { get; set; }
    public virtual DateTime Version { get; set; }

    public override bool Equals(object obj)
    {
        var other = obj as DomainEntity;

        if (other == null) { return false; }

        return other.Id == Id;
    }

    public override int GetHashCode()
    {
        return Id.GetHashCode();
    }
}


public class Account : DomainEntity
{
    public virtual string Name { get; set; }

    public Account()
    {
        Users = new List<User>();
    }

    public virtual void AddUser(User user)
    {
        user.AddAccount(this);
    }

    public virtual void RemoveUser(User user)
    {
        user.RemoveAccount();
    }
}

public class User : DomainEntity
{
    public virtual string Login { get; set; }

    public User(){}

    public virtual void AddAccount(Account account)
    {
        Account = account;
        account.Users.Add(this);
    }

    public virtual void RemoveAccount()
    {
        Account.Users.Remove(this);
        Account = null;
    }
}

Mappings:

public class DomainEntityMapping<TDomainEntity> : ClassMap<TDomainEntity> where TDomainEntity : DomainEntity
{
    public DomainEntityMapping()
    {
        Id(x => x.Id);
        Map(x => x.CreatedAt).Column("created_at");
        Map(x => x.CreatedBy).Length(50).Column("created_by");
        Map(x => x.UpdatedAt).Column("updated_at");
        Map(x => x.UpdatedBy).Length(50).Column("updated_by");
    }
}

public class AccountMapping : DomainEntityMapping<Account>
{
    public AccountMapping()
    {
        Map(x => x.Name).Length(50);
        HasMany(x => x.Users).Cascade.All().Inverse();
    }
}

public class UserMapping : DomainEntityMapping<User>
{
    public UserMapping()
    {
        Map(x => x.Login).Length(50).Unique();
        References(x => x.Account).Index("user_account_id_idx").Cascade.All().Not.Nullable(); // <- Problem here with the Not.Nullable();
    }
}

Test:

    [Test]
    public void Can_Correctly_Map_Account()
    {
        var account = DataHelper.GenerateFakeAccount();
        var users = new List<User>
                        {
                            new User { Login = "LoginPerson1", CreatedAt = DateTime.Parse("2011/10/01 23:12:00"), CreatedBy = "Me!", UpdatedAt = DateTime.Parse("2011/10/01 23:12:00"), UpdatedBy = "Me2" },
                            new User { Login = "LoginPerson2", CreatedAt = DateTime.Parse("2011/10/01 23:12:00"), CreatedBy = "Me!", UpdatedAt = DateTime.Parse("2011/10/01 23:12:00"), UpdatedBy = "Me2" },
                            new User { Login = "LoginPerson3", CreatedAt = DateTime.Parse("2011/10/01 23:12:00"), CreatedBy = "Me!", UpdatedAt = DateTime.Parse("2011/10/01 23:12:00"), UpdatedBy = "Me2" }
                        };


        new PersistenceSpecification<Account>(Session)
            .CheckProperty(c => c.Name, "Name")
            .CheckProperty(c => c.CreatedAt, DateTime.Parse("2010/02/01"))
            .CheckProperty(c => c.CreatedBy, "CreatedBy")
            .CheckProperty(c => c.UpdatedAt, DateTime.Parse("2010/02/02"))
            .CheckProperty(c => c.UpdatedBy, "UpdatedBy")
            .CheckList(c => c.Users, users, (a, u) => a.AddUser(u))
            .VerifyTheMappings();

    }

The NH output if I disable the .Not.Nullable() on the user to parent relationship:

NHibernate: INSERT INTO users (created_at, created_by, updated_at, updated_by, login, id) VALUES (@p0, @p1, @p2, @p3, @p4, @p5);@p0 = 1/10/2011 11:12:00 PM [Type: DateTime (0)], @p1 = 'Me!' [Type: String (0)], @p2 = 1/10/2011 11:12:00 PM [Type: DateTime (0)], @p3 = 'Me2' [Type: String (0)], @p4 = 'LoginPerson1' [Type: String (0)], @p5 = 314bd4ac-d11d-458c-9088-9e1c00bef03b [Type: Guid (0)]
NHibernate: INSERT INTO users (created_at, created_by, updated_at, updated_by, login, id) VALUES (@p0, @p1, @p2, @p3, @p4, @p5);@p0 = 1/10/2011 11:12:00 PM [Type: DateTime (0)], @p1 = 'Me!' [Type: String (0)], @p2 = 1/10/2011 11:12:00 PM [Type: DateTime (0)], @p3 = 'Me2' [Type: String (0)], @p4 = 'LoginPerson2' [Type: String (0)], @p5 = 91536f22-a53b-47d7-a603-9e1c00bef0e9 [Type: Guid (0)]
NHibernate: INSERT INTO users (created_at, created_by, updated_at, updated_by, login, id) VALUES (@p0, @p1, @p2, @p3, @p4, @p5);@p0 = 1/10/2011 11:12:00 PM [Type: DateTime (0)], @p1 = 'Me!' [Type: String (0)], @p2 = 1/10/2011 11:12:00 PM [Type: DateTime (0)], @p3 = 'Me2' [Type: String (0)], @p4 = 'LoginPerson3' [Type: String (0)], @p5 = db7baaa0-f97f-4546-a634-9e1c00bef0ea [Type: Guid (0)]
NHibernate: INSERT INTO accounts (created_at, created_by, updated_at, updated_by, name, id) VALUES (@p0, @p1, @p2, @p3, @p4, @p5);@p0 = 1/02/2010 12:00:00 AM [Type: DateTime (0)], @p1 = 'CreatedBy' [Type: String (0)], @p2 = 2/02/2010 12:00:00 AM [Type: DateTime (0)], @p3 = 'UpdatedBy' [Type: String (0)], @p4 = 'Name' [Type: String (0)], @p5 = 5622619e-d8d4-4572-962a-9e1c00bef0f0 [Type: Guid (0)]
NHibernate: UPDATE users SET Account_id = @p0 WHERE id = @p1;@p0 = 5622619e-d8d4-4572-962a-9e1c00bef0f0 [Type: Guid (0)], @p1 = 314bd4ac-d11d-458c-9088-9e1c00bef03b [Type: Guid (0)]
NHibernate: UPDATE users SET Account_id = @p0 WHERE id = @p1;@p0 = 5622619e-d8d4-4572-962a-9e1c00bef0f0 [Type: Guid (0)], @p1 = 91536f22-a53b-47d7-a603-9e1c00bef0e9 [Type: Guid (0)]
NHibernate: UPDATE users SET Account_id = @p0 WHERE id = @p1;@p0 = 5622619e-d8d4-4572-962a-9e1c00bef0f0 [Type: Guid (0)], @p1 = db7baaa0-f97f-4546-a634-9e1c00bef0ea [Type: Guid (0)]
NHibernate: SELECT account0.id as id0_0, account0.created_at as created2_0_0, account0.created_by as created3_0_0, account0.updated_at as updated4_0_0, account0.updated_by as updated5_0_0, account0.name as name0_0 FROM accounts account0 WHERE account0.id=@p0;@p0 = 5622619e-d8d4-4572-962a-9e1c00bef0f0 [Type: Guid (0)]
NHibernate: SELECT users0.Account_id as Account7_1, users0.id as id1, users0.id as id1_0, users0.created_at as created2_1_0, users0.created_by as created3_1_0, users0.updated_at as updated4_1_0, users0.updated_by as updated5_1_0, users0.login as login1_0, users0.Account_id as Account7_1_0 FROM users users0 WHERE users0.Account_id=@p0;@p0 = 5622619e-d8d4-4572-962a-9e1c00bef0f0 [Type: Guid (0)]

Thanks!
Maxus

  1. 2 Posted by Maxus on 27 Oct, 2010 03:51 AM

    Maxus's Avatar

    Sorry noticed a couple missing bits from the acocunt and user classes:

    public class Account : DomainEntity {

        public virtual string Name { get; set; }
        public virtual IList<User> Users { get; private set;}
    
        public Account()
        {
            Users = new List<User>();
        }
    
        public virtual void AddUser(User user)
        {
            user.AddAccount(this);
        }
    
        public virtual void RemoveUser(User user)
        {
            user.RemoveAccount();
        }
    }

    public class User : DomainEntity {

        public virtual string Login { get; set; }
    public virtual Account Account { get; private set; }
    
        public User(){}
    
        public virtual void AddAccount(Account account)
        {
            Account = account;
            account.Users.Add(this);
        }
        public virtual void RemoveAccount()
        {
            Account.Users.Remove(this);
            Account = null;
        }
    }
  2. 3 Posted by Maxus on 28 Oct, 2010 08:22 AM

    Maxus's Avatar

    Hi All,

    The solution appears to be to use CheckComponentList, rather than CheckList.

    As discussed here:

    http://www.triggerfishsoftware.co.uk/blog/fluentnhibernate-persiste...

    refering to here:

    http://groups.google.com/group/fluent-nhibernate/browse_thread/thre...

    Thanks,
    Maxus

Reply to this discussion

Preview Comments are parsed with Markdown. Help with syntax

Attached Files

    You can attach files up to 10MB

    What is two plus two?