我目前正在使用nhibernate 3.2击中SQLServer数据库,我正在尝试使用多Queary's and Futures来加载子女收藏。
无论如何,我都可以使用linq进行NHIBERNATE来工作,但是当查看SQL被发送到数据库时,看起来好像它正在加载所有父对象,而除了子Collection for for Child collection fet fitch(如它急切地加载)。我很好奇是否可以更改此行为以仅拉出所需的子对象列。
这是一个说明此问题的代码的示例。
public class Parent : Entity
{
public virtual string Name { get; set; }
public virtual IList<Child> Children { get; set; }
}
public class Child : Entity
{
public virtual int Age { get; set; }
public virtual string Name { get; set; }
public virtual Parent Parent { get; set; }
}
public class ChildClassMap : ClassMap<Child>
{
public ChildClassMap()
{
Id(x => x.Id,"Id");
Map(x => x.Age);
Map(x => x.Name);
this.References(x => x.Parent).Column("ParentId").ForeignKey("Id");
}
}
public class ParentClassMap : ClassMap<Parent>
{
public ParentClassMap()
{
Id(x => x.Id, "Id");
Map(x => x.Name);
this.HasMany(x => x.Children).KeyColumn("ParentId");
}
}
public class FamilyRepository : NHibernateRepository<Parent>
{
public Parent GetParent(int id)
{
using (var session = this.Session.OpenSession())
{
var parent = session.Query<Parent>()
.Where(p => p.Id == id);
parent.FetchMany(x => x.Children)
.ToFuture();
return parent.ToFuture().SingleOrDefault();
}
}
}
测试用例
[TestClass]
public class FamilyTests
{
[TestMethod]
public void Should_Get_Parent_And_Children()
{
// arrange
var repo = new FamilyRepository();
// act
var parent = repo.GetParent(1);
// assert
Assert.AreNotEqual(null, parent);
Assert.AreEqual("TheOldOne", parent.Name);
Assert.AreEqual(3, parent.Children.Count);
Assert.AreEqual(4, parent.Children[1].Age);
Assert.AreEqual("TheMiddleOne", parent.Children[1].Name);
}
}
sql:
CREATE TABLE [dbo].[Parent](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](50) NOT NULL,
CONSTRAINT [PK_Parent] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Child](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ParentId] [int] NOT NULL,
[Age] [int] NOT NULL,
[Name] [varchar](50) NOT NULL,
CONSTRAINT [PK_Child] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[Child] WITH CHECK ADD CONSTRAINT [FK_Child_Parent] FOREIGN KEY([ParentId])
REFERENCES [dbo].[Parent] ([Id])
GO
ALTER TABLE [dbo].[Child] CHECK CONSTRAINT [FK_Child_Parent]
GO
Set Identity_Insert [dbo].[Parent] on
insert into [dbo].[Parent]
(Id, Name)
values (1, 'TheOldOne');
insert into [dbo].[Parent]
(Id, Name)
values (2, 'TheOtherOne');
Set Identity_Insert [dbo].[Parent] off
GO
Set Identity_Insert [dbo].[Child] on
insert into [dbo].[Child]
(Id, ParentId, Age, Name)
values(1,1,3,'TheYoungOne')
insert into [dbo].[Child]
(Id, ParentId, Age, Name)
values(2,1,4,'TheMiddleOne')
insert into [dbo].[Child]
(Id, ParentId, Age, Name)
values(3,1,7,'TheFirstOne')
Set Identity_Insert [dbo].[Child] off
SQL Profiler的输出是:
exec sp_executesql N'
select parent0_.Id as Id3_0_, children1_.Id as Id2_1_, parent0_.Name as Name3_0_, children1_.Age as Age2_1_, children1_.Name as Name2_1_, children1_.ParentId as ParentId2_1_, children1_.ParentId as ParentId0__, children1_.Id as Id0__
from [Parent] parent0_ left outer join [Child] children1_ on parent0_.Id=children1_.ParentId where parent0_.Id=@p0;
select parent0_.Id as Id3_, parent0_.Name as Name3_ from [Parent] parent0_ where parent0_.Id=@p1;
',N'@p0 bigint,@p1 bigint',@p0=1,@p1=1
有人有任何建议吗?
感谢您的时间
只需将代码缩短到
public Parent GetParentWithChildrenInitialised(int id)
{
using (var session = SessionFactory.OpenSession())
{
return session.Query<Parent>()
.Where(p => p.Id == id)
.FetchMany(x => x.Children)
.SingleOrDefault();
}
}
我的个人会摆脱存储库,因为它添加了不必要的抽象,并且使调整性能更加困难,Isession已经像呼吸道一样。
一个更好的选择是使用session.Get(parentId);
,因为如果需要孩子,则使用LVL1/会话缓存或上面的查询。
还使用SessionFactory创建会话,因为它是螺纹安全,sessions却不是。