none
What is the correct way to associate a Model to an Inherited Model? RRS feed

  • Question

  • I have the following Models:

    ModelBase.vb

    Public Class ModelBase
    Public Property ID as Integer
    Public Property CreatedAt as Date
    Public Property UpdatedAt as Date
    Public Property CreatedBy as Integer
    Public Property UpdatedBy as Integer

    End Class

    User.vb

    Public Class User
    Inherits ModelBase

    Public Property FirstName as String
    Public Property LastName as String
    Public Property GoesBy as String
    Public Property Title as String
    Public Property Code as String
    Public Property Birthdate as Nullable(Of Date)
    End Class

    School.vb

    Public Class School
    Inherits ModelBase

    Public Property Name as String
    Public Property Moniker as String
    Public Property City as String
    Public Property State as String
    End Class

    I'm unsure how I should build up the association between Users and the different models.  For the CreatedBy and UpdatedBy on the ModelBase class I want to have it link to a single user.  Do I create the association with ModelBase or the individual models?  What would it possibly look like?  Please let me know if I haven't presented enough information, thanks!

    Wednesday, July 30, 2014 4:15 PM

Answers

  • Here's one way.  Note in this model User's key is not StoreGenerated, as that confuses EF when saving users.

    Imports System.Data.Entity
    Imports System.ComponentModel.DataAnnotations
    
    Public Class ModelBase
        Public Property ID As Integer
        Public Property CreatedAt As Date
        Public Property UpdatedAt As Date
    
    
        Public Property CreatedBy As User
        Public Property CreatedById As Integer
    
        Public Property UpdatedBy As User
        Public Property UpdatedById As Integer
    
    End Class
    
    Public Class User
        Inherits ModelBase
    
        Public Property FirstName As String
        Public Property LastName As String
        Public Property GoesBy As String
        Public Property Title As String
        Public Property Code As String
        Public Property Birthdate As Nullable(Of Date)
    End Class
    
    Public Class School
        Inherits ModelBase
    
        Public Property Name As String
        Public Property Moniker As String
        Public Property City As String
        Public Property State As String
    End Class
    
    Public Class MyModel
        Inherits DbContext
    
        Public Property Users As DbSet(Of User)
        Public Property Schools As DbSet(Of School)
    
        Protected Overrides Sub OnModelCreating(modelBuilder As DbModelBuilder)
    
    
            modelBuilder.Entity(Of User).Property(Function(r) r.ID) _
                .HasDatabaseGeneratedOption(Schema.DatabaseGeneratedOption.None)
    
    
            modelBuilder.Entity(Of User).HasRequired(Function(r) r.CreatedBy) _
                .WithMany().HasForeignKey(Function(r) r.CreatedById).WillCascadeOnDelete(False)
            modelBuilder.Entity(Of User).HasRequired(Function(r) r.UpdatedBy) _
                .WithMany().HasForeignKey(Function(r) r.CreatedById).WillCascadeOnDelete(False)
    
    
            modelBuilder.Entity(Of School).HasRequired(Function(r) r.CreatedBy) _
                .WithMany().HasForeignKey(Function(r) r.CreatedById).WillCascadeOnDelete(False)
            modelBuilder.Entity(Of School).HasRequired(Function(r) r.UpdatedBy) _
                .WithMany().HasForeignKey(Function(r) r.CreatedById).WillCascadeOnDelete(False)
    
            MyBase.OnModelCreating(modelBuilder)
        End Sub
    
        Public Overrides Function SaveChanges() As Integer
    
            For Each e In Me.ChangeTracker.Entries
                If e.State = EntityState.Added Then
                    Dim mb = DirectCast(e.Entity, ModelBase)
                    mb.CreatedAt = DateTime.Now
                    mb.UpdatedAt = DateTime.Now
                End If
                If e.State = EntityState.Modified Then
                    Dim mb = DirectCast(e.Entity, ModelBase)
                    mb.UpdatedAt = DateTime.Now
                End If
    
            Next
            Return MyBase.SaveChanges()
        End Function
    
    
    End Class
    
    
    Module Module1
    
        Sub Main()
    
            Dim db As New MyModel
            Dim user As New User
            user.ID = 0
            user.CreatedById = 0
            user.UpdatedById = 0
            db.Users.Add(user)
            db.SaveChanges()
    
            Dim s As New School
            s.CreatedBy = user
            s.UpdatedBy = user
            db.Schools.Add(s)
            db.SaveChanges()
    
    
        End Sub
    
    End Module
    


    David http://blogs.msdn.com/b/dbrowne/

    • Proposed as answer by Fred BaoModerator Thursday, July 31, 2014 3:47 AM
    • Marked as answer by moopasta Thursday, July 31, 2014 7:49 PM
    Wednesday, July 30, 2014 5:58 PM

