Установка уникального ограничения с помощью свободного API?

185

Я пытаюсь создать объект EF с помощью Code First и EntityTypeConfigurationсвободно использующего API. создавать первичные ключи легко, но не так с уникальным ограничением. Я видел старые посты, в которых предлагалось выполнить собственные команды SQL для этого, но, похоже, это не соответствует цели. это возможно с EF6?

kob490
источник

Ответы:

275

На EF6.2 вы можете использовать HasIndex()для добавления индексов для миграции через свободный API.

https://github.com/aspnet/EntityFramework6/issues/274

пример

modelBuilder
    .Entity<User>()
    .HasIndex(u => u.Email)
        .IsUnique();

На EF6.1 года, вы можете использовать , IndexAnnotation()чтобы добавить индексы для миграции в вашем беглом API.

http://msdn.microsoft.com/en-us/data/jj591617.aspx#PropertyIndex

Вы должны добавить ссылку на:

using System.Data.Entity.Infrastructure.Annotations;

Основной пример

Вот простое использование, добавление индекса на User.FirstNameимущество

modelBuilder 
    .Entity<User>() 
    .Property(t => t.FirstName) 
    .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute()));

Практический пример:

Вот более реалистичный пример. Он добавляет уникальный индекс для нескольких свойств: User.FirstNameи User.LastNameс именем индекса "IX_FirstNameLastName"

modelBuilder 
    .Entity<User>() 
    .Property(t => t.FirstName) 
    .IsRequired()
    .HasMaxLength(60)
    .HasColumnAnnotation(
        IndexAnnotation.AnnotationName, 
        new IndexAnnotation(
            new IndexAttribute("IX_FirstNameLastName", 1) { IsUnique = true }));

modelBuilder 
    .Entity<User>() 
    .Property(t => t.LastName) 
    .IsRequired()
    .HasMaxLength(60)
    .HasColumnAnnotation(
        IndexAnnotation.AnnotationName, 
        new IndexAnnotation(
            new IndexAttribute("IX_FirstNameLastName", 2) { IsUnique = true }));
