EF6中的唯一索引约定

时间:2017-02-12 10:32:58

标签: c# entity-framework ef-code-first

如何为不同类型的索引创建自定义索引和键约定。我需要为以下键或索引类型命名不同的命名:

  • PK_TableName 主键
  • 外键的
  • FK_SourceTable_Column_TargetTable
  • IX_TableName_Column1_Column2 非唯一索引
  • UX_TableName_Column1_Column2 唯一索引

默认情况下,Entity Framework使用以下命名:

    主要密钥的
  • PK_ schemaname .TableName
  • 外键
  • FK_ schemaname .SourceTable_ schemaname .TargetTable_Column1
  • IX_Column1 表示非唯一索引
  • 唯一索引的
  • ColumnName

我发现我可以实施IStoreModelConvention<T>,但我还没有找到特定类型作为类型参数。 此外,还有Custom Code-First Conventions,但我的研究结果却没有结果。当我使用Entity Framework Code First 时,如何获得提及的命名规则?它可以是任何东西:包装,样品,或只是后续研究的方向。

1 个答案:

答案 0 :(得分:6)

PK和FK无法完成任务。问题在于没有用于命名存储约束的特殊EdmModel属性/属性/注释 - 在模型中它们基本上表示为列(属性)列表,并且命名约定在迁移构建器类中是硬编码的。请注意,评论中提到的一些示例显示了如何重命名FK 列(属性),而不是FK约束本身。

幸运的是索引虽然不简单,但有可能,感谢IndexAttributeIndexAnnotation。这是因为注释(带属性)与列(实体属性)相关联,然后由名为ConsolidatedIndex的内部类进行合并。

因此,为了实现目标,您必须创建IStoreModelConvention<EntityType>,从类似于ConsolidatedIndex类的属性准备合并索引信息,根据您的规则确定新名称带有默认名称的未命名索引或索引,由ForeignKeyIndexConvention为FK约束生成,并更新相应的IndexAnnotation属性。

话虽如此,这里是应用索引名称约定的代码:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Infrastructure.Annotations;
using System.Data.Entity.Migrations.Model;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Linq;

public class IndexNameConvention : IStoreModelConvention<EntityType>
{
    public void Apply(EntityType item, DbModel model)
    {
        // Build index info, consolidating indexes with the same name
        var indexInfo = new List<IndexInfo>();
        foreach (var p in item.Properties)
        {
            foreach (var mp in p.MetadataProperties)
            {
                var a = mp.Value as IndexAnnotation;
                if (a == null) continue;
                foreach (var index in a.Indexes)
                {
                    var info = index.Name != null ? indexInfo.FirstOrDefault(e => e.Name == index.Name) : null;
                    if (info == null)
                    {
                        info = new IndexInfo { Name = index.Name };
                        indexInfo.Add(info);
                    }
                    else
                    {
                        var other = info.Entries[0].Index;
                        if (index.IsUnique != other.IsUnique || index.IsClustered != other.IsClustered)
                            throw new Exception("Invalid index configuration.");
                    }
                    info.Entries.Add(new IndexEntry { Column = p, Annotation = mp, Index = index });
                }
            }
        }
        if (indexInfo.Count == 0) return;
        // Generate new name where needed
        var entitySet = model.StoreModel.Container.EntitySets.First(es => es.ElementType == item);
        foreach (var info in indexInfo)
        {
            var columns = info.Entries.OrderBy(e => e.Index.Order).Select(e => e.Column.Name);
            if (info.Name == null || info.Name == IndexOperation.BuildDefaultName(columns))
            {
                bool unique = info.Entries[0].Index.IsUnique;
                var name = string.Format("{0}_{1}_{2}", unique ? "UX" : "IX", entitySet.Table, string.Join("_", columns));
                if (name.Length > 128) name = name.Substring(0, 128);
                if (info.Name == name) continue;
                foreach (var entry in info.Entries)
                {
                    var index = new IndexAttribute(name);
                    if (entry.Index.Order >= 0)
                        index.Order = entry.Index.Order;
                    if (entry.Index.IsUniqueConfigured)
                        index.IsUnique = entry.Index.IsUnique;
                    if (entry.Index.IsClusteredConfigured)
                        index.IsClustered = entry.Index.IsClustered;
                    entry.Index = index;
                    entry.Modified = true;
                }
            }
        }
        // Apply the changes
        foreach (var g in indexInfo.SelectMany(e => e.Entries).GroupBy(e => e.Annotation))
        {
            if (g.Any(e => e.Modified))
                g.Key.Value = new IndexAnnotation(g.Select(e => e.Index));
        }
    }

    class IndexInfo
    {
        public string Name;
        public List<IndexEntry> Entries = new List<IndexEntry>();
    }

    class IndexEntry
    {
        public EdmProperty Column;
        public MetadataProperty Annotation;
        public IndexAttribute Index;
        public bool Modified;
    }
}

您只需将其添加到DbModelBuilder.Conventions中的OnModelCreating

即可
modelBuilder.Conventions.Add<IndexNameConvention>();