Two Layer Vertical Inheritance - Discriminator values

6 posts, 1 answers
  1. Gary
    Gary avatar
    28 posts
    Member since:
    Jun 2015

    Posted 01 Oct 2015 Link to this post

    I'm trying to not go into too much detail but provide enough. If it helps I have a test project I can send you ... 

    In short what I am trying to do is create a 2 layer vertical inheritance. 

    Layer One

    I have defined a PRODUCT model/table mapping with a discriminator column

     

    var configuration = new  MappingConfiguration<ProductEntity>();
        configuration.HasProperty(x => x.ProductType)
            .HasFieldName("_productType")
            .WithDataAccessKind(DataAccessKind.ReadWrite)
            .ToColumn("DeliverableType")
            .IsNotNullable()
            .HasColumnType("nvarchar")
            .WithVariableLength(50);
        //
        // Identify the discriminator property
        configuration.HasDiscriminator().ToColumn("ProductType");

     
    "Deliverable" and "Package" model/tables are derived from this PRODUCT and mapped to define the value to be inserted into the discriminator field of the parent.

     

    var configuration = new  MappingConfiguration<ProductDeliverableEntity>();
        //
        // Identify the product type discriminator
        configuration.HasDiscriminatorValue("SINGLE");
         
    var configuration = new  MappingConfiguration<ProductDeliverablePackageEntity>();
        //
        // Identify the product type discriminator
        configuration.HasDiscriminatorValue("PKG");

     

    "Deliverable" is further extended by a specific ​TYPE of deliverable. In our example we have a PRINT deliverable and a VIDEO ​deliverable which derive from the ProductDeliverableEntity above.

     

    var configuration new MappingConfiguration<PrintProductDeliverableEntity> ();
        //
        // Identify the value to assign to the DeliverableType Discriminator column
        configuration.HasDiscriminatorValue("PRINT");
         
    var configuration new MappingConfiguration<VideoProductDeliverableEntity> ();
        //
        // Identify the value to assign to the Discriminator column
        configuration.HasDiscriminatorValue("VIDEO");

     
    In order to support th​is second tier of inheritance, I added the definition of it's own discriminator column to the ProductDeliverableEntity definition so that it could support the specific types of being derived. 

     

    var configuration = new  MappingConfiguration<ProductDeliverableEntity>();
        //
        // Identify the value to assign to the ProductType Discriminator column
        configuration.HasDiscriminatorValue("SINGLE");
     
        //
        // Identify the column which defines which TYPE this is (i.e. discriminator)
        configuration.HasDiscriminator().ToColumn("DeliverableType");

     

     When I attempt to add either of the specific Deliverable types (Video or Print) I am getting a Cannot insert a NULL value into the "DeliverableType" field. 

     

    //
    // Assemble
    DbContext ctx = new DbContext();
    VideoProductDeliverableEntity video = new VideoProductDeliverableEntity();
    PrintProductDeliverableEntity print = new PrintProductDeliverableEntity();
    ProductDeliverablePackageEntity pkg = new ProductDeliverablePackageEntity();
     
     
    video.ProductName   = "120 second greeting";
    video.ProductSku    = "VDO-120-MPG";
    video.VideoFormat   = "mpg";
    //video.DeliverableType = "TESTCODE";
     
    print.ProductName   = "Fringe Badge";
    print.ProductSku    = "BDG-FRINGE-40";
    print.Media         = "Badge";
    print.Printer = "DS40";
    //print.DeliverableType = "TESTCODE";
     
    pkg.Contents.Add(video);
    pkg.Contents.Add(print);
    pkg.ProductName = "Video print";
    pkg.ProductSku = "VDOPRNT-001";
     
     
    ctx.Add(video);
    ctx.Add(print);
    ctx.Add(pkg);
     
    ctx.SaveChanges();

     

    When I uncomment the lines where the DeliverableType is explicitly assigned, the test code completes running. What I have discovered however, is that the "VIDEO" and "PRINT" discriminator values are being inserted into the ProductType discriminator field instead of the DeliverableType, thus resulting in a NULL value being assigned to the DeliverableType (which is not allowed).

    The discriminator value of "SINGLE" is never used at all. I am not sure if there is an issue that causes the discriminator to be passed all the way to the lowest level instead of the "next" or first level that defines a discriminator. This is a fundamental piece of our architecture decision-making so any help would be greatly appreciated.

    I created a small test project that illustrates the issue if providing that would help I would be happy to forward that along. 

     

     

     

  2. Answer
    Thomas
    Admin
    Thomas avatar
    590 posts

    Posted 06 Oct 2015 Link to this post

    Hi Gary,

    reading through your description I understood:
    (a) You have a class hierarchy ProductEntity <- ProductDeliverableEntity <- PrintProductDeliverableEntity.
    (b) You defined two different discriminator columns.

    When a PrintProductDeliverableEntity is a ProductEntity, the discrimination needs to happen for the values in the ProductEntity table. There must not be two different discriminator columns in this one table hierarchy. DataAccess does not support such setups (the discriminator values could be contradictory), and the single discrimination value will be stored in the root table. DataAccess will know from your CLR class model which class extends which other class and infers the needed table relationship from CLR inheritance hierarchy. If you assign another table to hold that values for a particular class, the table will be used only for instances of this class.
    So I think the issue is with the second discriminator column that was defined, and I think removing it would resolve this issue.
    If it does not, or if I did not understand your setup correctly, please contact us again with the repro that you mentioned.

    Regards,
    Thomas
    Telerik
     
    Check out the latest announcement about Telerik Data Access vNext as a powerful framework able to solve core development problems.
  3. DevCraft banner
  4. Gary
    Gary avatar
    28 posts
    Member since:
    Jun 2015

    Posted 06 Oct 2015 in reply to Thomas Link to this post

    Thank you for your response and I think I understand your explanation. The ​scenario I am facing is that I need to be able to represent both a PackageEntity and a ProductDeliverableEntity as individual ProductEntities. If that was all there was to it, I would create a discriminator on the ProductEntity to distinguish whether the product was of a Package or Deliverable type .. no sweat. The problem comes up that the ProductDeliverableEntity could be of either Video or Print type. At the ProductEntity level we do not care whether the Product is a video or a print, just ​whether it is a Deliverable or a Package. But since I have already applied "discrimination" logic at the Deliverable level I am unable to apply it at the product level ... I have attached an image to illustrate what I am trying to do. It sounds like I will need to be a little more creative in how I resolve this issue.  <Diagram>

    Thank you!

  5. Thomas
    Admin
    Thomas avatar
    590 posts

    Posted 07 Oct 2015 Link to this post

    Hi Gary,

    I think I would not think in this hierarchy. IsA-kind relationships are represented with classes, but maybe a packaging is a property? Would that solve the issue? Again, we are not supporting more than one discriminator value per table hierarchy, thus class hierarchy.

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

    Posted 08 Oct 2015 in reply to Thomas Link to this post

    Thank you for your feedback. I am certainly open to any options, technical or architectural that would address my issue. I think, however that the "is-A" description is still appropriate here as an individual Deliverable "is a" product and a Package of Deliverables' also "is a" product. Having a many-to-many relationship makes using a property a little difficult. I think that the solution I have come upon, for now anyway, is to apply an "IProduct" interface to each type's class definition. I had to add a field to the Deliverable database table to support it but that's ok. This allows me to create a Data Transfer Object that represents ​the simple product interface regardless of whether they are a ​Package or Deliverable. 

    Since creating​, updating or deleting a product would not occur at this high of an abstracted level I don't really NEED the ORM to manage that relationship. 

    I think that that will work ... Thanks again!!

     

     

  7. Thomas
    Admin
    Thomas avatar
    590 posts

    Posted 13 Oct 2015 Link to this post

    Hi Gary,

    using an interface is a good solution to this problem I think. 

    Regards,
    Thomas
    Telerik
     
    Check out the latest announcement about Telerik Data Access vNext as a powerful framework able to solve core development problems.
Back to Top
DevCraft banner