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

如何使用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。尝试两种方法均有问题:

  1. 普通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);
  1. 使用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最佳实践)

更推荐的方式是在领域类中添加导航属性,通过映射关联关系简化查询:

  1. 修改领域类(注意修正可空字段类型):
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; } //添加导航属性
    //其他属性
}
  1. 修改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时设置为只读
        //其他字段映射
    }
}
  1. 简化查询代码:
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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 04:48:39