简体   繁体   中英

(Fluent) NHibernate table-per-hierarchy; Id is only unique along with discriminator

I have a legacy database mapping issue. The database is storing all of its lookup values (code/description) in one table, distinguished by a type code field. The tables referring to it do so with one column (the code, without the type code). The code table does not have a primary key constraint (!).

I have a class that looks like this:

public class Code
{
    [StringLength(8)]
    public virtual string CodeValue { get; set; }
    [StringLength(2000)]
    public virtual string Description { get; set; }
    public virtual long? OrderBy { get; set; }
    public virtual DateTime? StopDate { get; set; }
}

My initial mapping looked like this:

public class CodesMap : ClassMap<Code>
{
    public CodesMap()
    {
        Table("CODES");
        Id(x => x.CodeValue).Column("CODE_CODE").GeneratedBy.Assigned();
        Map(x => x.Description).Column("DESCRIPTION");
        Map(x => x.OrderBy).Column("ORDER_BY");

        DiscriminateSubClassesOnColumn("TYPE_CODE", "INVALID")
            .SqlType("VARCHAR2");
    }
}

And then there are a bunch of sub-classes that differ only in their discriminator values.

Another mapping might reference this as:

...
        References<FacilityType>(x => x.Type).Column("FACIL_TYPE_CODE").ReadOnly();
...

Now, this is all well and good, and everything works, since that reference knows the class, and therefore the discriminator value for the query, except...I only just hit the case where CODE_CODE is non-unique between two objects of different types (both subtypes of Code) in the same session. Oops.

CODE_CODE and TYPE_CODE are unique together, so the right thing ought to be to use them as a composite key. But then my References in the other class maps become impossible, because those tables only have a single column foreign key (obviously no FK constraint defined on table).

Short of adding a surrogate key on the code table, whatever shall I do?

In case, that we need to map look up values as readonly, solution would be surprisingly very easy. Instead of explicit inheritance, we will explicitly map each subclass. The discriminator will be moved to a WHERE clause:

public FacilityTypeMap()
{
    Table("CODES");

    // here we will use explicit runtime discriminator
    // injected by NHibernate into each SELECT .. FROM clause for this type
    Where(" TYPE_CODE = 'FACIL_TYPE' " )        

    Id(x => x.CodeValue).Column("CODE_CODE").GeneratedBy.Assigned();
    Map(x => x.Description).Column("DESCRIPTION");
    Map(x => x.OrderBy).Column("ORDER_BY");

    // no more inheritance
    // DiscriminateSubClassesOnColumn("TYPE_CODE", "INVALID")
    //    .SqlType("VARCHAR2");
}

This is very well working for SELECT. We just have to repeat that mapping for each Discriminator == each derived type.

see 5.1.3. class :

<class
    name="ClassName"                              (1)
    table="tableName"                             (2) 
    ...
    where="arbitrary sql where condition"         (11)

(11) where (optional) specify an arbitrary SQL WHERE condition to be used when retrieving objects of this class

In case, we need to use this class also for insert, we have to do few more steps. Explicitly map column 'TYPE_CODE' as eg Discriminator, and set it in constructor to correct value (eg 'FACIL_TYPE' ). It could be protected property mapped as .Not.Update()

string _discriminator = "FACIL_TYPE";
public virtual string Discriminator { get { return _discriminator; } protected set {} }
...
// mapping
Map(x => x.Discriminator, "TYPE_CODE").Not.Update()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM