关于c#:如何阻止NHibernate阅读子集合?

How to block NHibernate from reading child collection?

我的数据库里有文件清单。每个文档都有一个子元素集合。现在,我的设想是:-读取文档(仅从文档表)-关闭会话并让用户做一些工作。

但当我这样做时,文档试图在某些地方加载子元素。我不想要它。我只想明确地阅读儿童元素。对于第一部分,我只需要阅读简单的文档值。

所以有什么办法可以对NHibernate说:"嘿,不要读这本书!"?


将集合的延迟加载设置为LazyExtra,可能您的设置为NoLazy(也就是说,急加载)。

最好是设置为Extra,而不是只设置Lazy。因为当您只想获取子集合的.Count().Any()时,它将阻止nhibernate获取子集合的行。Extra就像懒惰的版本一样。

带着Nolazy/热切的装载:

1
var post = session.Get<Post>(1);

这将从发布表中读取一行,从数据库中读取评论表中的行,即使您没有从应用程序访问发布的子集合评论。

使用Lazy时,var post = session.Get(1)只从post s表中加载一行,nhibernate不会从数据库中读取post的子集合注释。

至于懒惰和多余的

使用懒惰:

1
var commentsCount = post.Comments.Count()

这将从数据库中加载日志的注释:

1
select * from comments where post_id = 1;

.Count()只发生在应用程序端。

使用extra,var commentsCount = post.Comments.Count()时,nhibernate只发出count查询,而不读取所有行。

1
select count(*) from comments where post_id = 1

下面是一个设置子集合加载机制的配置示例。如果使用nHibernate的自动应用,则在BeforeMapset事件上设置该设置:

enter image description here

当需要预先加载配置为LazyExtra的子集合时,请使用fetchmany


我发现了导致从数据库加载文档周期的原因,即使您不访问文档的Periods属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
namespace NHibernateFetchJoinTest2
{
    using System;

    using NHibernateFetchJoinTest2.DomainMapping;
    using NHibernateFetchJoinTest2.Domains;

    class MainClass
    {
        public static void Main(string[] args)
        {
            using (var session = Mapper.SessionFactory.OpenSession())
            {
                Console.WriteLine("SQL produced:");

                var d = session.Get<Document>(1);

                Console.ReadLine();

                //Console.WriteLine("Document's periods:");

                //foreach (var period in d.Periods)
                //{
                //    Console.WriteLine($"* {period.PeriodDescription}");
                //}

                Console.ReadLine();
            }
        }
    }
}

产生这种情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
SQL produced:
NHibernate:  
    SELECT
        document0_.Id as id1_0_1_,
        document0_.DocumentDescription as documentdescription2_0_1_,
        periods1_.DocumentId as documentid3_1_3_,
        periods1_.Id as id1_1_3_,
        periods1_.Id as id1_1_0_,
        periods1_.PeriodDescription as perioddescription2_1_0_
    FROM
        Document document0_
    left outer join
        Period periods1_
            on document0_.Id=periods1_.DocumentId
    WHERE
        document0_.Id=@p0;
    @p0 = 1 [Type: Int32 (0:0:0)]

您的映射如下所示。您的子集合Lazy加载设置为Lazy(与NoLazy相反),而其Fetch策略设置为Join。才智:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
namespace NHibernateFetchJoinTest2.DomainMapping.Mappings
{
    using NHibernate.Mapping.ByCode.Conformist;
    using NHibernateFetchJoinTest2.Domains;

    public class DocumentMapping : ClassMapping<Document>
    {
        public DocumentMapping()
        {
            Id(x => x.Id);

            Property(x => x.DocumentDescription);

            Bag(x => x.Periods, collectionMapping =>
            {
                collectionMapping.Inverse(true);
                collectionMapping.Key(k => k.Column("DocumentId"));

                collectionMapping.Lazy(NHibernate.Mapping.ByCode.CollectionLazy.Lazy);

                // Remove this. This causes Document's Periods to load,
                // even if child collection Periods is not accessed yet.
                // This is evident in SQL log, it shows LEFT JOIN Period.
                collectionMapping.Fetch(NHibernate.Mapping.ByCode.CollectionFetchMode.Join);
            }, mapping => mapping.OneToMany());
        }
    }

    public class PeriodMapping: ClassMapping<Period>
    {
        public PeriodMapping()
        {
            Id(x => x.Id);
            Property(x => x.PeriodDescription);
        }
    }
}

如果删除此项…

1
collectionMapping.Fetch(NHibernate.Mapping.ByCode.CollectionFetchMode.Join);

…子集合期间不是由其父级(文档)过早提取的:

1
2
3
4
5
6
7
8
9
10
SQL produced:
NHibernate:
    SELECT
        document0_.Id as id1_0_0_,
        document0_.DocumentDescription as documentdescription2_0_0_
    FROM
        Document document0_
    WHERE
        document0_.Id=@p0;
    @p0 = 1 [Type: Int32 (0:0:0)]

重复使用的步骤:https://github.com/michaelbuen/nhibernatefectjointest2


作为临时解决方案,我创建了一个简单的黑客程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Document
{
    IList<Periods> periods;
    public virtual IList<Period> Periods
    {
        get { return periods; }
        set { periods = value; }
    }

    public virtual void ResetPeriods()
    {
        periods = new List<Period>();
    }
}

我就是这样得到文件的:

1
2
3
4
5
6
7
8
9
10
11
12
13
db.BeginTransaction();
IList<Document> list = db.Get<Document>();
db.CommitTransaction();

List<Document> result = new List<Document>();
foreach (var item in list)
{
    item.ResetPeriods(); //todo: HACK! Preventing from lazy load of periods
    result.Add(item);
}


return result;

当然,这个集合被映射为lazy。子集合(句点)必须定义为back变量,因为它会阻止nhibernate代理使用属性getter。