EF6 中的唯一索引约定

Posted

技术标签:

【中文标题】EF6 中的唯一索引约定【英文标题】:Unique Indexes convention in EF6 【发布时间】:2017-06-30 09:39:28 【问题描述】:

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

PK_TableName 主键 FK_SourceTable_Column_TargetTable 用于外键 IX_TableName_Column1_Column2 非唯一索引 UX_TableName_Column1_Column2 唯一索引

默认情况下,实体框架使用以下命名:

PK_schemaname.TableName 用于主键 FK_schemaname.SourceTable_schemaname.TargetTable_Column1 用于外键 IX_Column1 用于非唯一索引 ColumnName 用于唯一索引

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

【问题讨论】:

***.com/questions/22618237/… 您可以在此处查看:***.com/a/18245172/5311735 一个如何使用 IStoreModelConvention 修改外键名称的示例。或许您也可以通过这种方式修改其他索引名称。 我试过但没有成功。我还尝试了自定义 sql 生成器。但由于缺乏信息我没有任何结果 您可以使用索引属性更改配置索引的名称(唯一和非唯一):[Index("IndexName")] 以下是更改外键名称的 storemodel 约定示例:msdn.microsoft.com/en-us/library/dn469439(v=vs.113).aspx 查看 IA 重命名约定 【参考方案1】:

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

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

因此,为了实现目标,您必须创建IStoreModelConvention&lt;EntityType&gt;,从类似于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;
    

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

modelBuilder.Conventions.Add<IndexNameConvention>();

【讨论】:

男孩!这一定需要很大的耐心! @GertArnold 实际上(对我而言)编写答案比探索 EF 源代码和编写代码要困难得多:) @IvanStoev 我也尝试过了解源代码,但这非常困难......

以上是关于EF6 中的唯一索引约定的主要内容,如果未能解决你的问题,请参考以下文章

唯一约束的命名约定

SQL Server 索引命名约定 [关闭]

mongoDB 3.2中的唯一索引忽略空值

唯一索引与主键索引的比较

oracle 唯一约束 为啥 唯一索引

添加索引迁移中的唯一性被忽略