1
votes

I'm trying to do a 1:1 relationship in FN but its being a bit of a pain.

I looked at http://avinashsing.sunkur.com/2011/09/29/how-to-do-a-one-to-one-mapping-in-fluent-nhibernate/ which seemed to confirm this should work but hey ho, it keeps trying to insert records into the child tables before the parent which means the child-inserts dont contain the CustomerId which is required as its a foreign key constraint.

Tables

   +-----------------------+      +----------------------+
   | tblCustomer           |      |  tblCustomerPhoto    |
   |-----------------------|      |----------------------|
   |                       | 1:1  |                      |
   | CustomerID (PK)       |+---->|  CustomerID (FK)     |
   | OtherJunk...          |      |  Photo (Image)       |
   |                       |      |                      |
   |                       |      |                      |
   +-----------------------+      +----------------------+

Models

public class Customer 
{
    public virtual int CustomerID { get; private set; }
    /* public virtual Other stuff */
}

public class CustomerPhoto
{
    public virtual int CustomerID { get;set;}
    public virtual Byte[] Photograph { get; set; }
}

Maps

public class CustomerPhotoMap : ClassMap<CustomerPhoto>
{
    public CustomerPhotoMap()
    {
        Id(x => x.CustomerID)
            .Column("CustomerID")
            .GeneratedBy.Assigned();

        Map(x => x.Photograph)
            .CustomSqlType("Image")
            .CustomType<Byte[]>()
            .Nullable();
    }
}

public class CustomerMap : ClassMap<Customer>
{
    public CustomerMap()
    {

        Id(x => x.CustomerID)
            .GeneratedBy.Identity()
            .Column("CustomerID");
        HasOne<CustomerPhoto>(x => x.CustomerPhoto)
            .LazyLoad()
            .ForeignKey("CustomerID");
    }
}

Test

class CustomerMappingFixture : IntegrationTestBase
{
    [Test]
    public void CanMapCustomer()
    {
        new PersistenceSpecification<Customer>(Session)
            .CheckReference(x => x.CustomerPhoto, new CustomerPhoto(){ Photograph = Arrange.GetBitmapData(ImageFormat.Jpeg) })
            .VerifyTheMappings();
    }
}

Now the foreign key column on the CustomerPhoto was set to not-null and I was repeating this in the notation on the CustomerPhotoMapping. On the basis of ( https://stackoverflow.com/a/2286491/529120 ) I changed that to nullable and removed the notation from the mapping.

Regardless of which, NHibernate returns System.NullReferenceException : Object reference not set to an instance of an object.

and appears to be trying to insert a CustomerPhoto record first, passing zero as the CustomerId; then creating the Customer record, then trying to select the customer and photo using a left outer join. Which obviously wont work as at no point has it attempted to update the ID in the photo table.

1

1 Answers

2
votes

Few things I noticed

  1. Using CheckReference to verify this mapping is probably incorrect. I'm pretty sure this is only for many-to-one relationships. Therefore it makes sense that it's trying to insert the CustomerPhoto before the Customer. I would write a test using straight up NH sessions here. I found it to be more trouble than it's worth to use PersistenceSpecification for many of my mappings that were non-trivial.
  2. The one to one mappings looked like they were a bit off (proposed solution below)
  3. When mapping an image column to a byte array I don't think there is a need to declare a custom type. The mapping below has worked fine for me. I think the other stuff you have on that property mapping is un-needed.

I have a mapping almost identical to yours and this is what I use:

public class Customer 
{
  public virtual int CustomerID { get; private set; }
  public virtual CustomerPhoto CustomerPhoto { get; set; }
  /* public virtual Other crap */

}

public class CustomerPhoto
{
  public virtual Customer Customer { get; set; }
  public virtual Byte[] Photograph { get; set; }
}

public class CustomerPhotoMap : ClassMap<CustomerPhoto>
{
    public CustomerPhotoMap()
    {
        Id(x => x.Id)
            .Column("CustomerID")
            .GeneratedBy.Foreign("Customer");

        Map(x => x.Photograph).Length(Int32.MaxValue);
    }
}

public class CustomerMap : ClassMap<Customer>
{
    public CustomerMap()
    {

        Id(x => x.CustomerID).GeneratedBy.Identity()
            .Column("CustomerID");

        HasOne(x => x.CustomerPhoto)
            .Cascade.All();

    }
}