C# 使用 Fluent NHibernate 映射时对接口进行编程
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/845536/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Programming to interfaces while mapping with Fluent NHibernate
提问by Rune Jacobsen
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.
我已经被鞭策并开始学习 Fluent NHibernate(之前没有 NHibernate 经验)。在我的项目中,我正在对接口进行编程以减少耦合等。这意味着“一切”几乎都是指接口而不是具体类型(IMessage 而不是 Message)。这背后的想法是通过能够模拟依赖关系来帮助使其更易于测试。
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
但是,当我尝试映射到接口而不是具体类时,(流畅的)NHibernate 不喜欢它。问题很简单 - 根据 Fluent Wiki,将我的类的 ID 字段定义为例如
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).
我猜这否定了在具体类中将 setter 设为私有(这个想法是只有 NHibernate 应该设置由 DB 分配的 ID)。
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?
现在,我想我只是将 setter 公之于众,并尽量避免写入它的诱惑..只有 NHibernate 可以写入而仍然只对接口进行编程的字段?
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?
根据我在 mookid 和 James Gregory 的以下两个答案之后的理解,我很可能走错了路 - 我没有理由像现在这样为每个实体拥有一个界面。这一切都很好。我想我的问题就变成了 - 是否没有理由针对任何实体的接口进行 100% 编程?如果甚至有一种情况可以证明这是合理的,是否可以使用(Fluent)NHibernate 来做到这一点?
I ask because I don't know, not to be critical. Thanks for the responses. :)
我问是因为我不知道,不要批评。感谢您的回复。:)
采纳答案by Dane O'Connor
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.
更新:通过 fluent-nhibernate 提供的流畅接口不支持使用 union-subclass。您必须使用常规的 hbm 映射文件并添加它。
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.
我也在尝试用流畅的 NHibernate 来做这件事。我不认为映射接口应该是问题。您想使用继承策略,特别是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.
本质上,您为基类(在本例中为您的接口)创建了一个映射定义,并通过使用联合子类指定 NHibernate 应如何处理实现者。
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.
注意所有具体实现者的 Id 是如何相同的。NHibernate 需要这个。此外, IAccountManager 表实际上并不存在。
You can also try and leverage NHibernate's Implicit Polymorphism (documented below the table-per-concrete-class strategy) - but it has tons of limitations.
您还可以尝试利用 NHibernate 的隐式多态性(记录在 table-per-concrete-class 策略下方) - 但它有很多限制。
回答by mookid8000
You can adjust your interface to contain only a getter:
您可以调整您的界面以仅包含一个 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".
您的具体类仍然可以实现 setter,并且由于您正在对接口进行编程,因此您永远不会“意外”调用 setter。
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,则可以避免实现 setter,然后让 NHibernate 访问该字段而不是属性 - 没错,NHibernate 可以使用一些漂亮的反射技巧来直接设置您的 id 字段而不是调用该属性。然后你可以像这样映射 id:
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.
在这种情况下,您的Id
财产必须由相应的id
字段支持。有更多的命名约定,例如,如果您更喜欢下划线作为私有字段前缀。
回答by James Gregory
I realise this is a diversion, and not an answer to your question (although I think mookid has got that covered).
我意识到这是一种消遣,而不是对您问题的回答(尽管我认为 mookid 已经涵盖了这一点)。
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.
例如:当它们都(几乎)毫无疑问地共享相同的签名时,如何依赖IMessage
比依赖低耦合Message
?您不应该需要模拟一个实体,因为它很少有足够的行为需要被模拟。
回答by theGecko
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.
映射到接口(更简洁的解决方案)会引发上面提到的问题,因为 Id 必须存在于用于设置的最顶层类中,并且需要接口上的设置器。
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.
目前,我唯一的解决方案是将 setter 添加到所有接口 Id 字段。令人遗憾的是,Id 不能存在于子类中,也不能从接口进行类型转换。
回答by BenCr
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.
References 现在有一个通用的重载,以允许 theGecko 在他的答案中寻找的演员表。