如何使用Linq与Fluent NHibernate实现多表左连接?
问题描述
我有以下领域类:
public class File { public virtual long LFD { get; set; } public virtual long AK_KEY_PE_WERBER { get; set; } public virtual long AK_KEY_PE_RECHT { get; set; } //其他属性 } public class Employee { public virtual long LFD { get; set; } //其他属性 }
对应的NHibernate映射类:
public class FileMap : ClassMap<File> { public FileMap() { Id(x => x.LFD); Map(x => x.AK_KEY_PE_WERBER); //其他字段映射 } } public class EmployeeMap : ClassMap<Employee> { public EmployeeMap() //注:原代码构造函数名MITARBEITERMap为笔误,已修正 { Id(x => x.LFD); //其他字段映射 } }
数据库数据:
- Files表:
- LFD: 1,AK_KEY_PE_WERBER: null
- LFD: 2,AK_KEY_PE_WERBER: 1
- Employees表:
- LFD: 1
我需要查询所有File数据,并在AK_KEY_PE_WERBER不为null时关联对应的Employee。尝试两种方法均有问题:
- 普通join仅返回AK_KEY_PE_WERBER不为null的File,结果数量为1而非2:
var contentQuery = (from file in DBSession.Query<File>() join werb in DBSession.Query<Employee>() on file.AK_KEY_PE_WERBER equals werb.LFD select new { File = file, Recht = werb, }).ToList(); Debug.WriteLine(contentQuery.Count);
- 使用
join into分组后调用FirstOrDefault()抛出NotImplementedException:
var contentQuery = (from file in DBSession.Query<File>() join werb in DBSession.Query<Employee>() on file.AK_KEY_PE_WERBER equals werb.LFD into werbJoin select new { File = file, Recht = werbJoin.FirstOrDefault(), }).ToList();
解决方案
方法1:使用左外连接的正确LINQ写法
NHibernate的LINQ Provider对join into后直接调用FirstOrDefault()支持有限,你可以通过DefaultIfEmpty()实现左外连接:
var contentQuery = (from file in DBSession.Query<File>() join werb in DBSession.Query<Employee>() on file.AK_KEY_PE_WERBER equals werb.LFD into werbJoin from werb in werbJoin.DefaultIfEmpty() select new { File = file, Recht = werb, }).ToList();
该写法会返回所有File记录,当AK_KEY_PE_WERBER为null时,Recht字段会是null,最终结果数量为2,符合需求。
方法2:通过导航属性优化(NHibernate最佳实践)
更推荐的方式是在领域类中添加导航属性,通过映射关联关系简化查询:
- 修改领域类(注意修正可空字段类型):
public class File { public virtual long LFD { get; set; } public virtual long? AK_KEY_PE_WERBER { get; set; } //原long类型无法存储null,改为可空类型 public virtual long AK_KEY_PE_RECHT { get; set; } public virtual Employee Werber { get; set; } //添加导航属性 //其他属性 }
- 修改
FileMap映射类:
public class FileMap : ClassMap<File> { public FileMap() { Id(x => x.LFD); Map(x => x.AK_KEY_PE_WERBER); References(x => x.Werber) .Column("AK_KEY_PE_WERBER") .Nullable() //允许关联为空 .ReadOnly(); //无需通过File维护Employee时设置为只读 //其他字段映射 } }
- 简化查询代码:
var contentQuery = DBSession.Query<File>() .Fetch(f => f.Werber) //预加载关联Employee,避免N+1查询 .Select(f => new { File = f, Recht = f.Werber }) .ToList();
这种方式代码更简洁,还能利用NHibernate的加载机制,避免手动join的潜在问题。
关键注意点
原领域类中AK_KEY_PE_WERBER为long类型,但数据库存在null值,这会导致映射异常,必须改为long?(可空长整型)确保类型匹配。
内容的提问来源于stack exchange,提问作者Gener4tor
相关产品推荐
相关产品推荐

