Entity Framework 6自动迁移下如何实现带筛选条件的自定义索引注解
我来帮你搞定EF6中筛选索引的自动迁移问题,思路和你之前实现列默认值的方式完全一致,一步步来就能实现完全自动化的迁移,不用手动修改Up/Down方法:
1. 创建自定义筛选索引属性
首先我们扩展EF原生的IndexAttribute,添加筛选条件的属性,这样可以复用EF原生的索引配置逻辑:
public class FilteredIndexAttribute : IndexAttribute { // 存储筛选索引的WHERE子句 public string WhereClause { get; set; } // 支持多列索引的列名集合(用于类级别的索引配置) public string[] Columns { get; set; } // 属性级单列索引构造函数 public FilteredIndexAttribute(string name, string whereClause) : base(name) { WhereClause = whereClause; } // 属性级唯一单列索引构造函数 public FilteredIndexAttribute(string name, bool isUnique, string whereClause) : base(name, isUnique) { WhereClause = whereClause; } // 类级多列索引构造函数 public FilteredIndexAttribute(string name, string[] columns, string whereClause) : base(name) { Columns = columns; WhereClause = whereClause; } // 类级唯一多列索引构造函数 public FilteredIndexAttribute(string name, bool isUnique, string[] columns, string whereClause) : base(name, isUnique) { Columns = columns; WhereClause = whereClause; } }
2. 在实体类中使用自定义属性
不管是单列还是多列的筛选索引,都可以通过这个属性配置:
// 单列筛选索引示例 public class MyEntity { public int Id { get; set; } // 给Status列创建筛选索引,只包含Status=1的行 [FilteredIndex("IX_MyEntity_Status", "Status = 1")] public int Status { get; set; } // 你之前的默认值属性保留 [SqlDefaultValue(123)] public int DefInt { get; set; } } // 多列筛选索引示例(类级别配置) [FilteredIndex("IX_Order_Customer_Amount", new[] { "CustomerId", "TotalAmount" }, "TotalAmount > 100")] public class Order { public int Id { get; set; } public int CustomerId { get; set; } public decimal TotalAmount { get; set; } }
3. 添加模型约定,将属性转换为模型注解
我们需要创建一个自定义约定,把FilteredIndexAttribute的信息添加到EF的模型元数据中,这样迁移时能读取到这些配置:
public class FilteredIndexConvention : Convention { public FilteredIndexConvention() { // 处理属性级别的筛选索引 Properties() .Having(p => p.GetCustomAttributes(typeof(FilteredIndexAttribute), false).Cast<FilteredIndexAttribute>()) .Configure((config, attributes) => { foreach (var attr in attributes) { var index = config.HasIndex(attr.Name, attr.IsUnique).Index; // 给索引添加筛选条件的注解 index.AddAnnotation("FilteredIndexWhereClause", attr.WhereClause); } }); // 处理类级别的多列筛选索引 Types() .Having(t => t.GetCustomAttributes(typeof(FilteredIndexAttribute), false).Cast<FilteredIndexAttribute>()) .Configure((config, attributes) => { foreach (var attr in attributes) { var index = config.HasIndex(attr.Name, attr.IsUnique, attr.Columns).Index; index.AddAnnotation("FilteredIndexWhereClause", attr.WhereClause); } }); } }
然后在OnModelCreating中注册这个约定:
protected override void OnModelCreating(DbModelBuilder modelBuilder) { // 你之前的默认值约定保留 modelBuilder.Conventions.Add(new AttributeToColumnAnnotationConvention<SqlDefaultValueAttribute, object>( nameof(SqlDefaultValueAttribute), (p, attributes) => attributes.Single().Value)); // 添加筛选索引的约定 modelBuilder.Conventions.Add(new FilteredIndexConvention()); }
4. 修改自定义SQL生成器,生成带WHERE子句的索引SQL
在你之前的CustomSqlServerMigrationSqlGenerator中,重写Generate(CreateIndexOperation)方法,读取注解并生成筛选索引的SQL:
public class CustomSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator { // 保留你之前实现的Generate(CreateTableOperation)和Generate(AddColumnOperation)方法 protected override void Generate(CreateIndexOperation createIndexOperation) { // 检查当前索引是否有筛选条件注解 if (createIndexOperation.Annotations.TryGetValue("FilteredIndexWhereClause", out var whereObj) && whereObj is string whereClause && !string.IsNullOrEmpty(whereClause)) { // 生成带WHERE子句的CREATE INDEX语句 var sql = string.Format( "CREATE {0}INDEX {1} ON {2} ({3}) WHERE {4};", createIndexOperation.IsUnique ? "UNIQUE " : "", Quote(createIndexOperation.Name), Quote(createIndexOperation.Table), string.Join(", ", createIndexOperation.Columns.Select(Quote)), whereClause); Statement(sql); } else { // 没有筛选条件时,调用EF原生的索引生成逻辑 base.Generate(createIndexOperation); } } }
5. 验证自动迁移
现在当你修改实体类的筛选索引配置后,EF的自动迁移会自动生成对应的筛选索引SQL,完全不需要手动修改迁移文件。比如给MyEntity的Status列添加FilteredIndexAttribute后,自动迁移会生成:
CREATE INDEX IX_MyEntity_Status ON dbo.MyEntities (Status) WHERE Status = 1;
内容的提问来源于stack exchange,提问作者hamed
相关产品推荐
相关产品推荐

