gpt4 book ai didi

NHibernate QueryOver 对每个一对多集合进行排序

转载 作者:行者123 更新时间:2023-12-01 11:24:48 30 4
gpt4 key购买 nike

考虑这个人为的域:

namespace TryHibernate.Example {

public class Computer
{
public int Id { get; set; }
public IList<Device> Devices { get; set; }
}

public class Device
{
public int Id { get; set; }
public Maker Maker { get; set; }
}

public class Maker
{
public int Id { get; set; }
public string Name { get; set; }
}

} // namespace

如果我只查询所有计算机,它们的设备将随机排序。我想让他们按制造商的名字订购。我真的不能用 HasMany().OrderBy() 来做,因为据我所知,OrderBy 只能使用本地列(所以我可以按 Device.Id,例如)。此外,在每个查询的基础上控制排序会很好,所以我正在寻找 QueryOver 解决方案。

我能到达的最远的地方是:

using (ISessionFactory sessionFactory = Fluently.Configure()
.Database(SQLiteConfiguration.Standard.UsingFile("temp.sqlite").ShowSql())
.Mappings(m => m.AutoMappings.Add(
AutoMap.AssemblyOf<Computer>(new ExampleConfig())
.Conventions.Add(DefaultLazy.Never())
.Conventions.Add(DefaultCascade.All())))
.ExposeConfiguration(c => new SchemaExport(c).Create(true, true))
.BuildSessionFactory())
{
using (ISession db = sessionFactory.OpenSession())
{
Computer comp = new Computer();
comp.Devices = new List<Device>();
Device dev1 = new Device();
comp.Devices.Add(dev1);
dev1.Maker = new Maker() { Name = "IBM"};
Device dev2 = new Device();
comp.Devices.Add(dev2);
dev2.Maker = new Maker() { Name = "Acer"};
db.Persist(comp);
db.Flush();
}
using (ISession db = sessionFactory.OpenSession())
{ // This is the part I'm having trouble with:
Device devAlias = null;
Maker makerAlias = null;
IList<Computer> comps = db.QueryOver<Computer>()
.JoinAlias(c => c.Devices, () => devAlias)
.JoinAlias(() => devAlias.Maker, () => makerAlias)
.OrderBy(() => makerAlias.Name).Asc
.List();
Console.WriteLine(comps.Count);
foreach (Device dev in comps[0].Devices)
{
Console.WriteLine(dev.Maker.Name);
}
}
}

当然,它并没有按照我的意愿行事。它尝试按制造商的名称对整个列表进行排序。它也成功了,正如我从 SQL 中看到的那样,我实际上得到了一个无用的计算机笛卡尔积,其设备由设备制造商分类。

但随后它发出另一个 SQL 查询来获取设备,这次没有排序。我想 NHibernate 不知道我的连接是为了获取 child 。

问题是,我如何控制第二个查询?例如,按制造商的名称订购设备,或者让每个 Computer.Devices 列表只包含由 IBM 制造的设备(如果有的话)。我想我需要一个子查询,但我应该在哪里插入它呢?

为了完整起见,这是我的配置:

class ExampleConfig : DefaultAutomappingConfiguration
{
public override bool ShouldMap(Type type)
{
return type.Namespace == "TryHibernate.Example";
}
}

最佳答案

您可以通过干扰 Devicees 检索的 SQL 选择语句来实现此目的。对我来说最优雅的解决方案是使用自定义加载器。但这是
not supported through Fluent NHibernate .幸运的是,您可以在项目的某处编写单个 hbm.xml,然后像这样通过 fluent 添加它:

设备.hbm.xml

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
<class name="TryHibernate.Example.Device, TryHibernate" table="Device">
<id name="Id" type="System.Int32, mscorlib" column="Id" generator="identity" />
<property name="Name" />
<many-to-one name="Maker" column="MakerId" />
<many-to-one name="Computer" column="ComputerId" />
<loader query-ref="DeviceLoader" />
</class>

<sql-query name="DeviceLoader">
<return alias="dev" class="TryHibernate.Example.Device, TryHibernate" lock-mode="read">
<return-property name="Computer" column="ComputerId" />
<return-property name="Maker" column="MakerId" />
</return>

SELECT Device.Id AS {dev.Id}, Device.Name As {dev.Name}, Device.MakerId AS {dev.MakerId}, Device.ComputerId AS {dev.ComputerId}
FROM Device INNER JOIN Maker ON Maker.Id = Device.MakerId
WHERE Device.Id=?
ORDER BY Device.ComputerId, Maker.Name
</sql-query>
</hibernate-mapping>

然后在构建 session 工厂时做:

    using (ISessionFactory sessionFactory = Fluently.Configure()        .Database(SQLiteConfiguration.Standard.UsingFile("temp.sqlite").ShowSql())        .Mappings(m => m.AutoMappings.Add(            AutoMap.AssemblyOf(new ExampleConfig())                .Conventions.Add(DefaultLazy.Never())                .Conventions.Add(DefaultCascade.All())))        .Mappings(m => m.HbmMappings.AddFromAssembly(Assembly.GetExecutingAssembly()))        .ExposeConfiguration(c => new SchemaExport(c).Create(true, true))        .BuildSessionFactory())

Another thought that hit me is you could specify Subselect() for your mapping; like this:

public class DeviceMap : ClassMap<Device>
{
public DeviceMap()
{
Table("Device");
Subselect(@"
SELECT TOP 100 Device.Id , Device.Name, Device.MakerId , Device.ComputerId
FROM Device INNER JOIN Maker ON Maker.Id = Device.MakerId
ORDER BY Device.ComputerId, Maker.Name
");

Id(x => x.Id);
Map(x => x.Name);

References(x => x.Computer).Column("ComputerId");
References(x => x.Maker).Column("MakerId");
}

如您所见,我使用了 TOP 子句。这是必要的,因为 sub-select 语句变成了一个子查询,你可能知道

The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP, OFFSET or FOR XML is also specified.


还有一个方法是实现一个IInterceptor 接口(interface)并适本地改变SQL。像这样:

public class SqlStatementInterceptor : EmptyInterceptor
{
public override SqlString OnPrepareStatement(SqlString sql)
{
var result = sql;

if (sql.IndexOfCaseInsensitive("FROM Device") != -1)
{
var sqlText = string.Format("WITH cteDev AS ({0}{1}{0}){0}SELECT cteDev.* FROM cteDev INNER JOIN Maker ON cteDev.MakerId1_0_ = Maker.Id ORDER BY Maker.Name", Environment.NewLine, sql.ToString());

result = sql
.Insert(0, "WITH cteDev AS (")
.Append(")SELECT cteDev.* FROM cteDev INNER JOIN Maker ON cteDev.MakerId1_0_ = Maker.Id ORDER BY Maker.Name");
;

}

Trace.WriteLine(result.ToString());

return result;
}
}

*** 对于这个,您需要使用约定并了解 NHibernate 如何生成表和列的名称。

关于NHibernate QueryOver 对每个一对多集合进行排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38414904/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com