NHibernate非流畅配置:实体只读集合能否通过自定义SQL查询填充?
答案:给实体配置只读的自定义SQL填充集合?当然可以!
完全没问题!尤其是在Entity Framework Core这类常用ORM框架里,你有几种靠谱的方式来实现Village实体的ReportsAsDefender只读集合需求,刚好匹配你提到的自定义SQL查询场景。
方案1:无键实体+手动填充(最灵活的只读方案)
这种方式完全贴合你的自定义SQL需求,而且集合天然只读,不会被ORM跟踪修改:
- 首先,给
Village实体添加一个非映射的只读集合属性,用[NotMapped]标记告诉EF Core不要把它当成数据库表的字段/导航属性:
public class Village { public int Id { get; set; } // 你的其他Village属性... [NotMapped] public IEnumerable<Report> ReportsAsDefender { get; private set; } }
- 然后在数据访问层(比如Repository类)里,写一个方法同时获取
Village和对应的防守报告,用你自定义的SQL查询填充集合:
public async Task<Village> GetVillageWithDefenderReportsAsync(int villageId) { var village = await _context.Villages.FindAsync(villageId); if (village == null) return null; // 用你提供的SQL(补全WHERE条件),注意用参数化避免注入 var reports = await _context.Reports .FromSqlInterpolated($@" SELECT report.* FROM armies army JOIN reports report ON report.DefendingArmyId = army.Id JOIN villages village ON village.Id = army.VillageId WHERE village.Id = {villageId}") .ToListAsync(); village.ReportsAsDefender = reports; return village; }
- 这个方案的好处是完全自定义SQL逻辑,集合是只读的(setter为private),不用担心误修改后被EF提交到数据库。
方案2:配置过滤导航属性(需要EF跟踪场景)
如果你希望这个集合能被EF Core跟踪(比如懒加载、自动关联更新,虽然你要只读,但也提一下),可以先建立实体间的导航关系,再配置过滤:
- 先完善实体间的关联:给
Army加Village外键和导航,给Report加DefendingArmy导航:
public class Army { public int Id { get; set; } public int VillageId { get; set; } public Village Village { get; set; } // 其他Army属性... } public class Report { public int Id { get; set; } public int DefendingArmyId { get; set; } public Army DefendingArmy { get; set; } // 其他Report属性... }
- 然后给
Village添加只读导航集合:
public class Village { public int Id { get; set; } // 其他Village属性... public IReadOnlyCollection<Report> ReportsAsDefender { get; } = new List<Report>(); }
- 最后在
DbContext的OnModelCreating里用Fluent API配置这个导航的关联逻辑,标记为只读:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Village>() .HasMany(v => v.ReportsAsDefender) .WithOne(r => r.DefendingArmy) .HasForeignKey(r => r.DefendingArmyId) .Metadata.SetIsReadOnly(true); // 标记集合为只读,禁止外部添加/删除元素 }
- 这种方式下,你可以直接通过
village.ReportsAsDefender获取数据,EF会自动生成关联查询,但如果要完全用你自己写的SQL,还是方案1更灵活。
几个注意点
- 确保你的SQL查询返回的字段和
Report实体的属性完全匹配,避免映射错误; - 用
FromSqlInterpolated或者参数化查询,不要直接拼接字符串,防止SQL注入; - 如果是纯只读需求,优先选方案1,减少EF跟踪带来的不必要开销。
内容的提问来源于stack exchange,提问作者Tyler Camp
相关产品推荐
相关产品推荐

