NHibernate QueryOver 嵌套 类

NHibernate QueryOver with Nested Classes

我正在使用 NHibernate 的 QueryOver 填充具有许多嵌套 class 的 class 的 IEnumerable。但是,这会在 Sql Profiler 中产生很多单独的 select 语句。使用下面的示例,NHibernate selects 来自 foo table,然后 "loops" 遍历结果集中的每一行,selecting 来自 bar、foobar 等。理想情况下,我希望这是一个使用连接的 select 语句,但我不确定这是否可能。

我认为问题可能在于映射中的 Not.LazyLoad() 方法。

鉴于这些 classes 和映射:

public class Foo
{
    public virtual int FooId {get; set;}
    public virtual Bar Bar {get; set;}
    public virtual Baz Baz {get; set;}
}

public class Bar
{
    public virtual int BarId {get; set;}
    public virtual FooBar FooBar {get; set;}
    ...
}

public class FooMap : ClassMap<Foo>{
    public FooMap(){
        Schema("my_schema");
        Table("foo");
        LazyLoad();
        Id(x => x.FooId).GeneratedBy.Identity().Column("foo_id");
        References(x => x.Bar).Column("bar").Not.LazyLoad();
        References(x => x.Baz).Column("baz").Not.LazyLoad();
    }
}

public class BarMap : ClassMap<Bar>{
    public BarMap(){
        Schema("my_schema");
        Table("bar");
        LazyLoad();
        Id(x => x.FooId).GeneratedBy.Identity().Column("bar_id");
        References(x => x.FooBar).Column("foo_bar").Not.LazyLoad();
    }
}

我正在通过以下方式使用 queryover 来填充 IEnumerable:

IEnumerable<Foo> foos;

using (ISession session = NHibernateSessionFactoryManager.Factory.OpenSession())
{
    foos = session.QueryOver<foos>()
        .Where(f => f.Baz.BazId == bazId).List();
}

这是我的 NHibernateSessionFactory 管理器class:

public class NHibernateSessionFactoryManager {
    private static readonly Object lockObject = new object();
    private static readonly ISessionFactory SessionFactory = BuildSessionFactory();

    private static ISessionFactory BuildSessionFactory(){
        lock (lockObject){//make sure only one session factory is created per start of the app
            return Fluently.Configure()
                .Database(MsSqlConfiguration.MsSql2008
                    .ConnectionString(c => c
                        .FromConnectionStringWithKey("foobardb")))
                .Mappings(m =>
                    m.FluentMappings.AddFromAssemblyOf<FooMap>())
                //.ExposeConfiguration(BuildSchema)
                .BuildSessionFactory();
        }
    }

    private static void BuildSchema(Configuration config) {
        // export the database schema
        new SchemaExport(config)
            .Create(true,false);
    }

    public static ISession OpenSession(){
        return Factory.OpenSession();
    }

    public static ISessionFactory Factory{
        get{
            return SessionFactory;
        }
    }
}

这可以通过 Futures 实现,请在下面找到完整的示例代码,

public class FooBar
{
    public virtual int FooBarId { get; set; }
    public virtual string FooBarName { get; set; }
}

public class FooBarMap : ClassMap<FooBar>
{
    public FooBarMap()
    {
        Table("foorbar");
        LazyLoad();
        Id(x => x.FooBarId).GeneratedBy.Identity().Column("foobar_id");
        Map(x => x.FooBarName);
    }
}

public class Bar
{
    public virtual int BarId {get; set;}
    public virtual FooBar FooBar {get; set;}
    public virtual string BarName { get; set; }
}

public class BarMap : ClassMap<Bar>
{
    public BarMap()
    {
        Table("bar");
        LazyLoad();
        Id(x => x.BarId).GeneratedBy.Identity().Column("bar_id");
        Map(x => x.BarName);
        References(x => x.FooBar).Column("foo_bar").Not.LazyLoad();
    }
}

public class Baz
{
    public virtual int BazId {get; set;}
    public virtual string BazName { get; set; }
}

