使用Add-Migration时EdmFunction引发报错的问题排查
解决EF Code First注册自定义Edm函数导致Add-Migration报错的问题
你在EF Code First模式下注册自定义RemoveDiacritics Edm函数后,运行时功能正常,但执行add-migration test -verbose时抛出Sequence contains no elements异常,问题根源出在自定义约定类对迁移流程的干扰上。
问题原因拆解
当执行迁移命令时,EF需要生成并对比实体模型与数据库结构的映射关系,但你的RemoveDiacriticsFunctionConvention做了两个干扰迁移的操作:
- 在
Apply方法中手动调用了model.Compile(),提前编译了模型,导致后续迁移流程读取元数据时出现空序列异常; - 迁移流程本身不需要注册存储层的函数约定——迁移负责的是实体到数据库表结构的变更,自定义数据库函数需要单独处理,不需要参与模型对比。
分步解决方案
1. 让约定仅在运行时生效
修改ApplicationDBContext的OnModelCreating方法,添加判断逻辑,只在非迁移上下文时注册约定:
protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // 仅在非迁移环境下注册自定义函数约定 if (!IsRunningMigration()) { modelBuilder.Conventions.Add(new RemoveDiacriticsFunctionConvention()); } } // 辅助方法:判断当前是否处于迁移上下文 private bool IsRunningMigration() { return AppDomain.CurrentDomain.GetAssemblies() .Any(assembly => assembly.FullName.StartsWith("EntityFramework.Migrations")); }
2. 移除手动编译模型的代码
在RemoveDiacriticsFunctionConvention的Apply方法中,删掉model.Compile()这行代码——EF会自动处理模型编译,手动调用会打乱迁移的模型生成流程:
public void Apply(EntityContainer item, DbModel model) { // 声明参数 List<FunctionParameter> Parameters = new List<FunctionParameter>(); Parameters.Add(FunctionParameter.Create("StringValue", GetStorePrimitiveType(model, PrimitiveTypeKind.String), ParameterMode.In)); // 声明返回参数 List<FunctionParameter> ReturnParameters = new List<FunctionParameter>(); ReturnParameters.Add(FunctionParameter.Create("ReturnValue", GetStorePrimitiveType(model, PrimitiveTypeKind.String), ParameterMode.ReturnValue)); // 创建函数负载 EdmFunctionPayload payload = new EdmFunctionPayload(); payload.IsComposable = true; payload.Schema = "dbo"; payload.StoreFunctionName = "RemoveDiacritics"; payload.ReturnParameters = ReturnParameters; payload.Parameters = Parameters; // 创建并添加函数到模型 EdmFunction function = EdmFunction.Create("RemoveDiacritics", "dbo", DataSpace.SSpace, payload, new MetadataProperty[] { }); model.StoreModel.AddItem(function); // 移除这行手动编译代码 // model.Compile(); }
3. 确保数据库中存在自定义函数
因为Code First迁移不会自动生成自定义函数的创建脚本,你需要手动处理:
- 如果是首次迁移,在初始迁移的
Up方法中添加创建函数的SQL:
public override void Up() { // 其他迁移代码... Sql(@" CREATE FUNCTION dbo.RemoveDiacritics(@StringValue NVARCHAR(MAX)) RETURNS NVARCHAR(MAX) AS BEGIN -- 替换为你的函数实现逻辑 RETURN @StringValue; END "); }
- 如果数据库已存在该函数,可忽略此步骤。
为什么之前的尝试无效?
你添加的[NotMapped]和modelBuilder.Ignore<RemoveDiacriticsFunctionConvention>()是用来忽略实体类的映射,而RemoveDiacriticsFunctionConvention是约定类,不属于实体,所以这些配置对它完全不起作用。
内容的提问来源于stack exchange,提问作者KingOfBabu
相关产品推荐
相关产品推荐

