7

I'm using two class NiceCustomer & RoughCustomer which implment the interface ICustomer.

The ICustomer has four properties. They are:

  1. Property Id() As Integer
  2. Property Name() As String
  3. Property IsNiceCustomer() As Boolean
  4. ReadOnly Property AddressFullText() As String

I don't know how to map the interface ICustomer, to the database.

I get an error like this in the inner exception.

An association refers to an unmapped class: ICustomer

I'm using Fluent and NHibernate.

Stephan Bauer
  • 9,120
  • 5
  • 36
  • 58
Josh
  • 117
  • 1
  • 4
  • I don't think you need to map any interface as such .. could you please post your mapping files ? Thanks – Mahesh Velaga Feb 18 '10 at 06:25
  • Thanks for responding Mahesh. But as Kevin had said we cannot map an interface in nhibernate. I've changed the interface to a base class. – Josh Feb 24 '10 at 11:27

3 Answers3

4

You can map directly to interfaces in NHibernate, by plugging in an EmptyInterceptor during the configuration stage. The job of this interceptor would be to provide implementations to the interfaces you are defining in your mapping files.

public class ProxyInterceptor : EmptyInterceptor
{
    public ProxyInterceptor(ITypeHandler typeHandler) {
        // TypeHandler is a custom class that defines all Interface/Poco relationships
        // Should be written to match your system
    }

    // Swaps Interfaces for Implementations
    public override object Instantiate(string clazz, EntityMode entityMode, object id)
    {
        var handler = TypeHandler.GetByInterface(clazz);
        if (handler == null || !handler.Interface.IsInterface) return base.Instantiate(clazz, entityMode, id);
        var poco = handler.Poco;
        if (poco == null) return base.Instantiate(clazz, entityMode, id);

        // Return Poco for Interface
        var instance = FormatterServices.GetUninitializedObject(poco);
        SessionFactory.GetClassMetadata(clazz).SetIdentifier(instance, id, entityMode);

        return instance;
    }

}

After this, all relationships and mappings can be defined as interfaces.

public Parent : IParent {
    public int ID { get; set; }
    public string Name { get; set; }
    public IChild Child { get; set; }
}

public Child : IChild {
    public int ID { get; set; }
    public string Name { get; set; }
}

public class ParentMap : ClassMap<IParent>
{
    public ParentMap()
    {
        Id(x => x.ID).GeneratedBy.Identity().UnsavedValue(0);
        Map(x => x.Name)
    }
}   

...

This type of technique is great if you want to achieve true decoupling of your ORM, placing all configuration/mappings in a seperate project and only referencing interfaces. Your domain layer is then not being polluted with ORM, and you can then replace it at a later stage if you need to.

Stumblor
  • 1,118
  • 8
  • 16
  • Can you add an example of using the ProxyInterceptor in the config? I'm just starting using Fluent Nhibernate now and am not familiar enough with the config part to know where to specify the proxy... – Bogdan Varlamov Apr 04 '14 at 20:55
  • I was able to get it working, but the problem is that the Domain layer is still overly polluted by ORM-specific methods--primarily the setters on properties. The ORM needs to be able to set a value on each field, while in the Domain object I DON'T want to make it possible to change the Id field of an object (same with many other read-only properties). – Bogdan Varlamov Apr 05 '14 at 01:23
0

how are you querying? If you're using HQL you need to import the interface's namespace with an HBM file with this line:

<import class="name.space.ICustomer, Customers" />

If you're using Criteria you should just be able to query for ICustomer and it'll return both customer types.

If you're mapping a class that has a customer on it either through a HasMany, HasManyToMany or References then you need to use the generic form:

References<NiceCustomer>(f=>f.Customer)

If you want it to cope with either, you'll need to make them subclasses

Subclassmap<NiceCustomer>

In which case I think you'll need the base class Customer and use that for the generic type parameter in the outer class:

References<Customer>(f=>f.Customer)

Regardless, you shouldn't change your domain model to cope with this, it should still have an ICustomer on the outer class.

I'm not sure if the 1.0RTM has the Generic form working for References but a quick scan of the changes should show the change, which I think is a two line addition.

Stu
  • 2,426
  • 2
  • 26
  • 42
-1

It is not possible to map an interface in nhibernate. If your goal is to be able to query using a common type to retrieve both types of customers you can use a polymorphic query. Simply have both your classes implement the interface and map the classes normally. See this reference:

https://www.hibernate.org/hib_docs/nhibernate/html/queryhql.html (section 11.6)

Kevin Stafford
  • 160
  • 1
  • 1
  • I've tried polymorphic query too but it doesn't work. Still had trouble with that unmapped interface customer. So made it a base class now that works fine. Thanks much Kevin. – Josh Feb 24 '10 at 11:24
  • "It is perfectly acceptable for the named persistent class to be an interface. You would then declare implementing classes of that interface using the element." from http://knol.google.com/k/fabio-maulo/nhibernate-chapter-5-basic-o-r-mapping/1nr4enxv3dpeq/8# – Neal Nov 15 '10 at 21:22
  • 3
    Link in this answer is broken. Please update the link or add more detail to your answer. – Richard Garside Mar 26 '13 at 15:33