NHibernate 通过代码 ManyToOne 与 CompositeIdentity 进行映射

Posted

技术标签:

【中文标题】NHibernate 通过代码 ManyToOne 与 CompositeIdentity 进行映射【英文标题】:NHibernate mapping by code ManyToOne with CompositeIdentity 【发布时间】:2013-06-24 08:52:21 【问题描述】:

我正在尝试使用 NHibernate 3.3.3 将我的 FluentNHibernate 映射转换为 NHibernate Mapping By-code。目标是升级到 NHibernate 3.3.3 并减少分发的程序集的数量。我在将 FluentNHibernate 的引用映射转换为多对一映射时遇到了一些问题。

我的许多实体都有需要翻译的描述。为此,我使用了一个文本表,其中包含所有可用语言的这些文本。我使用文本 ID 来引用文本表,然后在数据访问对象中过滤所需的语言。这项工作使用 NHibernate 3.1 和 FluentNHibernate 创建,使用 NHibernate 3.3.3 和映射代码但是我只是一个 MappingException 说:属性映射的列数错误:Category.Description 类型:Text。

我的新映射哪里出错了?或者这种类型的映射在 NHibernate 3.3.3 中是不可能的。

这是文本表(SQL-server 2008)。

CREATE TABLE Texts (
    ID         int           NOT NULL,
    languageID nvarchar(10)  NOT NULL,
    Singular   nvarchar(max) NOT NULL,
    Plural     nvarchar(max) NULL,
CONSTRAINT PK_Texts PRIMARY KEY CLUSTERED (ID ASC, languageID ASC)
WITH (PAD_INDEX              = OFF,
      STATISTICS_NORECOMPUTE = OFF,
      IGNORE_DUP_KEY         = OFF,
      ALLOW_ROW_LOCKS        = ON,
      ALLOW_PAGE_LOCKS       = ON) ON [PRIMARY]) ON [PRIMARY]

文本类:

public class Text

    public Text(int id, string language, string singular, string plural)
    
        this.ID = new TextCompositeID(id, language);
        this.Singular = singular;
        this.Plural = plural;
    
    public TextCompositeID ID  get; private set; 
    public string Plural  get; private set; 
    public string Singular  get; set; 
    public override bool Equals(object obj)
    
        var text = (Text)obj;
        if (text == null)
        
            return false;
        
        return this.ID.Equals(text.ID);
    
    public override int GetHashCode()
    
        return this.ID.GetHashCode();
    

这里以 Category 类为例:

public class Category

    public int ID  get; set; 
    public Text Description  get; set; 

Category 类的 FluentNHibernate xml 映射如下所示:

<class xmlns="urn:nhibernate-mapping-2.2"
       mutable="true"
       name="Category"
       lazy="false"
       table="Category"
       where="IsObsolete=0">
    <id name="ID" type="System.Int32">
        <column name="ID" not-null="true" />
        <generator class="native" />
    </id>
    <many-to-one cascade="none"
                 class="Text"
                 name="Description">
        <column name="TextID"
                not-null="true"
                unique="false" />
    </many-to-one>
</class>

由此生成:

public class CategoryMap : ClassMap<Category>

    public CategoryMap()
    
        this.Table("Category");
        Not.LazyLoad();
        this.Where("IsObsolete=0");
        Id(x => x.ID)
            .Column("ID")
            .GeneratedBy.Native()
            .Not.Nullable();
        References(x => x.Description)
            .Column("DescriptionID")
            .Cascade.None()
            .Not.Unique()
            .Not.Nullable();
    

这是我创建的 NHibernate ClassMapping:

public CategoryMap()

    this.Lazy(false);
    this.Mutable(true);
    this.Table("Category");
    this.Where("IsObsolete=0");
    this.Id(
        x => x.ID,
        map =>
        
            map.Column("ID");
            map.Generator(Generators.Native);
        );
    this.ManyToOne(
        x => x.Description,
        map =>
        
            map.Cascade(Cascade.None);
            map.Class(typeof(Text));
            map.Column("TextID");
            map.Fetch(FetchKind.Join);
            map.Lazy(LazyRelation.NoLazy);
            map.ForeignKey("none");
        );

从这里我得到这个 xml 映射:

<class name="Category"
       lazy="false"
       table="Category"
       where="IsObsolete=0">
    <id name="ID"
        column="ID"
        type="Int32">
        <generator class="native" />
    </id>
    <many-to-one name="Description"
                 class="Text"
                 column="TextID"
                 fetch="join"
                 foreign-key="none"
                 lazy="false" />
</class>

【问题讨论】:

【参考方案1】:

我自己找到了答案。我不得不从 Text 类中删除复合 ID:

public class Text

    public Text(int id, string language, string singular, string plural)
    
        this.ID = id;
        this.LanguageID = language;
        this.Singular = singular;
        this.Plural = plural;
    
    public int ID  get; private set; 
    public string LanguageID  get; private set; 
    public string Plural  get; private set; 
    public string Singular  get; set; 
    public override bool Equals(object obj)
    
        var text = (Text)obj;
        if (text == null)
        
            return false;
        
        return this.ID.Equals(text.ID);
    
    public override int GetHashCode()
    
        return this.ID.GetHashCode();
    

类别映射已变为:

public CategoryMap()

    this.Lazy(false);
    this.Mutable(true);
    this.Table("Category");
    this.Where("IsObsolete=0");
    this.Id(
        x => x.ID,
        map =>
        
            map.Column("ID");
            map.Generator(Generators.Native);
        );
    this.ManyToOne(
        x => x.Description,
        map =>
        
            map.Column("TextID");
            map.Fetch(FetchKind.Join);
            map.ForeignKey("none");
            map.Lazy(LazyRelation.NoLazy);
        );

在数据访问对象中,旧的 QueryOver 查询现在可以让我得到所需的结果。

【讨论】:

以上是关于NHibernate 通过代码 ManyToOne 与 CompositeIdentity 进行映射的主要内容,如果未能解决你的问题,请参考以下文章

如何通过代码将 Id 映射到 NHibernate 映射中的私有支持字段?

通过 NHibernate 代码映射将 GUID 属性作为外键映射到其他实体

如何通过级联级联@ManyToOne 双向关系?

Hibernate ManyToOne Mappings 多对一关联映射

Hibernate - 自定义查询未通过 ManyToOne 单向关系的 child 参数找到实体

Nhibernate子类映射问题