nhibernate - 如何避免 NHibernate N+1 使用复合键

标签 nhibernate queryover select-n-plus-1

编辑 我为这个问题重新制作了整个项目。因此,我重新提出了这个问题。

我希望能够有效地避免 N+1 和笛卡尔连接将 4 级深度实体与第三级的复合键连接在一起。

我希望仅在几个查询中完成此操作,而不是延迟加载,而不仅仅是将所有表连接在一起。

A -(多)-> B -(多)-> C -(复合,单)-> D

就像是:

Select * From A Left Join B On A.Id = B.AId
Select * From B Left Join C On B.Id = C.BId Inner Join D On C.DId = D.Id

这是使用的代码
这是一个功能齐全的应用程序。
我使用 NuGet 安装 Sqlite x86、StructureMap、NHProf、Fluent NH。

StructureMapServiceLocator:
namespace MyTest.NHibernateTest
{
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Practices.ServiceLocation;
using StructureMap;

public class StructureMapServiceLocator : ServiceLocatorImplBase
{
    private readonly IContainer _container;

    public StructureMapServiceLocator(IContainer container)
    {
        _container = container;
    }

    public IContainer Container { get { return _container; } }

    protected override object DoGetInstance(Type serviceType, string key)
    {
        return string.IsNullOrEmpty(key)
                   ? _container.GetInstance(serviceType)
                   : _container.GetInstance(serviceType, key);
    }

    protected override IEnumerable<object> DoGetAllInstances(Type serviceType)
    {
        return _container.GetAllInstances(serviceType).Cast<object>().AsEnumerable();
    }

    public override TService GetInstance<TService>()
    {
        return _container.GetInstance<TService>();
    }

    public override TService GetInstance<TService>(string key)
    {
        return _container.GetInstance<TService>(key);
    }

    public override IEnumerable<TService> GetAllInstances<TService>()
    {
        return _container.GetAllInstances<TService>();
    }
}
}

应用程序注册表
namespace MyTest.NHibernateTest
{
using System;
using System.Collections.Generic;
using System.Linq;
using StructureMap.Configuration.DSL;
using FluentNHibernate.Cfg.Db;
using FluentNHibernate.Cfg;
using NHibernate;
using NHibernate.Tool.hbm2ddl;
using FluentNHibernate.Automapping;
using FluentNHibernate.Data;

public class AppRegistry : Registry
{
    public AppRegistry()
    {
        var dbConfiguration = SQLiteConfiguration.Standard
            .ConnectionString("Data Source=sqlite.db;Version=3;New=True;");
        dbConfiguration.ShowSql();

        var cfg = Fluently.Configure()
            .Database(dbConfiguration)
            .Mappings(m =>
            {
                m.AutoMappings.Add(AutoMap.AssemblyOf<Program>().Where(t =>
                {
                    return typeof(Entity).IsAssignableFrom(t);
                }));
            })
            .ExposeConfiguration(c =>
            {
                if (RebuildSchema.Value)
                    new SchemaExport(c).Create(false, true);
            });
        var sessionFactory = cfg.BuildSessionFactory();

        For<ISessionFactory>().Singleton().Use(sessionFactory);
        For<ISession>().HybridHttpOrThreadLocalScoped().Use(cx =>
        {
            var session = cx.GetInstance<ISessionFactory>().OpenSession();
            session.FlushMode = FlushMode.Commit;

            return session;
        });
    }
}
}

列出实体:
namespace MyTest.NHibernateTest.Entities
{
using System;
using System.Collections.Generic;
using System.Linq;
using FluentNHibernate.Data;

public class Listing : Entity
{
    public Listing()
    {
        Items = new List<ListingItem>();
    }
    public virtual IList<ListingItem> Items { get; set; }
}

public class ListingItem : Entity
{
    public ListingItem()
    {
        Values = new List<ListingItemValue>();
    }
    public virtual IList<ListingItemValue> Values { get; set; }
}

public class ListingItemValue : Entity
{
    public virtual ListingItem ListingItem { get; set; }
    public virtual ListingItemField ListingItemField { get; set; }
}

public class ListingItemField : Entity
{
    public virtual string Value { get; set; }
}
}

