NHibernate 投影查询
NHibernate query over projection
我有两个实体 A
和 B
,其中 A
与 B
具有一对多关系。我想创建一个 NHibernate 查询来获取所有 A
实体和所有 B
记录,其中:
A
实体处于活动状态
- 获取的
B
个实体在一个日期范围内(我有 2 个 c#
DateTime
个对象)。
实体A
+----+--------+
|编号 |活跃|
+----+--------+
| 1 | 1 |
| 2 | 0 |
| 3 | 1 |
+----+--------+
实体B
+----+--------+--------+------------+
|编号 |年份 |月 | foreign_id |
+----+--------+--------+------------+
| 1 | 2000 | 11 | 1 |
| 2 | 2001 | 12 | 2 |
| 3 | 2002 | 4 | 1 |
+----+--------+--------+------------+
到目前为止我试过这个:
return this.sessionFactory.GetCurrentSession()
.QueryOver<A>()
.Where(x => x.Active)
.JoinQueryOver(x => x.BList)
.WhereRestrictionOn(y => y.Year * 12 + y.Month) // the problem is here, exception below
.IsBetween(2000 * 12 + 1) // january 2000
.And(2010 * 12 + 3) // march 2010
.List();
System.InvalidOperationException: variable 'x' of type 'Domain.A' referenced from scope '', but it is not defined
一般来说,我不喜欢以月数计算所有日期的方法(我的应用程序不关心天数、小时数等...)但是,我不想更改我的映射现在(如下所示)。
我需要一些帮助来修复这段代码,或者建议我怎样才能做得更好(或者两者都更好)。
更多详情:
我的 c#
实体如下所示:
public class A
{
public virtual int Id { get; set; }
public virtual int Active { get; set; }
public virtual IEnumerable<B> BList { get; set; }
}
public class B
{
public virtual int Month { get; set; }
public virtual int Year { get; set; }
}
internal class AMapping: ClassMap<A>
{
public AMapping()
{
Table("AObjects");
Id(x => x.Id, "id");
Map(x => x.Active, "active");
HasMany(x => x.BList)
.Table("Bobjects")
.KeyColumn("foreign_id")
.Component(y => {
y.Map(b => b.Month, "month");
y.Map(b => b.Year, "year");
});
}
}
我认为一种方法是使用过滤器。首先要做的是通过过滤器 class 创建过滤器定义,如下所示:
public class MonthsFilter : FilterDefinition
{
public MonthsFilter()
{
WithName("MonthsFilter")
.AddParameter("startMonths", NHibernateUtil.Int32)
.AddParameter("endMonths", NHibernateUtil.Int32);
}
}
然后您需要通过 ApplyFilter
方法将过滤器添加到您的 ClassMap
for A,添加到 BList
属性:
internal class AMapping : ClassMap<A>
{
public AMapping()
{
Table("AObjects");
Id(x => x.Id, "id");
Map(x => x.Active, "active");
HasMany(x => x.BList)
.Table("BObjects")
.KeyColumn("foreign_id")
.Component(y => {
y.Map(b => b.Month, "month");
y.Map(b => b.Year, "year");
}).ApplyFilter<MonthsFilter>("year * 12 + month BETWEEN :startMonths and :endMonths");
}
}
最后,您需要在发出查询之前启用过滤器,类似于:
using (var session = sessionFactory.OpenSession())
{
// Enable filter and pass parameters
var startMonthsValue = 2000 * 12 + 1; // january 2000
var endMonthsValue = 2010 * 12 + 3; // march 2010
session.EnableFilter("MonthsFilter")
.SetParameter("startMonths", startMonthsValue)
.SetParameter("endMonths", endMonthsValue);
// Create and execute query (no filter for B needed here)
var list = session.QueryOver<A>()
.Fetch(x => x.BList).Eager // Eager fetch to avoid the N+1 problem due to BList lazy load
.Where(x => x.Active)
.TransformUsing(Transformers.DistinctRootEntity) // List only distinct A entities, to avoid duplicated entries due to eager fetch one-to-many relation
.List();
// Do whatever you want with the results
foreach (var item in list)
{
Console.WriteLine("A id: {0} - B children count: {1}", item.Id, item.BList.Count());
}
}
一个完整的工作示例:
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using FluentNHibernate.Mapping;
using NHibernate;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace NHibernateTests
{
public class A
{
public virtual int Id { get; set; }
public virtual bool Active { get; set; }
public virtual IEnumerable<B> BList { get; set; }
}
public class B
{
public virtual int Month { get; set; }
public virtual int Year { get; set; }
}
internal class AMapping : ClassMap<A>
{
public AMapping()
{
Table("AObjects");
Id(x => x.Id, "id");
Map(x => x.Active, "active");
HasMany(x => x.BList)
.Table("BObjects")
.KeyColumn("foreign_id")
.Component(y => {
y.Map(b => b.Month, "month");
y.Map(b => b.Year, "year");
}).ApplyFilter<MonthsFilter>("year * 12 + month BETWEEN :startMonths and :endMonths");
}
}
public class MonthsFilter : FilterDefinition
{
public MonthsFilter()
{
WithName("MonthsFilter")
.AddParameter("startMonths", NHibernateUtil.Int32)
.AddParameter("endMonths", NHibernateUtil.Int32);
}
}
class Program
{
static void Main(string[] args)
{
var sessionFactory = CreateNHibernateSessionFactory();
using (var session = sessionFactory.OpenSession())
{
// Enable filter and pass parameters
var startMonthsValue = 2000 * 12 + 1; // january 2000
var endMonthsValue = 2010 * 12 + 3; // march 2010
session.EnableFilter("MonthsFilter")
.SetParameter("startMonths", startMonthsValue)
.SetParameter("endMonths", endMonthsValue);
// Create and execute query (no filter needed here)
var list = session.QueryOver<A>()
.Fetch(x => x.BList).Eager // Eager fetch to avoid the N+1 problem due to BList lazy load
.Where(x => x.Active)
.TransformUsing(Transformers.DistinctRootEntity) // List only distinct A entities, to avoid duplicated entries due to eager fetch one-to-many relation
.List();
// Do whatever you want with the results
foreach (var item in list)
{
Console.WriteLine("A id: {0} - B children count: {1}", item.Id, item.BList.Count());
}
}
Console.WriteLine("Press ENTER to continue...");
Console.ReadLine();
}
static ISessionFactory CreateNHibernateSessionFactory()
{
FluentConfiguration fc = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2012.ConnectionString("Server=.\SQLEXPRESS;Database=NHibernateTests;Trusted_Connection=True;"))
.Mappings(m => {
m.FluentMappings
.AddFromAssembly(Assembly.GetExecutingAssembly());
});
var config = fc.BuildConfiguration();
return config.SetProperty(NHibernate.Cfg.Environment.ReleaseConnections, "on_close")
.BuildSessionFactory();
}
}
}
有关过滤器的更多信息:
- NHibernate docs - Chapter 20. Filtering data
- NHibernate - Retrieve parent / children with criteria applied only to children
- Syntax to define a NHibernate Filter with Fluent Nhibernate?
从 NHibernate 5.3 开始支持 QueryOver lambda 内部的计算。所以
.WhereRestrictionOn(y => y.Year * 12 + y.Month)
应该按原样工作。
我有两个实体 A
和 B
,其中 A
与 B
具有一对多关系。我想创建一个 NHibernate 查询来获取所有 A
实体和所有 B
记录,其中:
A
实体处于活动状态- 获取的
B
个实体在一个日期范围内(我有 2 个c#
DateTime
个对象)。
实体A +----+--------+ |编号 |活跃| +----+--------+ | 1 | 1 | | 2 | 0 | | 3 | 1 | +----+--------+ 实体B +----+--------+--------+------------+ |编号 |年份 |月 | foreign_id | +----+--------+--------+------------+ | 1 | 2000 | 11 | 1 | | 2 | 2001 | 12 | 2 | | 3 | 2002 | 4 | 1 | +----+--------+--------+------------+
到目前为止我试过这个:
return this.sessionFactory.GetCurrentSession()
.QueryOver<A>()
.Where(x => x.Active)
.JoinQueryOver(x => x.BList)
.WhereRestrictionOn(y => y.Year * 12 + y.Month) // the problem is here, exception below
.IsBetween(2000 * 12 + 1) // january 2000
.And(2010 * 12 + 3) // march 2010
.List();
System.InvalidOperationException: variable 'x' of type 'Domain.A' referenced from scope '', but it is not defined
一般来说,我不喜欢以月数计算所有日期的方法(我的应用程序不关心天数、小时数等...)但是,我不想更改我的映射现在(如下所示)。
我需要一些帮助来修复这段代码,或者建议我怎样才能做得更好(或者两者都更好)。
更多详情:
我的 c#
实体如下所示:
public class A
{
public virtual int Id { get; set; }
public virtual int Active { get; set; }
public virtual IEnumerable<B> BList { get; set; }
}
public class B
{
public virtual int Month { get; set; }
public virtual int Year { get; set; }
}
internal class AMapping: ClassMap<A>
{
public AMapping()
{
Table("AObjects");
Id(x => x.Id, "id");
Map(x => x.Active, "active");
HasMany(x => x.BList)
.Table("Bobjects")
.KeyColumn("foreign_id")
.Component(y => {
y.Map(b => b.Month, "month");
y.Map(b => b.Year, "year");
});
}
}
我认为一种方法是使用过滤器。首先要做的是通过过滤器 class 创建过滤器定义,如下所示:
public class MonthsFilter : FilterDefinition
{
public MonthsFilter()
{
WithName("MonthsFilter")
.AddParameter("startMonths", NHibernateUtil.Int32)
.AddParameter("endMonths", NHibernateUtil.Int32);
}
}
然后您需要通过 ApplyFilter
方法将过滤器添加到您的 ClassMap
for A,添加到 BList
属性:
internal class AMapping : ClassMap<A>
{
public AMapping()
{
Table("AObjects");
Id(x => x.Id, "id");
Map(x => x.Active, "active");
HasMany(x => x.BList)
.Table("BObjects")
.KeyColumn("foreign_id")
.Component(y => {
y.Map(b => b.Month, "month");
y.Map(b => b.Year, "year");
}).ApplyFilter<MonthsFilter>("year * 12 + month BETWEEN :startMonths and :endMonths");
}
}
最后,您需要在发出查询之前启用过滤器,类似于:
using (var session = sessionFactory.OpenSession())
{
// Enable filter and pass parameters
var startMonthsValue = 2000 * 12 + 1; // january 2000
var endMonthsValue = 2010 * 12 + 3; // march 2010
session.EnableFilter("MonthsFilter")
.SetParameter("startMonths", startMonthsValue)
.SetParameter("endMonths", endMonthsValue);
// Create and execute query (no filter for B needed here)
var list = session.QueryOver<A>()
.Fetch(x => x.BList).Eager // Eager fetch to avoid the N+1 problem due to BList lazy load
.Where(x => x.Active)
.TransformUsing(Transformers.DistinctRootEntity) // List only distinct A entities, to avoid duplicated entries due to eager fetch one-to-many relation
.List();
// Do whatever you want with the results
foreach (var item in list)
{
Console.WriteLine("A id: {0} - B children count: {1}", item.Id, item.BList.Count());
}
}
一个完整的工作示例:
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using FluentNHibernate.Mapping;
using NHibernate;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace NHibernateTests
{
public class A
{
public virtual int Id { get; set; }
public virtual bool Active { get; set; }
public virtual IEnumerable<B> BList { get; set; }
}
public class B
{
public virtual int Month { get; set; }
public virtual int Year { get; set; }
}
internal class AMapping : ClassMap<A>
{
public AMapping()
{
Table("AObjects");
Id(x => x.Id, "id");
Map(x => x.Active, "active");
HasMany(x => x.BList)
.Table("BObjects")
.KeyColumn("foreign_id")
.Component(y => {
y.Map(b => b.Month, "month");
y.Map(b => b.Year, "year");
}).ApplyFilter<MonthsFilter>("year * 12 + month BETWEEN :startMonths and :endMonths");
}
}
public class MonthsFilter : FilterDefinition
{
public MonthsFilter()
{
WithName("MonthsFilter")
.AddParameter("startMonths", NHibernateUtil.Int32)
.AddParameter("endMonths", NHibernateUtil.Int32);
}
}
class Program
{
static void Main(string[] args)
{
var sessionFactory = CreateNHibernateSessionFactory();
using (var session = sessionFactory.OpenSession())
{
// Enable filter and pass parameters
var startMonthsValue = 2000 * 12 + 1; // january 2000
var endMonthsValue = 2010 * 12 + 3; // march 2010
session.EnableFilter("MonthsFilter")
.SetParameter("startMonths", startMonthsValue)
.SetParameter("endMonths", endMonthsValue);
// Create and execute query (no filter needed here)
var list = session.QueryOver<A>()
.Fetch(x => x.BList).Eager // Eager fetch to avoid the N+1 problem due to BList lazy load
.Where(x => x.Active)
.TransformUsing(Transformers.DistinctRootEntity) // List only distinct A entities, to avoid duplicated entries due to eager fetch one-to-many relation
.List();
// Do whatever you want with the results
foreach (var item in list)
{
Console.WriteLine("A id: {0} - B children count: {1}", item.Id, item.BList.Count());
}
}
Console.WriteLine("Press ENTER to continue...");
Console.ReadLine();
}
static ISessionFactory CreateNHibernateSessionFactory()
{
FluentConfiguration fc = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2012.ConnectionString("Server=.\SQLEXPRESS;Database=NHibernateTests;Trusted_Connection=True;"))
.Mappings(m => {
m.FluentMappings
.AddFromAssembly(Assembly.GetExecutingAssembly());
});
var config = fc.BuildConfiguration();
return config.SetProperty(NHibernate.Cfg.Environment.ReleaseConnections, "on_close")
.BuildSessionFactory();
}
}
}
有关过滤器的更多信息:
- NHibernate docs - Chapter 20. Filtering data
- NHibernate - Retrieve parent / children with criteria applied only to children
- Syntax to define a NHibernate Filter with Fluent Nhibernate?
从 NHibernate 5.3 开始支持 QueryOver lambda 内部的计算。所以
.WhereRestrictionOn(y => y.Year * 12 + y.Month)
应该按原样工作。