Ответ 1
Краткий ответ: Entity Framework 6 не позволяет использовать несколько индексов с разными видами.
Длинный ответ: может быть невозможно сделать это напрямую, но это может быть достигнуто с помощью некоторых настроек. После долгих чтений я обнаружил, что было бы действительно сложно создать новый класс, который наследовал бы IndexAnnotation
и добавил бы свойство SortOrder
.
Самым простым способом, который я нашел для достижения этой цели, было посмотреть, какое существующее свойство можно настроить для сортировки по нескольким индексам. Использование свойства Name
может сделать это в виде строки. Вы можете добавить индекс сортировки непосредственно в имя и перехватить его позже при генерации кода SQL.
Итак, давайте предположим, что мне нужно индексировать свойства следующим образом:
- Тип (ASC)
- DateFor (Desc)
- DateCreated (Описание изделия)
Затем я назову свой индекс, за которым следует разделитель (:) и порядок сортировки. Это будет выглядеть так:
var indexName = "IX_Table:ASC,DESC,DESC";
Индекс с несколькими полями будет выглядеть так:
this.Property(t => t.Type)
.HasColumnAnnotation(
IndexAnnotation.AnnotationName,
new IndexAnnotation(new[]
{
new IndexAttribute(indexName) { Order = 1 }
}
)
);
this.Property(t => t.DateFor)
.HasColumnAnnotation(
IndexAnnotation.AnnotationName,
new IndexAnnotation(new[]
{
new IndexAttribute(indexName) { Order = 2 }
}
)
);
this.Property(t => t.DateCreated)
.HasColumnAnnotation(
IndexAnnotation.AnnotationName,
new IndexAnnotation(new[]
{
new IndexAttribute(indexName) { Order = 3 }
}
)
);
Теперь мы должны создать собственный класс генерации SQL, чтобы сгенерировать правильный код SQL для анализа нашего "подправленного" имени индекса:
public class CustomSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
protected override void Generate(CreateIndexOperation createIndexOperation)
{
using (var writer = Writer())
{
writer.Write("CREATE ");
if (createIndexOperation.IsUnique)
{
writer.Write("UNIQUE ");
}
if (createIndexOperation.IsClustered)
{
writer.Write("CLUSTERED ");
}
else
{
writer.Write("NONCLUSTERED ");
}
string name = createIndexOperation.Name;
string[] sorts = {};
if (createIndexOperation.Name.Contains(":"))
{
var parts = createIndexOperation.Name.Split(':');
if (parts.Length >= 1)
{
name = parts[0];
}
if (parts.Length >= 2)
{
sorts = parts[1].Split(',');
}
}
writer.Write("INDEX ");
writer.Write(Quote(name));
writer.Write(" ON ");
writer.Write(Name(createIndexOperation.Table));
writer.Write("(");
// Add the columns to the index with their respective sort order
string fields = "";
if (sorts.Length == 0 || sorts.Length == createIndexOperation.Columns.Count)
{
for (int i=0 ; i<createIndexOperation.Columns.Count ; i++)
{
string sort = "ASC";
if (sorts.Length == 0)
{
// Do nothing
}
else if (sorts[i] != "ASC" && sorts[i] != "DESC")
{
throw new Exception(string.Format("Expected sort for {0} is 'ASC' or 'DESC. Received: {1}", name, sorts[i]));
}
else
{
sort = sorts[i];
}
fields = fields + Quote(createIndexOperation.Columns[i]) + " " + sort + ",";
}
fields = fields.Substring(0, fields.Length - 1);
}
else
{
throw new Exception(string.Format("The sort (ASC/DEC) count is not equal to the number of fields in your Index ({0}).", name));
}
writer.Write(fields);
writer.Write(")");
Statement(writer);
}
}
}
Наконец, вы должны указать Entity Framework использовать новый метод, сгенерированный кодом, вместо метода по умолчанию, отредактировав файл Configuration.cs
:
internal sealed class MyConfiguration : DbMigrationsConfiguration<MyContext>
{
/// <summary>
/// Constructor
/// </summary>
public MyConfiguration()
{
// Other stuff here...
// Index/Unique custom generation (Ascending and Descending)
SetSqlGenerator("System.Data.SqlClient", new CustomSqlServerMigrationSqlGenerator());
}
}
Это. Возможно, это не самое чистое решение, но если вы генерируете свои сущности на лету (как я), вы сэкономите много времени и не забудете запустить свой сырой SQL.
Большое спасибо Роуэн Миллер и всем статьям в его блоге. Этот ответ был вдохновлен: Настройка провайдера первых миграций кода.