5

I am working on a project using an EF 4.2 code first model. This model contains a TPH inheritance structure for products. I need to group the polymorphic results of this inheritance model on the discriminator and am running into some issues.

The entity framework does not expose the discriminator to complete this grouping. My first question is can I get direct access to this discriminator? My reading and experience is telling me no, so I came up with this solution that sort of works. It is not performing well and I am not happy with how it will need to be maintained.

My classes look something like this (simplified):

Public MustInherit Class Product
      <key()>  
      Public Property ProductID as integer

      <StringLength(50, ErrorMessage:="Max 50 characters")>
      <Required(ErrorMessage:="Product name is required")>
      Public Property Name as String

      <TimeStamp()>
      Public Property Time_Stamp as DateTime = DateTime.Now()
End Class

Public Class Desktop
      Inherits Product

      <StringLength(50, ErrorMessage:="Max 50 characters")>
      <Required(ErrorMessage:="Processor is required")>
      Public Property Processor as String
End Class

Public Class Monitor
      Inherits Product

      <Required(ErrorMessage:="Monitor size is required")>
      Public Property Size_Inches as Integer
End Class

I built an extension method that takes a product and returns it's basetype name as a string.

<Extension()>
Public Function ProductType(ByVal inProduct as Product) as String
      ProductType = inProduct.GetType().BaseType.Name
End Function

With that, I built this structure to group the results of product by type so I can run through them:

Dim tmpProducts = db.Products.ToList()
Dim GrpProducts = tmpProducts.GroupBy(Function(prod) prod.ProductType) _
                             .Select(Function(s) New With {.ProductType = S.Key,
                                                           .Products = S })

I can now loop through the list to get the behavior I want, but the performance is not ideal and I am concerned it will be unacceptable as the number of products grows.

For Each ProductGroup in GrpProducts
       Dim TypeName as String = ProductGroup.ProductType
       Dim TypeProducts = ProductGroup.Products
Next

Also, this can give me easy access to shared properties (Name) but now I don't have many options to cast these into their real type, maybe a select case around TypeName. . .

Any recommendations are appreciated, also please forgive any code errors above, I retyped the examples from memory as I don't have access to the project at the moment.

sgrade1979
  • 51
  • 3

2 Answers2

2

A solution would be to model a bit more, and have a new entity ProductType having a property Name. Then you would have a simple 1-N relationship between Product and ProductType. I have not used EntityFramework, but with NHibernate you could easily make the framework always join that table on queries, so that it would not return a proxy for ProductType for each Product, which could harm performance.

As an add-on, in the future ProductType could develop other interesting properties (such as values that are common for every Product of that ProductType), so it adds flexibility to your solution, although it does have the immediate cost of adding another table to your database.

Victor Zakharov
  • 25,801
  • 18
  • 85
  • 151
  • This is certainly an option, though I feel common properties belong on the type definition when possible. Lately I have been adding an overridable "ProductTypeName" property to the base class. Feels cludgy and repeats stored data but it does perform better. – sgrade1979 Oct 11 '13 at 20:43
2

Following Linq query should get you a way to solve group by discriminator

from a in db.Records
group a.ID by new
{
    Name= a is Audio ? "Audio" :
            a is Video ? "Video" :
            a is Picture ? "Picture" :
            a is Document ? "Document" : "File"
} into g
select new 
{
    Name = g.Key.Name,
    Total = g.Count()
}
Anuj Pandey
  • 938
  • 2
  • 11
  • 30