程序(控制台):
namespace MyTest.NHibernateTest
{
using System;
using System.Collections.Generic;
using System.Linq;
using StructureMap;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using Microsoft.Practices.ServiceLocation;
using NHibernate;
using System.Threading;
using NHibernate.Transform;
using MyTest.NHibernateTest.Entities;

public static class RebuildSchema
{
    public static bool Value { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        RebuildSchema.Value = true;
        Setup();
        BuildData();
        Work();
        Console.ReadLine();
    }

    static void Setup()
    {
        NHibernateProfiler.Initialize();

        ObjectFactory.Initialize(x =>
        {
            x.Scan(s =>
            {
                s.TheCallingAssembly();
                s.LookForRegistries();
            });
        });

        ServiceLocator.SetLocatorProvider(() => new StructureMapServiceLocator(ObjectFactory.Container));
    }

    static void BuildData()
    {
        var s = ObjectFactory.GetInstance<NHibernate.ISession>();
        using (var t = s.BeginTransaction())
        {
            var listing = new Listing();
            s.Save(listing);

            var item = new ListingItem();
            listing.Items.Add(item);
            s.Save(item);

            var item2 = new ListingItem();
            listing.Items.Add(item2);
            s.Save(item2);

            var field = new ListingItemField();
            field.Value = "A";
            s.Save(field);

            var field2 = new ListingItemField();
            field2.Value = "B";
            s.Save(field2);

            var value = new ListingItemValue();
            value.ListingItem = item;
            value.ListingItemField = field;
            item.Values.Add(value);
            s.Save(value);

            var value2 = new ListingItemValue();
            value2.ListingItem = item;
            value2.ListingItemField = field2;
            item.Values.Add(value2);
            s.Save(value2);

            var value3 = new ListingItemValue();
            value3.ListingItem = item2;
            value3.ListingItemField = field;
            item2.Values.Add(value3);
            s.Save(value3);

            t.Commit();
        }
    }

    static void Work()
    {
        var s = ObjectFactory.GetInstance<ISession>();
        IList<Listing> foo;
        using (var t = s.BeginTransaction())
        {
            foo = s.QueryOver<Listing>()
                .Left.JoinQueryOver<ListingItem>(x => x.Items)
                .Left.JoinQueryOver<ListingItemValue>(x => x.Values)
                .Left.JoinQueryOver<ListingItemField>(x => x.ListingItemField)
                .TransformUsing(Transformers.DistinctRootEntity)
                .List();
            t.Commit();
        }

        try
        {
            Thread.Sleep(100);
            var x1 = foo[0];
            Thread.Sleep(100);
            var x2 = x1.Items[0];
            Thread.Sleep(100);
            var x3 = x2.Values[0];
            Thread.Sleep(100);
            var x4 = x2.Values[0].ListingItemField.Value;
        }
        catch (Exception) { }
    }
}
}

最佳答案

您能否提供您的映射的详细信息。减少查询数量(不是一个,而是很少)的一种方法是在映射中使用批量大小功能。这将以比 N+1 更少的往返次数填充代理。但实际上应该有一个解决方案来使用 future 或类似的方式获取所有数据,所以请提供映射。

关于nhibernate - 如何避免 NHibernate N+1 使用复合键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9042078/

相关文章:

php - 如何将数据库中的信息添加到现有的对象数组中?

java - Hibernate 为@ManyToOne JPA 注释属性创建 N+1 查询

c# - NHibernate 不尊重创建数字列(对于小数)的精度和比例

c# - 如何动态构建 () => x.prop lambda 表达式?

nhibernate - NHibernate QueryOver在许多表上

nhibernate - 我在使用 Nhibernate Query Over fetch 时做错了什么吗?

c# - 为什么相同的 NHibernate 查询在 WinForm 项目中的运行速度比 MSTest 单元测试慢一个数量级?

sql - Nhibernate 是否容易受到 SQL 注入(inject)的影响?

linq - Linq到NHibernate项目状态?有贡献吗?铅?

database - 使用 NHibernate 创建数据库 View