我有一个FluentNhibernate公式映射的问题。我需要在公式中使用连接表中的列。
问题在以下三个表格中展示:城市,人,地址
class Person {
int PersonId { get; set; }
int AddressId { get; set; }
Address PersonAddress { get; set; }
string CityName { get; set; }
}
class Address {
int AddressId { get; set; }
string Street { get; set; }
}
class AddressMap<Address> {
Id(x => x.AddressId, "ADDRESS_ID");
Map(x => x.Street, "STREET");
}
class PersonMap<Person> {
Id(x => x.Id, "PERSON_ID");
References(x => x.PersonAddress).Column("ADDRESS_ID);
Map(x => x.CityName).Formula("select Name from City c where c.street = STREET");
// Doesn't work, STREET is a part of the joined table !
}
知道如何正确编写公式映射吗?当我使用NHibernate将生成的值重写映射时,一切都会正常工作,不过,这是非常脏的解决方案:
Map(x => x.CityName).Formula("select Name from City c where c.street = address1_.STREET");
// Works !!
非常感谢任何帮助!
在NHibernate中没有直接的方法如何支持另一个连接表的别名。为什么?
因为连接表根本不必是连接的一部分
考虑延迟加载的地址 - 即不是SELECT的一部分,或者没有地址表的一些投影。
公式旨在作为一种智能或更智能的方式,如何访问当前表中的数据。或者如何创建一些独立的subselect,subquery ..并传递当前行id
,作为参考过滤器。
例如。在Ayende的NHibernate property Mapping,我们可以看到:
<property name="CountOfPosts"
formula="(select count(*) from Posts where Posts.Id = Id)"/>
像这样生成SELECT:
SELECT ...
// the injected 'Id' is from current table
(select count(*) from Posts where Posts.Id = this_.Id)
FROM [MainTable] this_ // the alias of current table
建议:City
或城市名称可能只是另一个引用(如果不是直接地址的字符串属性)。它将允许我们非常容易地使用它(选择,投影,过滤,排序),我们将不依赖于某些“隐藏的,硬编码”映射。这将是干净的模型:
Person.Address.City.Name
RadimKöhler提供的大多数一般提示都是正确的,但关于配方公式的原始问题 - NH(至少> = 5,我不知道如何与旧的公式相比)应该能够涵盖这种情况。
首先,您的尝试将失败,因为在公式中,任何引用除当前主实体以外的任何内容的列名称都必须以别名为前缀。否则,NH将假定列名称是指当前主要实体。
例如,您最初的尝试是:
Map(x => x.CityName).Formula("select Name from City c where c.street = STREET");
^ fault ^CEE ^CEE ^fault
请注意如何将c
前缀添加到street
以消除哪些street
来自City以及哪个不是来自哪个c.street
。 NHibernate将检测此类前缀,并假设所有带前缀的列都绑定到sql文本中指定的表。所以,STREET
将被认为并不重要。但是,Name
和Name
没有前缀。 NH将假设它们来自主要实体,即您为其定义映射的Person。这有时会产生有趣的结果,但更有可能的是,Person表上没有Street
和Map(x => x.CityName).Formula("select c.Name from City c where c.street = a.STREET");
^CEE ^AYE
列,你会从rdbms得到一个错误。
要解决这个问题,你应该有类似的东西:
a
当然,现在另一个错误会说前缀Map(x => x.CityName)
.Formula(@"
select c.Name
from City c
where c.street =
( select a.Street
from Address a
where a.address_id = address_id
)"); ^
^ no prefix!
未知。当然,我们没有在任何地方定义它,因为我们有PERSON和CITY,并且在sql查询中完全省略了中间ADDRESS。
现在,看看这个:
address_id
由于内部 select c.Name
from Address a
inner join City c no c.street = a.street
where a.address_id = address_id
没有前缀,NH将假设它来自Person。大。没有其他没有前缀的列,因此忽略所有其他列并假定它们绑定到SQL文本本身中定义的范围。
因此,内部子查询将选择基于Person.AddressID的地址,并从中选择街道。希望它只是一条街,因为我们通过ID匹配地址。找到街道后,外部子查询将使用它来匹配城市。
我们也可以把公式写成
where foo = (select x from...)
具有相同的效果,可能性能不同。
有一点需要注意,当您编写子查询公式或在子查询中编写street
时,您必须确保子查询始终返回零或一个结果。不是两个或更多。许多RDBMS将此类事件视为错误。在此查询的情况下,多个城市中可能会出现相同的qazxswpoi名称,因此您很可能只是因为您将地址与City匹配。