Ef Code first index with navigation property,不添加Id

时间:2016-09-29 18:19:40

标签: c# entity-framework ef-migrations ef-fluent-api

我有一个名为UserTag的EF Code第一类。

UserTag具有以下两个属性

public string TagText { get; set; }
public UserGroup UserGroup { get; set; }

现在我想要一个索引,使得TagText在UserGroup中是唯一的。但是相同的TagText应该能够存在于两个不同的UserGroup中。

我有两个问题,一个是TagText不能成为varchar(max),第二个是我没有UserGroupId将索引连接到。

我想使用Fluent API,但可以考虑更改我的迁移的上下。

但我不想在TagText上添加UserGroupId和注释。这可能吗?

最好的是Fluent API,但编辑迁移也可以。

1 个答案:

答案 0 :(得分:3)

修改

我已经错过了你的问题,我会在底部留下原来的答案(我已经过度阅读,你不想在你的模型中添加一个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%确定是否需要这个,也许它直接放在navigationproperty上时也有效)。这适用于约定(通过命名FK属性作为带有Id后缀的navigationproeprty。我假设用户组的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);

在这个例子中,我还添加了TagText的MaxLength信息(以及一个必需的信息,因为这似乎至少对我有意义......)。

您可以阅读有关EF和索引on msdn

的更多信息