Quantcast
Channel: Development With A Dot
Viewing all articles
Browse latest Browse all 404

Entity Framework Pitfalls – Concrete Table Inheritance and Identity Keys

$
0
0

When using the Concrete Table Inheritance / Table Per Concrete Type pattern for mapping entity inheritances, you cannot use IDENTITYs as primary keys. It is easy to understand why: because each entity of a concrete type is stored in its own table, and if these tables would be using IDENTITYs for generating the primary key, if we would issue a query on their base class looking for a record by its primary key, Entity Framework would generate lots of UNIONs, one for each table, where only one could possibly return a record.

For example, say we have this model:

image

A query such as:

   1: var tool = ctx.Tools.Where(x => x.ToolId == new Guid("32C1BACF-5814-48DD-95E7-31855C7849CB")).SingleOrDefault();

Would generate the following SQL:

   1:SELECT
   2:CASEWHEN ([UnionAll2].[C6] = 1) THEN'0X0X'WHEN ([UnionAll2].[C7] = 1) THEN'0X1X'ELSE'0X2X'ENDAS [C1], 
   3: [UnionAll2].[C1] AS [C2], 
   4: [UnionAll2].[C2] AS [C3], 
   5:CASEWHEN ([UnionAll2].[C6] = 1) THEN [UnionAll2].[C3] WHEN ([UnionAll2].[C7] = 1) THENCAST(NULLASvarchar(1)) ENDAS [C4], 
   6:CASEWHEN ([UnionAll2].[C6] = 1) THENCAST(NULLASbit) WHEN ([UnionAll2].[C7] = 1) THEN [UnionAll2].[C4] ENDAS [C5], 
   7:CASEWHEN ([UnionAll2].[C6] = 1) THENCAST(NULLASbit) WHEN ([UnionAll2].[C7] = 1) THENCAST(NULLASbit) ELSE [UnionAll2].[C5] ENDAS [C6]
   8:FROM  (SELECT
   9:     [UnionAll1].[ToolId] AS [C1], 
  10:     [UnionAll1].[Name] AS [C2], 
  11:     [UnionAll1].[C1] AS [C3], 
  12:     [UnionAll1].[C2] AS [C4], 
  13:     [UnionAll1].[Automated] AS [C5], 
  14:     [UnionAll1].[C3] AS [C6], 
  15:     [UnionAll1].[C4] AS [C7]
  16:FROM  (SELECT
  17:         [Extent1].[ToolId] AS [ToolId], 
  18:         [Extent1].[Name] AS [Name], 
  19:CAST(NULLASvarchar(1)) AS [C1], 
  20:CAST(NULLASbit) AS [C2], 
  21:         [Extent1].[Automated] AS [Automated], 
  22:cast(0 asbit) AS [C3], 
  23:cast(0 asbit) AS [C4]
  24:FROM [dbo].[TestingTool] AS [Extent1]
  25:WHEREcast('32c1bacf-5814-48dd-95e7-31855c7849cb'as uniqueidentifier) = [Extent1].[ToolId]
  26:UNIONALL
  27:SELECT
  28:         [Extent2].[ToolId] AS [ToolId], 
  29:         [Extent2].[Name] AS [Name], 
  30:CAST(NULLASvarchar(1)) AS [C1], 
  31:         [Extent2].[CompatibleWithProject] AS [CompatibleWithProject], 
  32:CAST(NULLASbit) AS [C2], 
  33:cast(0 asbit) AS [C3], 
  34:cast(1 asbit) AS [C4]
  35:FROM [dbo].[ManagementTool] AS [Extent2]
  36:WHEREcast('32c1bacf-5814-48dd-95e7-31855c7849cb'as uniqueidentifier) = [Extent2].[ToolId]) AS [UnionAll1]
  37:UNIONALL
  38:SELECT
  39:     [Extent3].[ToolId] AS [ToolId], 
  40:     [Extent3].[Name] AS [Name], 
  41:     [Extent3].[Language] AS [Language], 
  42:CAST(NULLASbit) AS [C1], 
  43:CAST(NULLASbit) AS [C2], 
  44:cast(1 asbit) AS [C3], 
  45:cast(0 asbit) AS [C4]
  46:FROM [dbo].[DevelopmentTool] AS [Extent3]
  47:WHEREcast('32c1bacf-5814-48dd-95e7-31855c7849cb'as uniqueidentifier) = [Extent3].[ToolId]) AS [UnionAll2]

If all tables used IDENTITY, there was no way to make all primary keys unique to each table, at least, not in a easy way.

The solution is to use a different identifier generation algorithm, such as Guids:

   1:publicabstractclass Tool
   2: {
   3:public Tool()
   4:     {
   5:this.ToolId = Guid.NewGuid();
   6:     }
   7:  
   8:public String Name
   9:     {
  10:         get;
  11:         set;
  12:     }
  13:  
  14:public Guid ToolId
  15:     {
  16:         get;
  17:         set;
  18:     }
  19: }

For a complete mapping solution, please read this post.


Viewing all articles
Browse latest Browse all 404

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>