findulov / EntityFrameworkCore.TemporalTables

Extension library which allows developers to easily use temporal tables in SQL Server using Entity Framework Core.
MIT License
63 stars 19 forks source link

Views and null table name #30

Open awjacobson opened 3 years ago

awjacobson commented 3 years ago

I am looking for an example or help on using views along side temporal tables. My current implementation results in an exception when updating the database.

I have used PreventTemporalTables so that only specific tables are made temporal:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);
    modelBuilder.PreventTemporalTables();
    modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
    SeedDb.Seed(modelBuilder);
}

I have configured my views as follows:

public class OtherEntityStatesConfiguration : IEntityTypeConfiguration<OtherEntityState>
{
    public void Configure(EntityTypeBuilder<OtherEntityState> builder)
    {
        builder.HasNoKey();
        builder.ToView("View_OtherEntityStates");
    }
}

When I execute dotnet ef database update then I receive this exception:

System.ArgumentNullException: Value cannot be null. (Parameter 'Table name not initialized')
   at EntityFrameworkCore.TemporalTables.Sql.Generation.BaseTemporalTableSqlGenerator..ctor(String tableName, String schemaName)
   at EntityFrameworkCore.TemporalTables.Sql.Generation.NoSqlTemporalTableGenerator..ctor(String tableName, String schemaName)
   at EntityFrameworkCore.TemporalTables.Sql.Factory.TemporalTableSqlGeneratorFactory.CreateInstance(Boolean isEntityConfigurationTemporal, Boolean isEntityTemporalInDatabase, String tableName, String schemaName)
   at EntityFrameworkCore.TemporalTables.Sql.TemporalTableSqlBuilder`1.BuildTemporalTableSqlFromEntityTypeConfiguration(IEntityType entityType, Boolean appendSeparator)
   at EntityFrameworkCore.TemporalTables.Sql.TemporalTableSqlBuilder`1.BuildTemporalTablesSqlForEntityTypes(IEnumerable`1 entityTypes, Boolean appendSeparator)
   at EntityFrameworkCore.TemporalTables.Sql.TemporalTableSqlBuilder`1.BuildTemporalTablesSql(Boolean appendSeparator)
   at EntityFrameworkCore.TemporalTables.Sql.TemporalTableSqlExecutor`1.Execute()
   at EntityFrameworkCore.TemporalTables.Migrations.TemporalTableMigrator`1.Migrate(String targetMigration)
   at EntityFrameworkCore.TemporalTables.Migrations.TemporalTableMigratorResolver.Migrate(String targetMigration)
   at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.UpdateDatabase(String targetMigration, String connectionString, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabaseImpl(String targetMigration, String connectionString, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabase.<>c__DisplayClass0_0.<.ctor>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)

I can see the null check in the constructor that generates the exception but I am not seeing a way to get around it:

public BaseTemporalTableSqlGenerator(string tableName, string schemaName = "dbo")
{
    this.tableName = tableName;
    this.schemaName = schemaName;

    if (string.IsNullOrWhiteSpace(tableName))
    {
        throw new ArgumentNullException("Table name not initialized");
    }
awjacobson commented 3 years ago

I bypassed this logic when the table name is null and it seems to have fixed my issue on the surface. I do not know if this breaks something else???

private string BuildTemporalTableSqlFromEntityTypeConfiguration(IEntityType entityType, bool appendSeparator)
{
    StringBuilder sqlBuilder = new StringBuilder();

    var relationalEntityType = context.Model.FindEntityType(entityType.Name);
    if (relationalEntityType is IEntityType)
    {

      string tableName = relationalEntityType.GetTableName();
      if (tableName is not null) // the above returns null if not mapped to a table (views)
      {
            string schema = relationalEntityType.GetSchema() ?? "dbo";

            bool isEntityConfigurationTemporal = TemporalEntitiesCache.IsEntityConfigurationTemporal(entityType);
            bool isEntityTemporalInDatabase = tableHelper.IsTableTemporal(tableName, schema);

            ITemporalTableSqlGenerator temporalTableSqlGenerator = temporalTableSqlGeneratorFactory
                .CreateInstance(isEntityConfigurationTemporal, isEntityTemporalInDatabase, tableName, schema);

            string temporalTableSql = temporalTableSqlGenerator.Generate();

            if (!string.IsNullOrWhiteSpace(temporalTableSql))
            {
                sqlBuilder.AppendLine(temporalTableSql);

                if (appendSeparator)
                {
                    sqlBuilder.AppendLine(new string('-', 100));
                }
            }
        }
    }
    return sqlBuilder.ToString();
}
PeteCozens commented 3 years ago

I've been having the same issue with views and independently came to the same conclusion.

This doesn't appear to break anything else - any chance of getting the change added to the master?

awjacobson commented 3 years ago

I've been having the same issue with views and independently came to the same conclusion.

This doesn't appear to break anything else - any chance of getting the change added to the master?

Hi Pete, you are in luck! The master branch has been updated

tb1b commented 3 years ago

I came across the same issue. Any time soon it's available in nuget?

PeteCozens commented 3 years ago

I came across the same issue. Any time soon it's available in nuget?

I just downloaded the code and added the project to my solution so as I can continue work on my project. Once the NuGet's updated I'll use that instead.

PeteCozens commented 3 years ago

Any chance of getting an updated version of this package published to NuGet?