public class BazMap : ClassMap<Baz>
{
    public BazMap()
    {
        Table("baz");
        LazyLoad();
        Id(x => x.BazId).GeneratedBy.Identity().Column("baz_id");
        Map(x => x.BazName);
    }
}

public class Foo
{
    public virtual int FooId { get; set; }
    public virtual Bar Bar { get; set; }
    public virtual Baz Baz { get; set; }
    public virtual string FooName { get; set; }
}

public class FooMap : ClassMap<Foo>
{
    public FooMap()
    {
        Table("foo");
        LazyLoad();
        Id(x => x.FooId).GeneratedBy.Identity().Column("foo_id");
        Map(x => x.FooName);
        References(x => x.Bar).Column("bar").Not.LazyLoad();
        References(x => x.Baz).Column("baz").Not.LazyLoad();
    }
}

这个测试使用了未来的查询,

[Test]
public void FutureTest()
{
    using (ISession session = _sessionFactory.OpenSession())
    {
        var tx = session.BeginTransaction();
        for (int i = 0; i < 5; i++)
        {
            var fb = new FooBar() {FooBarName = "FooBar_" + i};
            session.Save(fb);
            var bz = new Baz() { BazName = "Baz_" + i};
            session.Save(bz);
            var b = new Bar() { BarName = "Bar_" + i ,FooBar = fb};
            session.Save(b);
            var f = new Foo() { FooName = "Foo_" + i , Bar = b,Baz = bz};
            session.Save(f);
        }
        session.Flush();
        tx.Commit();
    }

    using (ISession session = _sessionFactory.OpenSession())
    {
        Foo fooAlias = null;
        Bar barAlias = null;
        Baz bazAlias = null;
        FooBar fooBarAlias = null;

        var tx = session.BeginTransaction();

        var fooList = session.QueryOver<Foo>(() => fooAlias)
            .JoinAlias(f => f.Bar, () => barAlias)
            .JoinAlias(f => f.Baz, () => bazAlias)
            .JoinAlias(f => f.Bar.FooBar, () => fooBarAlias)
            .Future<Foo>().Distinct().ToList();

        foreach (var foo in fooList)
        {
            Console.WriteLine(foo.FooName);
            Console.WriteLine(foo.Bar.BarName);
            Console.WriteLine(foo.Baz.BazName);
            Console.WriteLine(foo.Bar.FooBar.FooBarName);
        }
        session.Flush();
        tx.Commit();
    }
}

结果SQL是,

SELECT this_.foo_id as foo1_5_3_, this_.FooName as FooName5_3_, this_.bar as bar5_3_, this_.baz as baz5_3_, baralias1_.bar_id as bar1_1_0_, baralias1_.BarName as BarName1_0_, baralias1_.foo_bar as foo3_1_0_, foobaralia3_.foobar_id as foobar1_4_1_, foobaralia3_.FooBarName as FooBarName4_1_, bazalias2_.baz_id as baz1_2_2_, bazalias2_.BazName as BazName2_2_ FROM foo this_ inner join bar baralias1_ on this_.bar=baralias1_.bar_id inner join foorbar foobaralia3_ on baralias1_.foo_bar=foobaralia3_.foobar_id inner join baz bazalias2_ on this_.baz=bazalias2_.baz_id;

输出,

Foo_0
Bar_0
Baz_0
FooBar_0
Foo_1
Bar_1
Baz_1
FooBar_1
Foo_2
Bar_2
Baz_2
FooBar_2
Foo_3
Bar_3
Baz_3
FooBar_3
Foo_4
Bar_4
Baz_4
FooBar_4

如果您通过 Join 加载什么,请使用 QueryOver API 来完成工作:

foos = session.QueryOver<Foo>()
    .JoinAlias(x => x.Bar, () => barAlias)
    .JoinAlias(x => x.Bar, () => barAlias)
    .JoinAlias(() => barAlias.FooBar, () => fooBarAlias)
    .List();

如果您使用原始查询默认加载什么,请将映射更改为 Fetch.Join() 而不是 Not.LazyLoad()

注意重复数据,您可以阅读相关内容here。或者使用不同的投影。