views:

85

answers:

1

So I'm a couple of weeks into NHibernate so pleae bear with me. I am working on a legacy database with lots of weird issues.

I have a name object

public class Name    {    
        public Name()
        {
            Addresses = new List<Address>();
        }    
        public virtual string Id { get; set; }
        public virtual string FirstName { get; set; }    
        public virtual string LastName { get; set; }     
        public virtual IList<NameAddress> Addresses { get; set; }    
}

It has children of address

public class Address 
{
        public virtual string NameId { get; set; }
        public virtual string Line1 { get; set; }
        public virtual string City { get; set; }
        public virtual string State { get; set; }
        public virtual string Zipcode { get; set; }
        public virtual string Type { get; set; }
        public virtual bool IsPreferrred { get; set; }
}

Here are the mappings Address has no primary key defined but it is name_id and type that make it unique. The columns you see are the table structure.

   Table("ADDRESS");
    CompositeId()
            .KeyProperty(x => x.NameId, "NAME_ID")
            .KeyProperty(x => x.Type, "ADDRESS_TYPE");
    Map(x => x.IsPreferred)
        .Column("PREF");
    Map(x => x.Line1)
        .Column("ADDRESS1");
    Map(x => x.Line2)
        .Column("ADDRESS2");
    Map(x => x.City)
        .Column("CITY");
    Map(x => x.State)
        .Column("STATE");

Name Table("NAME");

        Id(x => x.Id)
            .GeneratedBy.Custom<XXIdentifierGenerator>(p => p.AddParam("prefix", "NAME"))
            .Column("NAME_ID");
        Map(x => x.Prefix)               
            .Column("NAME_PRE");
        Map(x => x.FirstName)
            .Column("NAME_FIRST");
        HasMany(x => x.Addresses)
            .KeyColumn("NAME_ID")
            .Table("ADDRESS")
            .LazyLoad();

I can create a name without any addresss and get the generated id back.

 repository.Save(name); // only calls session.Save and does a commit 
 Address address = new NameAddress {IsPreferred = true, Type= "Home", Line1 = "123 Main St",
                                    City = "Anytown", State = "CT", Zipcode="06512" };
 name.Addresses.Add(address);
 repository.SaveOrUpdate(name);

When I try to save the address I get an exception

     {"Unexpected row count: 0; expected: 1"}

I am not sure if

  1. My mapping is wrong
  2. I don't get how to wire up a has many
  3. I can't do this without a primary key
  4. In this case do the addresses have to be saved on their own?

Thanks, Paul

A: 

This is a bit of a shot in the dark, but how about adding .Cascade.All or similar on the HasMany(x => x.Addresses) mapping for Name?

I would imagine that NHibernate needs to know that you want new Addresses to be inserted along with a new Name.

Jay
Jay,I just tried the cascade and it didn't work. I added the address before I saved the name as you mentioned.If you look at the code what I am trying to do is simulate creating a new name and adding an address later. Either way I still get the error.
Paul Speranza
@Paul does it work if you set the `NAME_ID` on the `Address` explicitly?
Jay
No - that was a good try though.
Paul Speranza
It is trying to update the address, not insert. Here is the sql it generated. And it is not grabbing the other values.UPDATE ADDRESS SET NAME_ID = :p0 WHERE NAME_ID = :p1 AND ADDRESS_TYPE = :p2;:p0 = 'N00052046505', :p1 = 'N00052046505', :p2 = 'Home'
Paul Speranza
@Paul Okay, I'm thinking from the error message than NHibernate is attempting to `UPDATE` the `Address` instead of `INSERT`, because the identifier is effectively assigned (and NH doesn't know whether the Address is new or not). When it attempts to update it finds no matching row instead of the 1 that it expected. I don't have much experience with assigned composite IDs, but I know that you can specify an unsaved value in the mapping, on which NHibernate can rely to identify transient from persistent instances. I think in FNH that looks something like `.UnsavedValue(null)`.
Jay
Jay, first let me thank you for all of your help. I tried the Unsavedvalue(null) and got this error - {"The 'unsaved-value' attribute is invalid - The value '' is invalid according to its datatype 'urn:nhibernate-mapping-2.2:unsavedValueType' - The Enumeration constraint failed."}BTW - they fixed my dev database so it has a primary key (they left it off) on the nameid and type. What they don't have is a foreign key to the name table. I am still in the same boat.
Paul Speranza
Found this; not sure if it is helpful: http://code.google.com/p/fluent-nhibernate/issues/detail?id=288 I'm wondering if it would make a difference to map this as a bidirectional association (give Address a reference to Name: `References(x => x.Name).Inverse()`); you'd have to add a Name property to the Address class to try it.
Jay
I couldn't get it to work. For now I'm just managing the addresses separately. Thanks for all of your help!
Paul Speranza
Bummer; maybe someone else will come up with something. Good luck!
Jay