Run*_*sen 25 c# nhibernate nhibernate-mapping fluent-nhibernate
我已经被提交并已经开始学习Fluent NHibernate(没有以前的NHibernate经验).在我的项目中,我正在编程接口以减少耦合等.这意味着几乎所有"一切"都指的是接口而不是具体类型(IMessage而不是Message).这背后的想法是通过能够模拟依赖关系来帮助使其更易于测试.
然而,(流利的)当我尝试映射到接口而不是具体类时,NHibernate并不喜欢它.问题很简单 - 根据Fluent Wiki,定义我的类的ID字段是明智的
int Id { get; private set; }
Run Code Online (Sandbox Code Playgroud)
获取典型的自动生成的主键.但是,这只适用于具体的类 - 我不能在接口上指定访问级别,同一行必须是这样
int Id { get; set; }
Run Code Online (Sandbox Code Playgroud)
并且我认为否定在具体类中使setter私有(想法是只有NHibernate应该将ID设置为由DB分配).
就目前而言,我想我只会公开制定者并试图避免写入它的诱惑..但是有没有人知道什么是"正确的",最佳实践方式来创建一个正确的主键只有NHibernate可以写入的字段,而仍然只编程到接口?
更新
根据我从mookid和James Gregory的两个答案之后的理解,我可能走错了路 - 我不应该有理由让每个实体拥有一个接口,就像我现在一样.这一切都很好.我想我的问题就变成了 - 是否没有理由对任何实体的接口进行100%编程?如果甚至有一种情况可以证明这是合理的,那么可以用(流利的)NHibernate做到这一点吗?
我问,因为我不知道,不要批评.谢谢你的回复.:)
Jam*_*ory 12
我意识到这是一种转移,而不是你的问题的答案(尽管我认为mookid已经涵盖了这一点).
您应该真正评估您的域实体上的接口是否实际提供了任何有价值的东西; 很难找到你真正需要这样做的情况.
例如:当它们(几乎)无疑共享相同的签名时,如何依赖于IMessage不依赖于耦合Message?你不应该模拟一个实体,因为它很少有足够的行为来要求被嘲笑.
moo*_*000 10
您可以调整界面以仅包含getter:
public interface ISomeEntity
{
int Id { get; }
}
Run Code Online (Sandbox Code Playgroud)
你的具体类仍然可以实现一个setter,因为你正在编程你的接口,你永远不会"意外"调用setter.
如果你想要禁止设置id,即使你持有对具体实例的引用,你也可以避免实现setter,然后让NHibernate访问字段而不是属性 - 这是正确的,NHibernate可以使用一些漂亮的反射技巧来直接设置您的id字段而不是调用属性.然后你可以像这样映射id:
Id(e => e.Id).Access.AsCamelCaseField();
Run Code Online (Sandbox Code Playgroud)
在这种情况下,您的Id属性必须由相应的id字段支持.有更多的命名约定,例如,如果您更喜欢下划线作为私有字段前缀.
the*_*cko 10
我有完全相同的问题.不幸的是,我有一个使用实体接口的正当理由; 实体模型将以不同的方式实现,并且每个客户具有不同的映射.
整个模型需要是只读的,因此接口的风格如下:
public interface IAccount
{
long AccountId { get; }
IHouse House { get; }
}
public interface IHouse
{
long HouseId { get; }
HouseStatus Status { get; }
IList<IAccount> Accounts { get; }
}
Run Code Online (Sandbox Code Playgroud)
具体实现然后使用内部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<IAccount> Accounts { get; internal set; }
}
Run Code Online (Sandbox Code Playgroud)
我已经走上了映射到具体课程的路线.在创建返回接口并需要转换为具体实现的关系之前,一切都很好.
HasMany(x => x.Accounts)
Run Code Online (Sandbox Code Playgroud)
可以变成
HasMany<Account>(x => x.Accounts)
Run Code Online (Sandbox Code Playgroud)
但没有相应的'演员'
References(x => x.House)
Run Code Online (Sandbox Code Playgroud)
映射到接口(更整洁的解决方案)会引发上面提到的问题,因为Id必须存在于最顶层的类中才能进行设置,并且需要在接口上设置setter.
public sealed class AccountMap : ClassMap<IAccount>
{
public PokerPlayerMap()
{
Id(x => x.AccountId, "account_id");
DiscriminateSubClassesOnColumn("Type").SubClass<Account>(s =>
{
References(x => x.House);
});
}
}
Run Code Online (Sandbox Code Playgroud)
目前,我唯一的解决方案是将setter添加到所有接口Id字段.遗憾的是,Id不能存在于子类中,或者从接口中强制转换类型.
更新: fluent-nhibernate提供的fluent接口不支持使用union-subclass.您将不得不使用常规的hbm映射文件并添加它.
我也试着用流利的NHibernate来做这件事.我不认为映射接口应该是一个问题.您希望使用继承策略,特别是每个具体的表策略.
本质上,您为基类(在本例中为您的接口)创建映射定义,并指定NHibernate如何使用union-subclass处理实现者.
因此,例如,这应该允许您进行多态关联:
<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>
Run Code Online (Sandbox Code Playgroud)
请注意所有具体实施者的Id是如何相同的.NHibernate需要这个.此外,IAccountManager表实际上并不存在.
您还可以尝试利用NHibernate的隐式多态性(在每个混凝土类表的策略下面记录) - 但它有很多限制.
| 归档时间: |
|
| 查看次数: |
12896 次 |
| 最近记录: |