Vertical Inheritance and the discriminator column - Using Code only

Thread is closed for posting
6 posts, 1 answers
  1. Gary
    Gary avatar
    28 posts
    Member since:
    Jun 2015

    Posted 14 Sep 2015 Link to this post

    Vertical Inheritance and the discriminator column - Using Code only

    I am trying to map a vertical inheritance between a base class and derived class (obviously). I am using code-only and the FluentAPI approach for which I have found very little documentation. I have found a couple of docs on vertical inheritance and code-only but very few on managing the discriminator column/value.

    S​o ​I have been trying to extrapolate how to do it from a combination of this blog post and some documentation on implementing vertical inheritance using code-only. All to no avail. 

     I am also providing a link to my DB Diagram (easier than typing in all that code and easier to read). 

    You will see that I have a "Deliverables" base table and "PrintDeliverables" derives from that. There will be other "Deliverable" derivatives coming down the road. But I figured I would start with one first.

    Anyway, I naturally have models that map to the tables.

    01.public class PrintDeliverable : BDeliverableBase
    03.    public String PaperItemNumber { get; set; }
    05.    public String PrinterModel { get; set; }
    07.    public Boolean? ColorOption { get; set; }
    09.    public String ProductCode { get; set; }
    13.public class BDeliverableBase : BModelBase, IDeliverable, ISingleID
    15.    public Int64 ID { get; set; }
    17.    public String Label { get; set; }
    19.    public String Description { get; set; }
    21.    public IList<DeliverableAttribute> Attributes { get; set; }
    23.    public Int64 TypeID { get; set; }
    24.    public DeliverableType Type { get; set; }
    28.public class DeliverableType : BModelBase, ISingleID
    31.    public Int64 ID { get; set; }
    33.    public String Label { get; set; }
    35.    public String Description { get; set; }
    37.    public IList<BDeliverableBase> Deliverables { get; set; }

    I have the standard mapping which maps the fields and types, sizes, etc. When I run it with no further additions I get an Error "Invalid Column name voa_class". My research uncovered that the ORM is attempting to update a "discriminator" column with a value that will tie the base table and associated table with the derived data together. So I learned that I can change the name of the column it uses which I did, in the BASE CLASS mapping (BDeliverableBase). I changed it to use the "DeliverableTypeId" column. Since the DeliverableType indicates which TYPE of deliverable it is, each type will have a separate associated table to keep it's type-specific information in, but derived from the Deliverables table.

       MappingConfiguration<BDeliverableBase> map = new MappingConfiguration<BDeliverableBase>();

    It appears to like this better but it wants to insert this crazy number (ex// 612274703-854) into the "DeliverableTypeId" column which, of course, being a foreign key to the DeliverableTypes table is not allowed. 

    Insert of '612274703-' failed: Telerik.OpenAccess.RT.sql.SQLException: The INSERT statement conflicted with the FOREIGN KEY constraint "FK_DeliverableType". The conflict occurred in database "DB1", table "dbo.DeliverableTypes", column 'DeliverableTypeId'

    I continued my research to learn that OpenAccess/DataAccess generates a hash value to insert into the discriminator column. I do not want this, in fact I know that the value will must one of the ID values available in the DeliverableType. So I read in one of the docs that you could also define what value to assign to the discriminator. The example applied a hard-coded value to the base class (dog and cat derived from animal) ...


     This presented one problem ... I do not have a single value I can hard-code. It could one of MANY values present in the DeliverableType table. However, for the sake of proving out the concept I hard-coded the value of an existing record in the base class mapping.

       MappingConfiguration<BDeliverableBase> map = new MappingConfiguration<BDeliverableBase>();

    I continued to get the identical error as before. So it doesn't appear that it was applying my hard-coded value. So ... I thought, while hard-coding the value is a little hacky it would make more sense to define that value in the mapping for the derived class. That would resolve my hard-coded issue since ALL instances of that derived class WOULD indeed be of the same DeliverableTypeId. So I tried ... 

       MappingConfiguration<BDeliverableBase> map = new MappingConfiguration<BDeliverableBase>();
       MappingConfiguration<PrintDeliverable> map = new MappingConfiguration<PrintDeliverable>();

    This resulted in the Error

    Insert of '612274703-857' failed: Telerik.OpenAccess.RT.sql.SQLException: String or binary data would be truncated.

    So I got a different error but still the same poblem. This time it was trying to stuff the ORM generated discriminator value (instead of my 819) into what I am assuming is my defined discriminator column (DeliverableTypeId). The different error makes me suspicious that it was targeting a different column.

    In an effort to not drag this out too long, suffice to say I have tried several combinations of where to these "HasDiscriminator" and "HasDiscriminatorValue" assignments go but always end up with one or the other of these errors. So the question is ...

    How, using code-only, do I map Vertical Inheritance using multiple, existing "type" values?


    Thanks in advance!

  2. Answer
    Ralph Waldenmaier
    Ralph Waldenmaier avatar
    202 posts

    Posted 15 Sep 2015 Link to this post

    Hi gstenstrom,
    Thank you for contacting us.
    Unfortunately, it is not possible to use a lookup table to check which discriminator to assign to a certain derived type. The only way is the one you already figured out, using the HasDiscriminatorValue method.
    Attached you can find an example showing how to use this using the fluent mapping approach following your example.

    The string truncation error message usually occurs when a string in your persistent object is longer than the size of the column in your database. Can you in your test ensure that you are not exceeding these limitations?

    I hope this information is helpful for you.
    Do come back in case you need further help.

    Ralph Waldenmaier
    Check out the latest announcement about Telerik Data Access vNext as a powerful framework able to solve core development problems.
  3. Gary
    Gary avatar
    28 posts
    Member since:
    Jun 2015

    Posted 15 Sep 2015 in reply to Ralph Waldenmaier Link to this post

    The problem I am having is that even though I am specifying the Discriminator value ..

       MappingConfiguration<PrintDeliverable> map = new MappingConfiguration<PrintDeliverable>();

    I am STILL getting the error ... 

       Insert of '612274703-857' failed: Telerik.OpenAccess.RT.sql.SQLException: String or binary data would be truncated.

    Since I am specifying 819 as the discriminator value ​I do not understand why it is even attempting to insert the hash value. Any ideas?




  4. Gary
    Gary avatar
    28 posts
    Member since:
    Jun 2015

    Posted 15 Sep 2015 in reply to Gary Link to this post

    Here's a little more of how the actual code is written ... for your reference. I cannot see what is different between this and the example you sent. 


    001.public partial class CatalogAdminDbCtxtMetadataSource : FluentMetadataSource
    003.    protected override IList<MappingConfiguration> PrepareMapping()
    004.    {
    005.        List<MappingConfiguration> configurations = new List<MappingConfiguration>();
    007.        Mapping.DeliverableBaseMapper delivBaseMapper = new DeliverableBaseMapper();
    008.        MappingConfiguration<BDeliverableBase> delivBaseMap = delivBaseMapper.Map();
    010.        Mapping.PrintDeliverableMapper printMapper = new PrintDeliverableMapper();
    011.        MappingConfiguration<PrintDeliverable> printMap = printMapper.Map();
    014.        configurations.Add(delivBaseMap);
    015.        configurations.Add(printMap);
    017.        return configurations;
    018.    }
    022.public class PrintDeliverableMapper
    024.    public MappingConfiguration<PrintDeliverable> Map()
    025.    {
    026.        MappingConfiguration<PrintDeliverable> map = new MappingConfiguration<PrintDeliverable>();
    027.        map.MapType(model => new
    028.        {
    029.            PaperItemNumber = model.PaperItemNumber,
    030.            PrinterModel    = model.PrinterModel,
    031.            ColorOption     = model.ColorOption,
    032.            PrintCode       = model.ProductCode
    034.        })
    035.        .Inheritance(Telerik.OpenAccess.InheritanceStrategy.Vertical)
    036.        .ToTable("PrintDeliverables");
    037.        //
    038.        //// Identify the column which defines whihc TYPE this is (i.e. discriminator)
    039.        map.HasDiscriminatorValue("819");
    040.        //
    041.        // Identify contraints on fields
    042.        map.HasProperty(c => c.PaperItemNumber).WithVariableLength(25); // nvarchar(25)
    043.        map.HasProperty(c => c.PrinterModel).WithVariableLength(100);   // nvarchar(100)
    044.        map.HasProperty(c => c.ProductCode).WithVariableLength(50);     // nvarchar(50)
    046.        return map;
    047.    }
    050.public class DeliverableBaseMapper
    052.    public MappingConfiguration<BDeliverableBase> Map()
    053.    {
    054.        MappingConfiguration<BDeliverableBase> map = new MappingConfiguration<BDeliverableBase>();
    055.        map.MapType(model => new
    056.        {
    057.            DeliverableId           = model.ID,
    058.            DeliverableLabel        = model.Label,
    059.            DeliverableDescription  = model.Description,
    060.            DeliverableTypeId       = model.TypeID,
    061.            IsEnabled               = model.IsEnabled
    063.        }).ToTable("Deliverables");
    064.        //
    065.        // Identify the column which defines which TYPE this is (i.e. discriminator)
    066.        map.HasDiscriminator().ToColumn("DeliverableTypeId");
    068.        #region map columns
    070.        map.HasProperty(c => c.ID)
    071.            .HasColumnType("bigint")
    072.            .IsNotNullable()
    073.            .IsIdentity(KeyGenerator.Autoinc);
    075.        map.HasProperty(c => c.Label)
    076.            .HasColumnType("nvarchar"
    077.            .WithVariableLength(100)
    078.            .IsNotNullable();
    080.        map.HasProperty(c => c.Description)
    081.            .HasColumnType("nvarchar")
    082.            .WithInfiniteLength()
    083.            .IsNullable();
    085.        map.HasProperty(c => c.TypeID)
    086.            .HasColumnType("bigint")
    087.            .IsNotNullable();
    089.        map.HasProperty(c => c.IsEnabled)
    090.            .HasColumnType("bit")
    091.            .IsNotNullable();
    093.        #endregion map columns
    095.        #region Navigation Properties
    097.        map.HasAssociation(d => d.Type)
    098.            .WithOpposite(t => t.Deliverables)
    099.            .HasConstraint((d, t) => d.TypeID == t.ID)
    100.            .IsManaged(true);
    102.        #endregion Navigation Properties
    104.        return map;
    105.    }


  5. Gary
    Gary avatar
    28 posts
    Member since:
    Jun 2015

    Posted 15 Sep 2015 in reply to Ralph Waldenmaier Link to this post

    It appears that the String truncation problem was, as you suggested, a field length issue unrelated to the Discriminator. However the error confused me (and still does) as the value it suggested it was trying to put in the field was not among the values being inserted. Therefore, I assumed that it was generated and related to the Discriminator issue. I still do not know where it was coming from but after some SQL Profiling I was able see the values being passed in and discovered the problem.


    Thanks for all the help!

  6. Ralph Waldenmaier
    Ralph Waldenmaier avatar
    202 posts

    Posted 16 Sep 2015 Link to this post

    Hi gstenstrom,
    I am glad that you were able to fix the previously reported issues.

    Do no hesitate to contact us again in case you need further assistance.

    Ralph Waldenmaier
    Check out the latest announcement about Telerik Data Access vNext as a powerful framework able to solve core development problems.
Back to Top