级联多种关系

时间:2014-06-12 14:43:36

标签: c# nhibernate relationships cascading

我有一个相当常见的场景,我有两张桌子,我想联系在一起,特别是国家和语言。

我已经指定了这样的映射:

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为什么级联没有提升它需要填充国家/地区外键?

1 个答案:

答案 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读取

期间