我有一个相当常见的场景,我有两张桌子,我想联系在一起,特别是国家和语言。
我已经指定了这样的映射:
public LanguageMapping()
{
Table("Languages");
Id(x => x.Id, "LANG_ID").GeneratedBy.Identity();
HasMany(x => x.CountriesLanguages)
.Cascade
.SaveUpdate()
.KeyColumn("LANG_ID");
}
public CountryMapping()
{
Table("COUNTRIES")
Id(x => x.Id, "COUNTRY_ID").GeneratedBy.Identity();
HasMany(x => x.Languages)
.Cascade
.SaveUpdate()
.KeyColumn("COUNTRY_ID");
}
public CountryLanguageMapping()
{
Table("COUNTLANG");
Id(x => x.Id, "COUNTLANG_ID").GeneratedBy.Identity();
References(x => x.Country,"COUNTRY_ID").Cascade.SaveUpdate().Not.Nullable();
References(x => x.Language, "LANG_ID").Cascade.SaveUpdate().Not.Nullable();
}
所以,这一切看起来都很好。 (注意我已经取出数据以简化)。所以我要说加入表必须同时包含一个国家和一种语言才有效,这看起来很公平。
在我即将讨论的所有场景中,会话中都没有数据。
如果我这样做,一切都很好。
var country = new DboCountry
{
Name = "UK",
Code = "GB",
};
var language = new DboLanguage()
{
Code = "EN",
Name = "English"
};
var countryLanguage = new DboCountryLanguage
{
Country = country,
Language = language,
IsDefault = true,
LanguageCode = "en-GB"
};
dataSession.Save(countryLanguage);
一切都好。但我并不总是想这样工作。如果我尝试像下面这样做,它会打破一个例外,暗示语言没有被设置,我打破了可空的constaraint ......,尽管我期待着级联来处理这个......
var language = new DboLanguage()
{
Code = "EN",
Name = "English"
};
var countryLanguage = new DboCountryLanguage
{
Language = language,
IsDefault = true,
LanguageCode = "en-GB"
};
var country = new DboCountry
{
Name = "UK",
Code = "GB",
Languages = new []{countryLanguage}
};
dataSession.Save(country);
我得到NHibernate.PropertyValueException : not-null property references a null or transient value IDL.Common.ControlCloud.Orm.Dto.DboCountryLanguage.Country
为什么级联没有提升它需要填充国家/地区外键?
答案 0 :(得分:0)
这里的技巧是必须在C#(实体)级别上明确设置关系数据库级别上清楚的内容。
我们确实对CountryLanguage有明确的定义
public CountryLanguageMapping()
{
...
References(x => x.Country,"COUNTRY_ID")
...
// here
.Not.Nullable();
所以,我们指示NHibernate:
只要有
CountryLanguage
个实例,请检查其引用Country
是否为空
但我们在代码中可以看到:
var language = ...
// create instance of CountryLanguage
var countryLanguage = new DboCountryLanguage
{
Language = language,
IsDefault = true,
LanguageCode = "en-GB"
};
// at this moment the countryLanguage.Country == null
// we (I really mean we, not NHibernate) did not set that value
// next we do create Country
var country = new DboCountry
{
Name = "UK",
Code = "GB",
Languages = new []{countryLanguage}
};
// and while we assigned that country will have some item in Languages collection
// the countryLanguage.Country is still null
// NHibernate cannot continue below
// because we did not fulfill the contract .Not.Nullable();
dataSession.Save(country);
这里的解决方案是明确设定两个方向(几乎是黄金法则)
var country = new DboCountry
{
Name = "UK",
Code = "GB",
Languages = new List<CountryLanguage>{countryLanguage},
};
countryLanguage.Country = country;
现在NHibernate有足够的信息可以正常进行
扩展。如果我们在C#中正确地执行了这两个关系,我们也可以使用反向设置来改进SQL INSERT UPDATE 处理:
Table("COUNTRIES")
Id(x => x.Id, "COUNTRY_ID").GeneratedBy.Identity();
HasMany(x => x.Languages)
.Cascade
.SaveUpdate()
.KeyColumn("COUNTRY_ID")
.Inverse() // here we go
;
这将指示NHibernate使用更高效的SQL语句。但同样需要在C#中正确分配关系的两个方面。稍后,当对象已经在DB中然后由NHibernate加载时,两个关系都将被设置。这是NHibernate的工作。但这发生在从DB读取
期间