C# MVC多语言站点EF查询报错:字段映射不匹配求解决方案
问题背景
我正在使用C#开发多语言站点,数据库表以en_Baslik、tr_Baslik这类带语言前缀的字段存储多语言内容,示例表结构如下:
ID|tr_Baslik|tr_Aciklama|tr_Icerik|en_Baslik|en_Aciklama|en_Icerik 1 | Turkey | Turkey | Turkey |English |English |English
我编写了GetHaber函数,将活跃语言作为参数传入,通过原生SQL查询对应语言的字段:
public static List<Haberler> GetHaber(string lang) { using (BIRLIKB2BEntities db = new BIRLIKB2BEntities()) { var Haber = db.Database.SqlQuery<Haberler>($"SELECT ID, {lang}_Baslik, {lang}_KisaAciklama, {lang}_Icerik FROM Haberler").ToList(); return Haber; } }
期望根据传入的语言代码仅拉取对应语言列,但Entity Framework抛出错误:
The data reader is incompatible with the specified 'BIRLIKB2BModel.Haberler'. A member of the type, 'Baslik', does not have a corresponding column in the data reader with the same name.
希望实现按语言代码读取对应列的预期效果,请问是否有替代解决方法?
报错的核心原因是:Haberler实体类的属性是Baslik、KisaAciklama、Icerik,但SQL查询返回的列名是带语言前缀的tr_Baslik/en_Baslik,EF无法自动映射名称不匹配的列和属性。以下是几个可行的解决方案:
方案1:SQL查询时给列起别名,匹配实体属性名
这是最直接的方案,在SQL语句里将带前缀的列名重命名为实体类对应的属性名,让EF能正确映射:
public static List<Haberler> GetHaber(string lang) { using (BIRLIKB2BEntities db = new BIRLIKB2BEntities()) { // 给查询列起别名,和Haberler实体的属性名完全一致 var sql = $"SELECT ID, {lang}_Baslik AS Baslik, {lang}_KisaAciklama AS KisaAciklama, {lang}_Icerik AS Icerik FROM Haberler"; var Haber = db.Database.SqlQuery<Haberler>(sql).ToList(); return Haber; } }
方案2:创建DTO类做中间转换
如果不想直接操作原实体类,可以创建一个和查询结果结构匹配的DTO(数据传输对象),查询后再转换为Haberler实体:
// 定义匹配查询结果的DTO public class HaberDto { public int ID { get; set; } public string Baslik { get; set; } public string KisaAciklama { get; set; } public string Icerik { get; set; } } // 修改查询方法 public static List<Haberler> GetHaber(string lang) { using (BIRLIKB2BEntities db = new BIRLIKB2BEntities()) { var sql = $"SELECT ID, {lang}_Baslik AS Baslik, {lang}_KisaAciklama AS KisaAciklama, {lang}_Icerik AS Icerik FROM Haberler"; var dtoList = db.Database.SqlQuery<HaberDto>(sql).ToList(); // 将DTO转换为目标实体 return dtoList.Select(dto => new Haberler { ID = dto.ID, Baslik = dto.Baslik, KisaAciklama = dto.KisaAciklama, Icerik = dto.Icerik }).ToList(); } }
这个方案适合需要对查询数据做额外处理的场景,也能隔离实体类和查询逻辑。
方案3:修改实体类映射配置(仅Code First场景参考)
如果项目是Code First模式,可以通过Fluent API配置列名与属性名的映射,但该方案只能固定映射一个列名,无法满足动态切换语言的需求,仅作参考:
protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Haberler>() .Property(p => p.Baslik) .HasColumnName("en_Baslik"); // 仅能固定映射一个列名,不适合动态场景 }
内容的提问来源于stack exchange,提问作者Ahmet Enes Söylemez

