Unique Indexes convention in EF6

前端 未结 1 988
借酒劲吻你
借酒劲吻你 2021-01-19 00:53

How to create custom index and key conventions for different type of indexes. I need different naming for following key or index types:

  • PK_TableName
相关标签:
1条回答
  • 2021-01-19 01:26

    Mission impossible for PK and FK. The problems is that there is no special EdmModel property/attribute/annotation for naming the store constraint - in the model they are basically represented as list of columns (properties) and the naming convention is hardcoded inside the migration builder classes. Please note that some examples mentioned in the comments are showing how to rename the FK columns (properties), not the FK constraint itself.

    Luckily for indexes, although not simple, but it's possible, thanks to the IndexAttribute and IndexAnnotation. This is because the annotation (with attribute) is associated with column (entity property), and then consolidated by an internal class called ConsolidatedIndex.

    So in order to achieve the goal, you have to create IStoreModelConvention<EntityType>, prepare a consolidated index info from properties similar to how ConsolidatedIndex class does it, determine the new name based on your rules for the unnamed indexes or indexes with default name generated for FK constrains by the ForeignKeyIndexConvention, and update the corresponding IndexAnnotation of the properties.

    With that being said, here is the code for applying your index name convention:

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

    All you need is to add it to the DbModelBuilder.Conventions in your OnModelCreating:

    modelBuilder.Conventions.Add<IndexNameConvention>();
    
    0 讨论(0)
提交回复
热议问题