Ef Code first Index со свойством навигации без добавления идентификатора
У меня есть EF Code первого класса под названием UserTag.
UserTag имеет следующие два свойства
public string TagText { get; set; }
public UserGroup UserGroup { get; set; }
Теперь я хочу индекс, который делает так, чтобы TagText был уникальным в пределах UserGroup. Но один и тот же TagText должен существовать в двух разных группах пользователей.
У меня есть две проблемы, одна из которых заключается в том, что TagText не может быть varchar (max), которым он становится, и вторая, что у меня нет UserGroupId для подключения индекса.
Я хотел бы использовать Fluent API, но мог бы рассмотреть возможность изменения Up и Down моих миграций.
Но я бы не хотел добавлять UserGroupId и аннотации поверх TagText. Это возможно?
Лучше всего будет использовать Fluent API, но редактирование миграций тоже будет хорошо.
1 ответ
РЕДАКТИРОВАТЬ:
Я неправильно прочитал вашу проблему, оставлю свой исходный ответ внизу (я перечитал, что вы не хотите добавлять свойство ForeignKey к вашей модели).
Отредактированный ответ:
Это выполнимо, используя свободный API.
По сути, вам нужно настроить EF, как сопоставить свойство навигации (UserGroup) с базой данных (имя столбца внешнего ключа, которого нет в вашей модели) и разместить на нем атрибут / аннотацию индекса.
Чтобы решить проблему с длиной столбца TagText (по умолчанию это nvarchar(max) и, следовательно, не может быть частью индекса (слишком долго, вы получите исключение SQL)), вы также можете использовать свободный API и использовать .HasMaxLength (StringLengthThatMakesSense). Это также можно сделать, разместив аннотации в вашей модели (см. Мой старый ответ ниже).
modelBuilder.Entity<UserTag>()
.HasRequired(x => x.UserGroup)
.WithMany() // I assume a 1 : many relation
.Map(x =>
// map the Foreignkey to the default naming convention
x.MapKey("UserGroup_Id")
// add a Columnanotation, which you would place on the FK property, if you had one...
.HasColumnAnnotation("UserGroup_Id", "Index", new IndexAnnotation(new IndexAttribute("UI_UserGroupTagtext", 2)
{
IsUnique = true
})));
modelBuilder.Entity<UserTag>()
.Property(x => x.TagText)
// configure the length of the column in the database
.HasMaxLength(128)
// add the Unique index information - I used this column to be the first in the index
.HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("UI_UserGroupTagtext", 1)
{
IsUnique = true
}));
Это должно дать вам миграцию, которая выглядит примерно так:
CreateTable(
"dbo.UserTags",
c => new
{
Id = c.Int(nullable: false, identity: true),
TagText = c.String(maxLength: 128),
UserGroup_Id = c.Int(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.UserGroups", t => t.UserGroup_Id, cascadeDelete: true)
.Index(t => new { t.TagText, t.UserGroup_Id }, unique: true, name: "UI_UserGroupTagtext");
CreateTable(
"dbo.UserGroups",
c => new
{
Id = c.Int(nullable: false, identity: true),
})
.PrimaryKey(t => t.Id);
Старый ответ:
Есть несколько решений вашей проблемы.
Как вы уже упоминали, вы всегда можете сделать это при переносе базы данных и просто создать уникальный индекс, используя оба столбца (я думаю, что реляционная модель содержит UserGroupId).
Чтобы создать уникальный индекс с помощью аннотаций данных, вам необходимо расширить вашу модель следующим образом (для этого требуется EF 6.1):
public class UserTag {
[StringLength(128)]
[Required]
[Index("UI_UserGroupTagtext", 2, IsUnique = true)]
public string TagText { get; set; }
[Index("UI_UserGroupTagtext", 1, IsUnique = true)]
public int UserGroupId { get; set; }
public UserGroup UserGroup { get; set; }
}
Я раскрыл свойство ForeignKey (информация: я не уверен на 100%, требуется ли это, может быть, это также работает, если непосредственно поместить его в свойство навигатора). Это работает по соглашениям (называя свойство FK как навигационное свойство с суффиксом Id. Я предполагаю, что Id группы пользователей является целым числом). Вы можете прочитать больше об этих соглашениях на msdn (см. Ссылку внизу ответа)
Я также добавил атрибут StringLength, потому что в противном случае EF обычно создает столбец NVARCHAR(MAX), который не будет работать в SQL Server, поскольку он превышает максимальное значение. длина индекса (как вы уже упоминали в своем ответе). Это также заставит EF проверять длину свойства при вызове SaveChanges, не заходя в базу данных (но это можно отключить).
Другой вариант - сделать это через Fluent Api:
modelBuilder.Entity<UserTag>
.Property(t => t.TagText )
.HasMaxLength(128)
.IsRequired()
.HasUniqueIndexAnnotation("UI_UserGroupTagtext", 2);
modelBuilder.Entity<UserTag>
.Property(t => t.UserGroupId)
.HasUniqueIndexAnnotation("UI_UserGroupTagtext", 1);
В этом примере я также добавил информационную информацию MaxLength для TagText (и необходимую информацию, поскольку, по-видимому, это имеет смысл, по крайней мере, для меня...).
Вы можете прочитать больше о EF и индексировать на MSDN