All replies

  • Just omit ModelBase from your DbContext, and you'll have inheritance and code reuse in VB, and SQL Server will store all the ModelBase properties for each table.

    Like this:

    Imports System.Data.Entity
    
    Public Class ModelBase
        Public Property ID As Integer
        Public Property CreatedAt As Date
        Public Property UpdatedAt As Date
        Public Property CreatedBy As Integer
        Public Property UpdatedBy As Integer
    
    End Class
    
    Public Class User
        Inherits ModelBase
    
        Public Property FirstName As String
        Public Property LastName As String
        Public Property GoesBy As String
        Public Property Title As String
        Public Property Code As String
        Public Property Birthdate As Nullable(Of Date)
    End Class
    
    Public Class School
        Inherits ModelBase
    
        Public Property Name As String
        Public Property Moniker As String
        Public Property City As String
        Public Property State As String
    End Class
    
    Public Class MyContect
        Inherits DbContext
    
        Public Property Users As DbSet(Of User)
        Public Property Schools As DbSet(Of School)
    
    End Class

    Adding ModelBase to your DbContext won't work in the general case as the key structure would have to be the same for all the subtypes, and navigation properties can get tricky.  And having ModelBase in your database isn't really very useful.

    David

    David http://blogs.msdn.com/b/dbrowne/

    Wednesday, July 30, 2014 5:01 PM
  • Yes I'm sorry, forgot to include that. That is how I have it set up currently.  My question is how can I associated the model User with the CreatedBy and UpdatedBy attributes that all the models that inherit from ModelBase will have?  
    Wednesday, July 30, 2014 5:09 PM
  • Here's one way.  Note in this model User's key is not StoreGenerated, as that confuses EF when saving users.

    Imports System.Data.Entity
    Imports System.ComponentModel.DataAnnotations
    
    Public Class ModelBase
        Public Property ID As Integer
        Public Property CreatedAt As Date
        Public Property UpdatedAt As Date
    
    
        Public Property CreatedBy As User
        Public Property CreatedById As Integer
    
        Public Property UpdatedBy As User
        Public Property UpdatedById As Integer
    
    End Class
    
    Public Class User
        Inherits ModelBase
    
        Public Property FirstName As String
        Public Property LastName As String
        Public Property GoesBy As String
        Public Property Title As String
        Public Property Code As String
        Public Property Birthdate As Nullable(Of Date)
    End Class
    
    Public Class School
        Inherits ModelBase
    
        Public Property Name As String
        Public Property Moniker As String
        Public Property City As String
        Public Property State As String
    End Class
    
    Public Class MyModel
        Inherits DbContext
    
        Public Property Users As DbSet(Of User)
        Public Property Schools As DbSet(Of School)
    
        Protected Overrides Sub OnModelCreating(modelBuilder As DbModelBuilder)
    
    
            modelBuilder.Entity(Of User).Property(Function(r) r.ID) _
                .HasDatabaseGeneratedOption(Schema.DatabaseGeneratedOption.None)
    
    
            modelBuilder.Entity(Of User).HasRequired(Function(r) r.CreatedBy) _
                .WithMany().HasForeignKey(Function(r) r.CreatedById).WillCascadeOnDelete(False)
            modelBuilder.Entity(Of User).HasRequired(Function(r) r.UpdatedBy) _
                .WithMany().HasForeignKey(Function(r) r.CreatedById).WillCascadeOnDelete(False)
    
    
            modelBuilder.Entity(Of School).HasRequired(Function(r) r.CreatedBy) _
                .WithMany().HasForeignKey(Function(r) r.CreatedById).WillCascadeOnDelete(False)
            modelBuilder.Entity(Of School).HasRequired(Function(r) r.UpdatedBy) _
                .WithMany().HasForeignKey(Function(r) r.CreatedById).WillCascadeOnDelete(False)
    
            MyBase.OnModelCreating(modelBuilder)
        End Sub
    
        Public Overrides Function SaveChanges() As Integer
    
            For Each e In Me.ChangeTracker.Entries
                If e.State = EntityState.Added Then
                    Dim mb = DirectCast(e.Entity, ModelBase)
                    mb.CreatedAt = DateTime.Now
                    mb.UpdatedAt = DateTime.Now
                End If
                If e.State = EntityState.Modified Then
                    Dim mb = DirectCast(e.Entity, ModelBase)
                    mb.UpdatedAt = DateTime.Now
                End If
    
            Next
            Return MyBase.SaveChanges()
        End Function
    
    
    End Class
    
    
    Module Module1
    
        Sub Main()
    
            Dim db As New MyModel
            Dim user As New User
            user.ID = 0
            user.CreatedById = 0
            user.UpdatedById = 0
            db.Users.Add(user)
            db.SaveChanges()
    
            Dim s As New School
            s.CreatedBy = user
            s.UpdatedBy = user
            db.Schools.Add(s)
            db.SaveChanges()
    
    
        End Sub
    
    End Module
    


    David http://blogs.msdn.com/b/dbrowne/

    • Proposed as answer by Fred BaoModerator Thursday, July 31, 2014 3:47 AM
    • Marked as answer by moopasta Thursday, July 31, 2014 7:49 PM
    Wednesday, July 30, 2014 5:58 PM
  • Thanks David! Very informative.



    • Edited by moopasta Thursday, July 31, 2014 7:49 PM
    Thursday, July 31, 2014 1:16 PM
  • Is the following section correct?  If the School can only have one User as the CreatedBy and/or UpdatedBy?  I was unsure because it uses .WithMany()

    modelBuilder.Entity(Of School).HasRequired(Function(r) r.CreatedBy) _
                .WithMany().HasForeignKey(Function(r) r.CreatedById).WillCascadeOnDelete(False)
            modelBuilder.Entity(Of School).HasRequired(Function(r) r.UpdatedBy) _
                .WithMany().HasForeignKey(Function(r) r.CreatedById).WillCascadeOnDelete(False)


    Thursday, July 31, 2014 9:06 PM
  • WithMany() specifies a 1-many relationship. Check the generated database to be sure the Foreign Keys are correct.

    David


    David http://blogs.msdn.com/b/dbrowne/

    Thursday, July 31, 2014 9:12 PM
  • They seem to map out correctly and contain the proper values in the tables.

    Foreign Key Base Table: Schools
    Foreign Key Columns: UpdatedById
    Primary/Unique Key Base: Users
    Primary/Unique Key Columns: ID

    Yet when I go to access the relationship, `item.CreatedBy` I get the error "Object reference not set to an instance of an object."

    Thursday, July 31, 2014 9:27 PM
  • Probably not loaded. Mark the navigation property as Overridable to enable lazy loading or load it eagerly or imperatively.

    David

    David http://blogs.msdn.com/b/dbrowne/

    Thursday, July 31, 2014 10:26 PM
  • That was it thanks changed the following from the code above if anyone else runs across this. Thanks again!

    Public Overridable Property CreatedBy As User
    Public Overridable Property UpdatedBy As User

    Thursday, July 31, 2014 10:46 PM