You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EF迁移报错GenerateCodeLiteral,应用运行正常但迁移失败

EF Core迁移报错:无法搭建List字面量的解决方案

问题原因

EF Core生成迁移时,需要将实体配置里的表达式转换成可序列化的C#字面量,但你用的WithProjection扩展中,List<ProjectionInfo>这个类型没有对应的CoreTypeMapping实现——运行时是内存直接执行表达式所以没问题,但设计时(迁移生成阶段)工具无法处理这个自定义类型,就抛出了错误。

解决办法

1. 让投影配置只在运行时生效

通过判断环境,在迁移生成的设计时环境下跳过这个配置,运行时再加载:

// 在DbContext的OnModelCreating方法中
bool isDesignTime = Environment.GetEnvironmentVariable("EF_DESIGN_TIME") == "true";
if (!isDesignTime)
{
    builder.Entity<TopicModel>()
        .WithProjection(
            x => x.MenuIds,
            x => x.Menus.Select(y => y.Id).ToList()
        );
}

也可以通过IDesignTimeDbContextFactory创建设计时专用的DbContext实例,不在其中添加该投影配置。

2. 为自定义类型实现CoreTypeMapping

如果需要迁移工具识别这个类型,得自定义类型映射,实现GenerateCodeLiteral方法来生成对应的C#代码:

// 在DbContext中重写ConfigureConventions注册映射
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
    configurationBuilder.Properties<List<ProjectionInfo>>()
        .HaveTypeMapping(new ProjectionInfoListTypeMapping());
}

// 自定义类型映射类
public class ProjectionInfoListTypeMapping : CoreTypeMapping
{
    public ProjectionInfoListTypeMapping() : base(new TypeMappingInfo(typeof(List<ProjectionInfo>)))
    {
    }

    public override Expression GenerateCodeLiteral(object value)
    {
        var projectionList = (List<ProjectionInfo>)value;
        // 根据ProjectionInfo的实际构造逻辑生成表达式,示例如下
        var elementExpressions = projectionList.Select(info => 
            Expression.New(
                typeof(ProjectionInfo).GetConstructor(new[] { typeof(Expression), typeof(Expression) }),
                GenerateCodeLiteral(info.SourceExpression),
                GenerateCodeLiteral(info.TargetExpression)
            )
        );
        return Expression.NewArrayInit(typeof(ProjectionInfo), elementExpressions.ToArray());
    }
}

注意:要根据你ProjectionInfo类的实际属性和构造方法调整代码,确保能正确生成可编译的C#字面量。

3. 改用原生投影方式

放弃自定义扩展的配置,改用EF Core原生支持的方式实现投影:
比如给TopicModel添加只读计算属性:

public class TopicModel
{
    // 其他属性...
    public ICollection<MenuModel> Menus { get; set; }
    public List<int> MenuIds => Menus?.Select(y => y.Id).ToList() ?? new List<int>();
}

然后在DbContext中标记该属性不映射到数据库:

builder.Entity<TopicModel>()
    .Property(x => x.MenuIds)
    .Metadata.SetIsTableColumn(false)
    .SetAfterSaveBehavior(PropertySaveBehavior.Ignore);

内容的提问来源于stack exchange,提问作者Randy Hall

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 02:05:37