我对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 !!
非常感谢任何帮助!
答案 0 :(得分:1)
在NHibernate中有没有直接方式如何支持另一个加入表的别名。为什么?
因为加入表只是不必须成为加入的一部分
考虑延迟加载地址 - 即不是SELECT的一部分,或者没有地址表的一些投影。
公式旨在作为智能或智慧方式,如何访问当前表中的数据。或者如何创建一些独立的子选择子查询,并传递当前行id
作为参考过滤器。
E.g。在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
或城市名称可能只是另一个引用(如果不是直接地址的字符串属性)。它将允许我们非常容易地使用它(选择,投影,过滤,排序),我们不会依赖于某些隐藏的,硬编码的&#34;映射。这将是干净的模型:
Person.Address.City.Name
答案 1 :(得分:0)
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
来自城市,哪个不是城市。 NHibernate将检测到此类前缀,并假定所有带前缀的列都绑定到sql文本中指定的表。因此,将假设c.street
不重要。但是,STREET
和Name
没有前缀。 NH将假定它们来自主要实体,即您要为其定义映射的Person。有时这可能会产生有趣的结果,但更有可能的是,“人”表上没有Name
或Street
列,并且您会从rdbms中得到一个错误。
要解决此问题,您应该具有以下内容:
Map(x => x.CityName).Formula("select c.Name from City c where c.street = a.STREET");
^CEE ^AYE
当然,现在另一个错误将表明前缀a
未知。当然,我们没有在任何地方定义它,因为我们有PERSON和CITY并且中间的ADDRESS在sql查询中完全被省略了。
现在,看看这个:
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!
由于内部address_id
没有前缀,因此NH将假定它来自Person。大。没有其他未添加前缀的列,因此所有其他列都将被忽略,并假定绑定到SQL文本本身中定义的范围。
因此,内部子查询将基于Person.AddressID选择地址,然后从中选择街道。希望这只是一条街道,因为我们通过ID匹配地址。找到街道后,外部子查询将使用它来匹配城市。
我们也可以将公式写为
select c.Name
from Address a
inner join City c no c.street = a.street
where a.address_id = address_id
效果相同,性能可能不同。
要注意的一件事是,当您编写子查询公式或在子查询中编写where foo = (select x from...)
时,必须必须确保子查询始终返回零或一个结果。不能两个或两个以上。许多RDBMS将这种情况视为错误。如果进行此查询,则在多个城市中可能会使用相同的street
名称,因此您将失败的可能性很高,只是因为您将地址按街道匹配到了城市。