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

Другие вопросы по тегам