可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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. :)
回答1:
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.
回答2:
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.
回答3:
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.
回答4:
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.
回答5:
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.