Is there a data annotation for unique constraint in EF Core (code first)?

佐手、 提交于 2019-11-27 16:44:03

问题


I am wondering if there is a data annotation for unique constraint in Entity Framework Core 2 code first approach?


回答1:


In EF Core You could use the extension method HasAlternateKey in fluent API only. There are no data annotations to realize a unique constraint.

This MS doc article - Alternate Keys (Unique Constraints) - will explain how to use and which further possibilities are exist.

A short example from link above:

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; }
}

Also it's possible to define an unique index. Therefor in EF Core you have to use in fluent API the extension method HasIndex (no data annotations). In this MS doc article - Indexes - you will find further information.

And here an example for an unique index:

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; }
}



回答2:


I've written an Attribute class that can allow you to decorate your EF Core Entity class properties to cause a Unique Key to be generated (without the Fluent API).

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; }
}

In your DbContext.cs file, inside OnModelCreating(modelBuilder) method, add this:

// 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
}

Also in your DbContext.cs class, add this private method:

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>();
}

Usage in your Entity.cs class:

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; }
}

You can even use this across multiple properties to form a Unique Key across multiple columns in your table. (Note the use of "groupId" and then the "order")

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; }
}


来源:https://stackoverflow.com/questions/49526370/is-there-a-data-annotation-for-unique-constraint-in-ef-core-code-first

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!