32

I just switched to Fluent NHibernate and I've encountered an issue and did not find any information about it.

Here's the case :

public class Field : DomainObject, IField
{
    public Field()
    {  
    }

    public virtual string Name { get; set; }
    public virtual string ContactPerson { get; set; }
    public virtual bool Private { get; set; }
    public virtual IAddress Address { get; set; }  
}

IAddress is an interface implemented by a class named Address

public class Address : DomainObject, IAddress
{
    public Address()
    {
    }

    public virtual string City { get; set; }
    public virtual string Country { get; set; }
    public virtual string PostalCode { get; set; }
    public virtual string StreetAddress { get; set; }
}

Here's my mapping files for both classes

ADDRESS

public class AddressMap : ClassMap<Address>
{   
    public AddressMap()
    {
        WithTable("Addresses");
        Id(x => x.Id, "Id").Access.AsCamelCaseField(Prefix.Underscore).GeneratedBy.Guid();
        Map(x => x.City, "City");
        Map(x => x.Country, "Country");
        Map(x => x.PostalCode, "PostalCode");
        Map(x => x.StreetAddress, "StreetAddress");
    }
}

FIELD

public class FieldMap : ClassMap<Field>
{
    public FieldMap()
    {
        WithTable("Fields");
        Id(x => x.Id, "Id").Access.AsCamelCaseField(Prefix.Underscore).GeneratedBy.Guid();
        Map(x => x.Name, "Name");
        Map(x => x.ContactPerson, "ContactPerson");
        Map(x => x.Private, "Private");
        References(x => x.Address, "AddressId").Cascade.Delete().Cascade.SaveUpdate();
    }
}

So when I tried to retrive a field object from my database, I get an NHibernate error that states that IAddress is not mapped. Is there any way to specify to NHibernate to use the Address class in the mapping?

Please let me know if more information are needed.

Thanks a lot,

Charles

harriyott
  • 10,505
  • 10
  • 64
  • 103
Charles Ouellet
  • 6,338
  • 3
  • 41
  • 57
  • How are you modeling the IAddress hierarchy in the database? Table-per-hierarchy (one table that holds all types of address), or table-per-concrete-class? You said you switched to Fluent so posting an example XML mapping might help determine what you're trying to accomplish. – Stuart Childs Mar 23 '09 at 19:19
  • In fact I switched from ADO.NET Entity Framework to Nhibernate with Fluent NHibernate. In my database, there's a table that holds all types of address. By the way, I decided to remove those interfaces, I realized that it was useless. But I think the question is still valid. Thanks, Charles – Charles Ouellet Mar 24 '09 at 15:14

3 Answers3

37

I find that there are valid reasons for using an interface instead of a concrete class as a property.

For example, if your Field class was in a seperate project to the Address class, and you didn't have a dependency on the Address class's project from the Field class's project.

There are other ways of dealing with this situation, but the simplest way is often to attempt what you are doing and explicity tell NHibernate the concrete class to use for IAddress.

You can now do this in Fluent NHibernate, like this:

References(x => x.Address, "AddressId")
    .Class(typeof(Address);

Unfortunately you can't do this with HasMany or HasManyToMany. I'm not sure if this would even be possible due to lack of good covariance support in C#.

cbp
  • 25,252
  • 29
  • 125
  • 205
  • 28
    **Update:** You can use `References
    (x => x.Address).Column("AddressId");` You can do the same with 'HasMany' and 'HasManyToMany' by adding the generic type parameter.
    – Jay Apr 06 '10 at 14:28
  • Thanks so much, exactly what I was looking for as well... tried doing .CollectionType() chained onto my HasMany(), but that didn't seem to work. – calebt Dec 10 '10 at 16:36
  • 1
    +1 for update. `HasMany(s => s.Features).KeyColumn("SiteId");` Where s.Features is a collection of IFeatureInstance – Ben Foster May 13 '11 at 11:02
  • Thanks, really helped a lost person find his way. – Umair Ahmed Oct 23 '11 at 04:19
  • Can you specify multiple concrete classes that this interface can map to? In the example above it looks like you can only use one concrete class. – Rafi Dec 18 '14 at 08:22
  • @Rafi NHibernate can handle subclass mapping, so if you map to a base class, then NHibernate can determine which subclass to instantiate at runtime. But I don't think this is possible with an interface (unless you write your own custom type handler). – cbp Dec 21 '14 at 22:38
  • 1
    @cbp Thanks for the response. It is possible. Using ReferencesAny. Look at https://github.com/jagregory/fluent-nhibernate/wiki/Fluent-mapping#any. Then you use AddMetaValue to register each class that instantiates the interface. – Rafi Dec 24 '14 at 06:32
  • @cbp Yeah it's a great feature. I was really excited when I saw it. It is one of the reasons I chose nhibernate instead of Entity Framework. – Rafi Dec 25 '14 at 12:11
5

On your Field object, you have an object of type IAddress. This could be implemented by any number of different implementations. With what you are asking, each implementation would have its own mapping, which would introduce any number of difficulties (impossibilities?) for NHibernate to handle.

A simple example would help to demonstrate. Say you have two IAddress implementations Address1, and Address2. They each are saved in their own table, tblAddress1, and tblAddress2. When you try to load your Field object, all NHibernate knows is that you have something that implements IAddress, it does not know which implementation was persisted. How would it know which mapping to use to retrieve the child object for any given field?

I'm sure there are more complications, but this example shows why you have to have a mapping for the exact type of object that you have declared.

Luke
  • 2,101
  • 19
  • 21
0

If you are interested in decoupling your ORM entirely from your domain layer, and referencing interfaces throughout your data layer rather than specifying concrete classes, you can implement an EmptyInterceptor to map between the two.

See my answer here.

Community
  • 1
  • 1
Stumblor
  • 1,118
  • 8
  • 16