views:

3088

answers:

5

Hi,

I have been whipped into submission and have started learning Fluent NHibernate (no previous NHibernate experience). In my project, I am programming to interfaces to reduce coupling etc. That means pretty much "everything" refers to the interface instead of the concrete type (IMessage instead of Message). The thought behind this is to help make it more testable by being able to mock dependencies.

However, (Fluent) NHibernate doesn't love it when I try to map to interfaces instead of concrete classes. The issue is simple - according to the Fluent Wiki, it is smart to define the ID field of my class as for instance

int Id { get; private set; }

to get a typical auto-generated primary key. However, that only works with concrete classes - I can't specify an access level on an interface, where the same line has to be

int Id { get; set; }

and I guess that negates making the setter private in the concrete class (the idea being that only NHibernate should ever set the ID as assigned by the DB).

For now, I guess I will just make the setter public and try to avoid the temptation of writing to it.. But does anyone have an idea of what would be the "proper", best-practice way to create a proper primary-key field that only NHibernate can write to while still only programming to interfaces?

UPDATED

From what I understand after the two answers below from mookid and James Gregory, I may well be on the wrong track - there shouldn't be a reason for me to have an interface per entity as I have now. That's all well and good. I guess my question then becomes - is there no reason to program 100% against an interface for any entities? And if there is even a single situation where this could be justified, is it possible to do this with (Fluent) NHibernate?

I ask because I don't know, not to be critical. Thanks for the responses. :)

+4  A: 

You can adjust your interface to contain only a getter:

public interface ISomeEntity
{
    int Id { get; }
}

Your concrete class can still implement a setter as well, and since you are programming to your interfaces you will never call the setter "by accident".

If you want to disallow setting the id even when you hold a reference to a concrete instance, you can refrain from implementing a setter, and then let NHibernate access the field instead of the property - that's right, NHibernate can use some nifty reflection trickery to set your id field directly instead of invoking the property. Then you might map the id like this:

Id(e => e.Id).Access.AsCamelCaseField();

in which case your Id property must be backed by a corresponding id field. There are more naming conventions, e.g. if you prefer underscores as private field prefix.

mookid8000
Thanks for the answer! Your ISomeEntity is how I had it, but then NHibernate complains about no setter if I map to the interface. If I map to the concrete class, it works, but that seems to smell a bit to me.. So I guess I will go for the second option, and go for field access. Less clean that I'd like, but I guess reality limits what I can do. :)
Rune Jacobsen
Ok, I tried to do the Id(e => e.Id).Access.AsCamelCaseField();bit, but that requires the backing id field to be in the interface as well. I see that there might not be an easy way to accomplish what I am after, but I also see from James Gregory's answer that what I am after might not be The Right Thing (tm). Thanks though!
Rune Jacobsen
+6  A: 

I realise this is a diversion, and not an answer to your question (although I think mookid has got that covered).

You should really evaluate whether interfaces on your domain entities are actually providing anything of worth; it's rare to find a situation where you actually need to do this.

For example: How is relying on IMessage any less coupled than relying on Message, when they both (almost) undoubtedly share identical signatures? You shouldn't need to mock an entity, because it's rare that it has enough behavior to require being mocked.

James Gregory
Wow, answer from the mothership. :) Thanks, and I see. I certainly can't justify using interfaces by myself, I have just read enough blogs etc. that claims I should always be programming to interfaces, never to concrete implementations. I'll abandon that thanks to your (and mookid's) insight, but it would be great to see more about this in a TDD/mock/IoC/ORM crazy world. ;)
Rune Jacobsen
You mock to verify behavior, and you inject to avoid coupling on implementations. Entities are rarely anything more than data structures, and as such have no behavior (and thus no variability in implementation) to require abstractions (interfaces).That being said, this design should still be supported by Fluent NHibernate as it's supported by NH-core, so I'll be sure to create an issue for it.
James Gregory
Thanks James, both for the insight and the issue. :)
Rune Jacobsen
What if you realise you need different Message objects later on down the road (i.e. ImmediateMessage vs. ScheduledMessage or whatever)? Is that a good example of a time to use interfaces or should the domain model be re-evaluated?
Jeffrey Cameron
+4  A: 

UPDATE: using union-subclass is not supported via the fluent interface fluent-nhibernate provides. You'll have to use a regular hbm mapping file and add it.

I too I'm trying do this with fluent NHibernate. I don't think it should be a problem mapping interfaces. You want to use an inheritance strategy, specifically the table-per-concrete-class strategy.

Essentially, you create a mapping definition for the base class (in this case your interface) and specify how to NHibernate should deal with implementers by using union-subclass.

So, for example, this should allow you to make polymorphic associations:

<class name="IAccountManager"
                abstract="true"
                table="IAccountManager">

        <id name="Id">
                <generator class="hilo"/>
        </id>

        <union-subclass
                table="DefaultAccountManager"
                name="DefaultAccountManager">
                <property name="FirstName"/>
        </union-subclass>

        <union-subclass
                table="AnotherAccountManagerImplementation"
                name="AnotherAccountManagerImplementation">
                <property name="FirstName"/>
        </union-subclass>
        ...
</class>

Note how the Id is the same for all concrete implementers. NHibernate required this. Also, IAccountManager table doesn't actually exist.

You can also try and leverage NHibernate's Implicit Polymorphism (documented below the table-per-concrete-class strategy) - but it has tons of limitations.

TheDeeno
Union subclass is now supported by Fluent NHibernate 1.1 : Call UseUnionSubclassForInheritanceMapping() from the base ClassMap
cbp
+2  A: 

I am having exactly the same issue. Unfortunately I have a valid reason for using entity interfaces; the entity model will be implemented in different ways and with different mappings per customer.

The entire model needs to be read-only, so interfaces are of the style:

public interface IAccount
{
 long AccountId { get; }
 IHouse House { get; }
}

public interface IHouse
{
 long HouseId { get; }
 HouseStatus Status { get; }
 IList<IAccount> Accounts { get; }
}

Concrete implementations then implement these with internal setters:

public class Account: IAccount
{
 public virtual long AccountId { get; internal set; }
 public virtual IHouse House { get; internal set; }
}

public class House: IHouse
{
 public virtual long HouseId { get; internal set; }
 public virtual HouseStatus Status { get; internal set; }
 public virtual IList<IAccount> Accounts { get; internal set; }
}

I have gone down the route of mapping to the concrete classes. All is fine until you create relations which return interfaces and need to be cast to concrete implementations.

HasMany(x => x.Accounts)

can become

HasMany<Account>(x => x.Accounts)

But there is no equivalent 'cast' for

References(x => x.House)

Mapping to the interfaces (the neater solution) throws up the problem mentioned above in that the Id must exist on the topmost class for setting and requires a setter on the interface.

public sealed class AccountMap : ClassMap<IAccount>
{
 public PokerPlayerMap()
 {
  Id(x => x.AccountId, "account_id");

  DiscriminateSubClassesOnColumn("Type").SubClass<Account>(s =>  
  {
   References(x => x.House);  
  });
 }
}

For now, my only solution is to add setters to all of the interface Id fields. Its a shame the Id can't exist inside a subclass or have its type cast from the interface.

theGecko
A: 

Looks like I don't have enough reputation to comment on other peoples answers yet as such I'm going to have to make this an answer in it's own right.

References now has a generic overload to allow the cast that theGecko was looking for in his answer.

BenCr