EF Core: How to include a common column in all generated migrations?

50 views Asked by At

Note: an answer in either VB.NET or C# will be fine. I have no preference.

I'm using table-per-type architecture.

I want all of my entities to have a second key column (of type Guid), so I've created a base class like this:

Public MustInherit Class Entity
    Public Property Id As Integer
    Public Property Key As Guid
End Class

Public Class City
    Inherits Entity

    Public Property Name As Integer
    Public Property Code As String
End Class

I want each of these columns to self-populate at the database level (SQL Server) when a new row is added, so I've created a configuration like this:

Public Class EntityConfiguration
  Implements IEntityTypeConfiguration(Of Entity)

  Public Sub Configure(Builder As EntityTypeBuilder(Of Entity)) Implements IEntityTypeConfiguration(Of Entity).Configure
    Builder.Property(Function(Entity) Entity.Key).IsRequired.HasDefaultValueSql("NEWID()")
  End Sub
End Class

This works—the column is added correctly—but the migrations generator builds code for one table only, named Entities.

I tried this in my CityConfiguration class:

Builder.ToTable("Cities")

...but the generator still includes an Entity table. This is undesirable for obvious reasons. Entity is a base class only. And the Key column is omitted from the Cities table.

Is there a way to have all of my entities contain this common Key column without having to include an explicit call to HasDefaultValueSql("NEWID()") in each entity's configuration? That's going to be tedious and error-prone.

1

There are 1 answers

0
InteXX On BEST ANSWER

Well, shucks... that didn't take long.

A quick-and-dirty extension method gets us there just nicely:

Public Module Extensions
  <Extension>
  Public Sub AddKey(Of TEntity As Entity)(Builder As EntityTypeBuilder(Of TEntity))
    Builder.Property(Function(Entity) Entity.Key).IsRequired.HasDefaultValueSql("NEWID()")
    Builder.HasIndex(Function(Entity) Entity.Key).IsUnique()
  End Sub
End Module

And then we just call it like this in each entity's configuration:

Builder.AddKey

I'll leave this Q&A up in case anyone wants to use the idea.

--EDIT--

Possibly even cleaner would be to use a base class:

Public MustInherit Class BaseConfiguration(Of T As Entity)
  Public Overridable Sub Configure(Builder As EntityTypeBuilder(Of T))
    Builder.Property(Function(Entity) Entity.Key).IsRequired.HasDefaultValueSql("NEWID()")
    Builder.HasIndex(Function(Entity) Entity.Key).IsUnique()
  End Sub
End Class

Just make sure it doesn't implement IEntityTypeConfiguration(Of Entity), or an unwanted Entity table will be generated.

Use it like this:

Public Class CityConfiguration
  Inherits BaseConfiguration(Of City)
  Implements IEntityTypeConfiguration(Of City)

  Public Overrides Sub Configure(Builder As EntityTypeBuilder(Of City)) Implements IEntityTypeConfiguration(Of City).Configure
    Builder.Property(Function(City) City.Code).IsRequired.HasDefaultValue(String.Empty).HasMaxLength(7)
    Builder.Property(Function(City) City.Name).IsRequired.HasDefaultValue(String.Empty).HasMaxLength(25)

    Builder.HasIndex(Function(City) City.Code).IsUnique()
    Builder.HasIndex(Function(City) City.Name)

    MyBase.Configure(Builder)
  End Sub
End Class