Yorro
источник
4
Это необходимо для обозначения аннотации столбца как «Индекс»! Я написал другое имя, и оно не сработало! Я потратил часы, прежде чем попытаться переименовать его в оригинальный «Индекс», как в вашем посте, и понял, что это важно. :( В нем должна быть константа, чтобы не жестко кодировать строку.
Александр Васильев
10
@AlexanderVasilyev Константа определяется какIndexAnnotation.AnnotationName
Натан
3
@ Натан Спасибо! Это оно! Пример в этом посте должен быть исправлен с помощью этой константы.
Александр Васильев
2
Не могу найти его в EF7 - DNX
Shimmy Weitzhandler
2
Я считаю, что IsUnique необходимо установить в true при создании IndexAttribute в первом примере. Как это: new IndexAttribute() { IsUnique = true }. В противном случае он создает только обычный (неуникальный) индекс.
Якубка
134

В дополнение к ответу Йорро, это можно сделать с помощью атрибутов.

Образец для intтипа уникальной комбинации клавиш:

[Index("IX_UniqueKeyInt", IsUnique = true, Order = 1)]
public int UniqueKeyIntPart1 { get; set; }

[Index("IX_UniqueKeyInt", IsUnique = true, Order = 2)]
public int UniqueKeyIntPart2 { get; set; }

Если тип данных string, то MaxLengthатрибут должен быть добавлен:

[Index("IX_UniqueKeyString", IsUnique = true, Order = 1)]
[MaxLength(50)]
public string UniqueKeyStringPart1 { get; set; }

[Index("IX_UniqueKeyString", IsUnique = true, Order = 2)]
[MaxLength(50)]
public string UniqueKeyStringPart2 { get; set; }

Если существует проблема разделения модели домен / хранилище, можно использовать Metadatatypeатрибут / класс: https://msdn.microsoft.com/en-us/library/ff664465%28v=pandp.50%29.aspx?f= 255 & MSPPError = -2147217396


Пример быстрого консольного приложения:

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;

namespace EFIndexTest
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var context = new AppDbContext())
            {
                var newUser = new User { UniqueKeyIntPart1 = 1, UniqueKeyIntPart2 = 1, UniqueKeyStringPart1 = "A", UniqueKeyStringPart2 = "A" };
                context.UserSet.Add(newUser);
                context.SaveChanges();
            }
        }
    }

    [MetadataType(typeof(UserMetadata))]
    public class User
    {
        public int Id { get; set; }
        public int UniqueKeyIntPart1 { get; set; }
        public int UniqueKeyIntPart2 { get; set; }
        public string UniqueKeyStringPart1 { get; set; }
        public string UniqueKeyStringPart2 { get; set; }
    }

    public class UserMetadata
    {
        [Index("IX_UniqueKeyInt", IsUnique = true, Order = 1)]
        public int UniqueKeyIntPart1 { get; set; }

        [Index("IX_UniqueKeyInt", IsUnique = true, Order = 2)]
        public int UniqueKeyIntPart2 { get; set; }

        [Index("IX_UniqueKeyString", IsUnique = true, Order = 1)]
        [MaxLength(50)]
        public string UniqueKeyStringPart1 { get; set; }

        [Index("IX_UniqueKeyString", IsUnique = true, Order = 2)]
        [MaxLength(50)]
        public string UniqueKeyStringPart2 { get; set; }
    }

    public class AppDbContext : DbContext
    {
        public virtual DbSet<User> UserSet { get; set; }
    }
}
coni2k
источник
45
Нет, если вы хотите полностью отделить модель своего домена от проблем хранения.
Рикард Лильеберг
4
Вы также должны убедиться, что у вас есть ссылка на EntityFramework
Майкл Транчида
2
Было бы хорошо, если бы атрибут Index был отделен от Entity Framework, чтобы я мог включить его в свой проект Models. Я понимаю, что это проблема хранения, но главная причина, по которой я ее использую, заключается в том, чтобы наложить уникальные ограничения на такие вещи, как имена пользователей и имена ролей.
Сэм
2
Не могу найти его в EF7 - DNX
Shimmy Weitzhandler
4
Это работает, только если вы также ограничиваете длину строки, так как SQL не позволяет использовать nvarchar (max) в качестве ключа.
JMK
17

Вот метод расширения для более быстрой установки уникальных индексов:

public static class MappingExtensions
{
    public static PrimitivePropertyConfiguration IsUnique(this PrimitivePropertyConfiguration configuration)
    {
        return configuration.HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute { IsUnique = true }));
    }
}

Использование:

modelBuilder 
    .Entity<Person>() 
    .Property(t => t.Name)
    .IsUnique();

Будет генерировать миграцию, такую ​​как:

public partial class Add_unique_index : DbMigration
{
    public override void Up()
    {
        CreateIndex("dbo.Person", "Name", unique: true);
    }

    public override void Down()
    {
        DropIndex("dbo.Person", new[] { "Name" });
    }
}

Src: Создание уникального индекса с помощью API-интерфейса Entity Framework 6.1.

Барто Бернсманн
источник
16

Ответ @ coni2k правильный, но вы должны добавить [StringLength]атрибут, чтобы он работал, иначе вы получите недопустимое исключение ключа (пример ниже).

[StringLength(65)]
[Index("IX_FirstNameLastName", 1, IsUnique = true)]
public string FirstName { get; set; }

[StringLength(65)]
[Index("IX_FirstNameLastName", 2, IsUnique = true)]
public string LastName { get; set; }
Arijoon
источник
0
modelBuilder.Property(x => x.FirstName).IsUnicode().IsRequired().HasMaxLength(50);
Русонур Джаман
источник