使用Fluent NHibernate进行映射时对接口进行编程

我已经被提交并已经开始学习Fluent NHibernate(没有以前的NHibernate经验)。 在我的项目中,我正在编程接口以减少耦合等。这意味着几乎所有“一切”都指的是接口而不是具体类型(IMessage而不是Message)。 这背后的想法是通过能够模拟依赖关系来帮助使其更易于测试。

但是,(流畅)当我尝试映射到接口而不是具体类时,NHibernate不喜欢它。 问题很简单 – 根据Fluent Wiki,例如,定义我的类的ID字段是明智的

int Id { get; private set; } 

获取典型的自动生成的主键。 但是,这只适用于具体的类 – 我不能在接口上指定访问级别,同一行必须是这样

 int Id { get; set; } 

而且我认为否定在具体类中将setter设为私有(这个想法是只有NHibernate应该将ID设置为由DB分配)。

就目前而言,我想我只会公开制定者,并试图避免写入它的诱惑。但是,是否有人知道什么是“适当的”,最佳实践方式来创建一个正确的主键只有NHibernate可以写入的字段,同时仍然只编程到接口?

更新

根据我在mookid和James Gregory的两个答案后的理解,我可能会走错路 – 我不应该有理由让每个实体拥有一个接口,就像我现在一样。 这一切都很好。 我想我的问题就变成了 – 是否没有理由对任何实体的接口进行100%编程? 如果甚至有一种情况可以certificate这是合理的,那么可以用(流利的)NHibernate做到这一点吗?

我问,因为我不知道,不要批评。 谢谢你的回复。 🙂

更新:通过fluent-nhibernate提供的流畅接口不支持使用union-subclass。 您将不得不使用常规的hbm映射文件并添加它。

我也是用流利的NHibernate尝试这样做的。 我不认为它应该是映射接口的问题。 您希望使用inheritance策略,特别是每个具体的表策略 。

本质上,您为基类(在本例中为您的接口)创建映射定义,并指定NHibernate如何使用union-subclass处理实现者。

因此,例如,这应该允许您进行多态关联:

           ...  

请注意所有具体实施者的Id是如何相同的。 NHibernate需要这个。 另外,IAccountManager表实际上并不存在。

您还可以尝试利用NHibernate的隐式多态性(在每个混凝土类表的策略下面记录) – 但它有很多限制。

我意识到这是一种转移,而不是你的问题的答案(尽管我认为mookid已经涵盖了这一点)。

您应该真正评估您的域实体上的接口是否实际上提供了任何有价值的东西; 很难找到你真正需要这样做的情况。

例如:当它们(几乎)无疑共享相同的签名时,如何依赖于IMessage不是依赖于Message ? 你不应该模拟一个实体,因为它很少有足够的行为来要求被嘲笑。

您可以调整界面以仅包含getter:

 public interface ISomeEntity { int Id { get; } } 

你的具体类仍然可以实现一个setter,因为你正在编程你的接口,你永远不会“意外”调用setter。

如果你想要禁止设置id,即使你持有对具体实例的引用,你也可以避免实现setter,然后让NHibernate访问字段而不是属性 – 这是正确的,NHibernate可以使用一些漂亮的reflection技巧来直接设置您的id字段而不是调用属性。 然后你可以像这样映射id:

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

在这种情况下,您的Id属性必须由相应的id字段支持。 有更多的命名约定,例如,如果您更喜欢下划线作为私有字段前缀。

我有完全相同的问题。 不幸的是,我有使用实体接口的正当理由; 实体模型将以不同的方式实现,并且每个客户具有不同的映射。

整个模型需要是只读的,因此接口的风格如下:

 public interface IAccount { long AccountId { get; } IHouse House { get; } } public interface IHouse { long HouseId { get; } HouseStatus Status { get; } IList Accounts { get; } } 

具体实现然后使用内部setter实现这些:

 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 Accounts { get; internal set; } } 

我已经走下了映射到具体类的路线。 在创建返回接口并需要转换为具体实现的关系之前,一切都很好。

 HasMany(x => x.Accounts) 

可以变成

 HasMany(x => x.Accounts) 

但没有相应的“演员”

 References(x => x.House) 

映射到接口(整个解决方案)会引发上面提到的问题,因为Id必须存在于最顶层的类中才能进行设置,并且需要在接口上设置setter。

 public sealed class AccountMap : ClassMap { public PokerPlayerMap() { Id(x => x.AccountId, "account_id"); DiscriminateSubClassesOnColumn("Type").SubClass(s => { References(x => x.House); }); } } 

目前,我唯一的解决方案是在所有接口Id字段中添加setter。 很遗憾,Id不能存在于子类中,或者从接口中转换类型。

看起来我没有足够的声誉评论其他人的答案,因此我将不得不自己做出这个答案。

引用现在有一个generics重载,允许Gecko在他的答案中寻找的演员。