views:

161

answers:

1

Hi, I have inhertited an legacy application, that I have little control over. I need to map a one to one relationship because "A user may have one of these, lets call them 'a Rejection', but not more than one" The data stored in the Rejection table is quite large.

Anyway, I have simplyfied this model, using cats, dogs and an owner. An animal, by default, is stray (its owner object will be null), until we assign an owner. Cats and dogs are both animals. The classes look like this. By law, you are only allowed one Pet ;-)

public abstract class Entity {
    public virtual int Id { get; set; }
}

public class Animal : Entity {
    public virtual string Name { get; set; }
    public virtual Owner Owner { get; set; }
}

public class Dog : Animal {
    public virtual string Bark { get; set; }
}

public class Cat : Animal {
    public virtual string Colour { get; set; }
}

public class Owner : Entity {
    public virtual string Name { get; set; }
    public virtual Animal Pet { get; set; }
}

And the mapping files for the Animal and Owner are like this:

public class OwnerMap : ClassMap<Owner> {
    public OwnerMap() {
        Id(x => x.Id).Column("animal_id");
        Map(x => x.Name);
        References(x => x.Pet, "animal_id").Cascade.SaveUpdate().ForeignKey();
    }
}
public class AnimalMap : ClassMap<Animal> {
    public AnimalMap() {
        Id(x => x.Id).Column("animal_id");
        Map(a => a.Name);
        HasOne(x => x.Owner).PropertyRef(p => p.Pet).Cascade.All().Fetch.Join();
    }
}

Now for the problem. I am able to insert the data into the database, and the sql generated by nhibernate is:

INSERT INTO [Animal] ([Name]) VALUES (@p0); select SCOPE_IDENTITY();@p0 = 'Odie'
INSERT INTO [Dog] ([Bark], [animal_id]) VALUES (@p0, @p1);@p0 = 'bark', @p1 = 1
INSERT INTO [Owner] ([Name], [animal_id]) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 'Jon', @p1 = 1

The data does appear in the database, but then I get an error ' NHibernate.AssertionFailure: null identifier' after calling Session.save(animal) I think the problem is with the last bit of SQL "select SCOPE_IDENTITY()" which returns null. This is because the Owner Table primary key needs to come from the Animal Table.

Can anyone suggest how I could map this correctly in FNH? Any help appriciated. Dai

the table Schema

CREATE TABLE [dbo].[Animal](
    [animal_id] [int] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](50) NULL, 
    CONSTRAINT [PK_Animal] PRIMARY KEY CLUSTERED ([animal_id] ASC ) 
    WITH PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF,
    ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) 
ON [PRIMARY]) ON [PRIMARY]


CREATE TABLE [dbo].[Owner](
    [animal_id] [int] NOT NULL,
    [Name] [nvarchar](50) NULL, CONSTRAINT [PK_owner2] 
    PRIMARY KEY CLUSTERED (
    [animal_id] ASC )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, 
    IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON)
    ON [PRIMARY] ) ON [PRIMARY]
A: 

Based on your mapping, the Owner table should have a column called "animal_id" that stores its ID (its primary key). But that's the same as the name of the column that stores the ID (foreign key) of the animal that this instance of Owner has.

Can you post your actual DB schema?

statichippo
Thanks, just added it. One could put the owner name as a column in the Animal table too, and call it owner_name, but here I am trying to model a 1-2-1 relationship. My naming and my real world example may not be that great.
Dai Bok