When I have this entity type:
```C#
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
And configure it like this:
```C#
modelBuilder.Entity<User>().HasIndex(e => e.Name);
Then EF uses the "key or index" mapping for the string type, which results in this:
CREATE TABLE [Users] (
[Id] int NOT NULL IDENTITY,
[Name] nvarchar(450) NULL,
CONSTRAINT [PK_Users] PRIMARY KEY ([Id])
);
CREATE INDEX [IX_Users_Name] ON [Users] ([Name]);
Notice that nvarchar(450) is used.
However, if I do this:
```C#
[Index(nameof(User.Name))]
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
Then creating the database throws because Name is still using `nvarchar(max)`.
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (5ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
CREATE TABLE [Users] (
[Id] int NOT NULL IDENTITY,
[Name] nvarchar(max) NULL,
CONSTRAINT [PK_Users] PRIMARY KEY ([Id])
);
fail: Microsoft.EntityFrameworkCore.Database.Command[20102]
Failed executing DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
CREATE INDEX [IX_Users_Name] ON [Users] ([Name]);
Unhandled exception. Microsoft.Data.SqlClient.SqlException (0x80131904): Column 'Name' in table 'Users' is of a type that is invalid for use as a key column in an index.
at Microsoft.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action1 wrapCloseInAction)
at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action1 wrapCloseInAction)
at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at Microsoft.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at Microsoft.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean isAsync, Int32 timeout, Boolean asyncWrite)
at Microsoft.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource1 completion, Boolean sendToPipe, Int32 timeout, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry, String methodName)
at Microsoft.Data.SqlClient.SqlCommand.ExecuteNonQuery()
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteNonQuery(RelationalCommandParameterObject parameterObject)
at Microsoft.EntityFrameworkCore.Migrations.MigrationCommand.ExecuteNonQuery(IRelationalConnection connection, IReadOnlyDictionary2 parameterValues)
at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationCommandExecutor.ExecuteNonQuery(IEnumerable`1 migrationCommands, IRelationalConnection connection)
at Microsoft.EntityFrameworkCore.Storage.RelationalDatabaseCreator.CreateTables()
at Microsoft.EntityFrameworkCore.Storage.RelationalDatabaseCreator.EnsureCreated()
at Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade.EnsureCreated()
at Program.Main() in /home/ajcvickers/AllTogetherNow/Daily/Daily.cs:line 22
ClientConnectionId:ac59a862-d8f7-44f8-95be-c95a41b4b94d
Error Number:1919,State:1,Class:16
```
IndexAttributeConvention runs after TypeMappingConvention causing issue.
IndexAttributeConvention should be converted to an interactive convention by implementing IEntityTypeAddedConvention and IPropertyAddedConvention instead of IModelFinalizingConvention
IndexAttributeConventionshould be converted to an interactive convention by implementingIEntityTypeAddedConventionandIPropertyAddedConventioninstead ofIModelFinalizingConvention
@AndriySvyryd Can you explain more why this is necessary and what would go in these methods?
TypeMappingConvention. What test scenario am I missing?IndexAttributeConvention as an EntityTypeAttributeConventionBase which had a ProcessEntityTypeAdded() method (see this commit). But we moved to an IModelFinalizing convention because we had to check whether the strings passed in to the IndexAttribute corresponded to real properties and if they were not throwing was inappropriate before finalization. Why would that problem not come up again?@lajones There are two problems with IndexAttributeConvention or any convention running as IModelFinalizing:
C#
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
foreach (var index in entityType.GetDeclaredIndexes())
{
index.SetDatabaseName("My" + index.Name);
}
}
In ProcessEntityTypeAdded the convention will have to do nothing if there are missing properties. If the properties are added explicitly later then the index would be created in ProcessPropertyAdded. If the properties are still missing by when ProcessModelFinalizing runs then it will throw, but it won't mutate the model in any scenario.
So would the following be correct?
ProcessEntityTypeAdded and ProcessPropertyAdded which will both do what ProcessModelFinalizing does now except if the properties are in any way incorrect they will both just do nothing (no error, and no index created).ProcessModelFinalizing needs to remain - but only to do error checking - i.e. it does what it does now except will no longer create the index.@lajones Yes