web-dev-qa-db-fra.com

Existe-t-il une annotation de données pour une contrainte unique dans EF Core (code en premier)?

Je me demande s'il y a une annotation de données pour une contrainte unique dans la première approche du code Entity Framework Core 2?

7
Elnoor

Dans EF Core, vous pouvez utiliser la méthode d'extension HasAlternateKey dans l'API couramment uniquement. Il n'y a aucune annotation de données pour réaliser une contrainte unique .

Cet article MS doc - Clés alternatives (contraintes uniques) - expliquera comment utiliser et quelles autres possibilités existent.

Un court exemple du lien ci-dessus:

class MyContext : DbContext
{
    public DbSet<Car> Cars { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Car>()
            .HasAlternateKey(c => c.LicensePlate)
            .HasName("AlternateKey_LicensePlate");
    }
}

class Car
{
    public int CarId { get; set; }
    public string LicensePlate { get; set; }
    public string Make { get; set; }
    public string Model { get; set; }
}

Il est également possible de définir un index unique . Pour cela, dans EF Core, vous devez utiliser dans l'API couramment la méthode d'extension HasIndex ( aucune annotation de données ). Dans cet article MS doc - Index - vous trouverez plus d'informations.

Et voici un exemple pour un index unique:

class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>()
            .HasIndex(b => b.Url)
            .IsUnique();
    }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}
9
ChW

J'ai écrit une classe d'attributs qui peut vous permettre de décorer les propriétés de votre classe EF Core Entity pour générer une clé unique (sans l'API Fluent).

using System;
using System.ComponentModel.DataAnnotations;

/// <summary>
/// Used on an EntityFramework Entity class to mark a property to be used as a Unique Key
/// </summary>
[AttributeUsageAttribute(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public class UniqueKeyAttribute : ValidationAttribute
{
    /// <summary>
    /// Marker attribute for unique key
    /// </summary>
    /// <param name="groupId">Optional, used to group multiple entity properties together into a combined Unique Key</param>
    /// <param name="order">Optional, used to order the entity properties that are part of a combined Unique Key</param>
    public UniqueKeyAttribute(string groupId = null, int order = 0)
    {
        GroupId = groupId;
        Order = order;
    }

    public string GroupId { get; set; }
    public int Order { get; set; }
}

Dans votre fichier DbContext.cs, à l'intérieur de la méthode OnModelCreating (modelBuilder), ajoutez ceci:

// Iterate through all EF Entity types
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
    #region Convert UniqueKeyAttribute on Entities to UniqueKey in DB
    var properties = entityType.GetProperties();
    if ((properties != null) && (properties.Any()))
    {
        foreach (var property in properties)
        {
            var uniqueKeys = GetUniqueKeyAttributes(entityType, property);
            if (uniqueKeys != null)
            {
                foreach (var uniqueKey in uniqueKeys.Where(x => x.Order == 0))
                {
                    // Single column Unique Key
                    if (String.IsNullOrWhiteSpace(uniqueKey.GroupId))
                    {
                        entityType.AddIndex(property).IsUnique = true;
                    }
                    // Multiple column Unique Key
                    else
                    {
                        var mutableProperties = new List<IMutableProperty>();
                        properties.ToList().ForEach(x =>
                        {
                            var uks = GetUniqueKeyAttributes(entityType, x);
                            if (uks != null)
                            {
                                foreach (var uk in uks)
                                {
                                    if ((uk != null) && (uk.GroupId == uniqueKey.GroupId))
                                    {
                                        mutableProperties.Add(x);
                                    }
                                }
                            }
                        });
                        entityType.AddIndex(mutableProperties).IsUnique = true;
                    }
                }
            }
        }
    }
    #endregion Convert UniqueKeyAttribute on Entities to UniqueKey in DB
}

Toujours dans votre classe DbContext.cs, ajoutez cette méthode privée:

private static IEnumerable<UniqueKeyAttribute> GetUniqueKeyAttributes(IMutableEntityType entityType, IMutableProperty property)
{
    if (entityType == null)
    {
        throw new ArgumentNullException(nameof(entityType));
    }
    else if (entityType.ClrType == null)
    {
        throw new ArgumentNullException(nameof(entityType.ClrType));
    }
    else if (property == null)
    {
        throw new ArgumentNullException(nameof(property));
    }
    else if (property.Name == null)
    {
        throw new ArgumentNullException(nameof(property.Name));
    }
    var propInfo = entityType.ClrType.GetProperty(
        property.Name,
        BindingFlags.NonPublic |
        BindingFlags.Public |
        BindingFlags.Static |
        BindingFlags.Instance |
        BindingFlags.DeclaredOnly);
    if (propInfo == null)
    {
        return null;
    }
    return propInfo.GetCustomAttributes<UniqueKeyAttribute>();
}

Utilisation dans votre classe Entity.cs:

public class Company
{
    [Required]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid CompanyId { get; set; }

    [Required]
    [UniqueKey(groupId: "1", order: 0)]
    [StringLength(100, MinimumLength = 1)]
    public string CompanyName { get; set; }
}

Vous pouvez même l'utiliser sur plusieurs propriétés pour former une clé unique sur plusieurs colonnes de votre table. (Notez l'utilisation de "groupId" puis de la "commande")

public class Company
{
    [Required]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid CompanyId { get; set; }

    [Required]
    [UniqueKey(groupId: "1", order: 0)]
    [StringLength(100, MinimumLength = 1)]
    public string CompanyName { get; set; }

    [Required]
    [UniqueKey(groupId: "1", order: 1)]
    [StringLength(100, MinimumLength = 1)]
    public string CompanyLocation { get; set; }
}
8
Justin Tubbs