引用类时的 NHibernate 映射问题(延迟加载问题?) [英] NHibernate mappings issue when referencing class (lazy load issue?)

查看:25
本文介绍了引用类时的 NHibernate 映射问题(延迟加载问题?)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 NHibernate + Fluent 来处理我的数据库,但在查询引用其他数据的数据时遇到了问题.我的简单问题是:我需要在映射中定义一些BelongsTo"等,还是在一侧定义引用就足够了(参见下面的映射示例)?如果是这样 - 如何?如果没有,请继续阅读.. 看看这个简化的例子 - 从两个模型类开始:

I'm using NHibernate + Fluent to handle my database, and I've got a problem querying for data which references other data. My simple question is: Do I need to define some "BelongsTo" etc in the mappings, or is it sufficient to define references on one side (see mapping sample below)? If so - how? If not please keep reading.. Have a look at this simplified example - starting with two model classes:

public class Foo
{
    private IList<Bar> _bars = new List<Bar>();

    public int Id { get; set; }
    public string Name { get; set; }
    public IList<Bar> Bars
    {
        get { return _bars; }
        set { _bars = value; }
    }
}

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

我已经为这些类创建了映射.这真的是我想知道我是否做对的地方.我是否需要定义从 Bar(BelongsTo"等)返回到 Foo 的绑定,还是一种方法就足够了?或者我是否也需要在模型类中定义从 Foo 到 Bar 的关系等?这是映射:

I have created mappings for these classes. This is really where I'm wondering whether I got it right. Do I need to define a binding back to Foo from Bar ("BelongsTo" etc), or is one way sufficient? Or do I need to define the relation from Foo to Bar in the model class too, etc? Here are the mappings:

public class FooMapping : ClassMap<Foo>
{
    public FooMapping()
    {
        Not.LazyLoad();
        Id(c => c.Id).GeneratedBy.HiLo("1");
        Map(c => c.Name).Not.Nullable().Length(100);
        HasMany(x => x.Bars).Cascade.All();
    }
}

public class BarMapping : ClassMap<Bar>
{
    public BarMapping()
    {
        Not.LazyLoad();
        Id(c => c.Id).GeneratedBy.HiLo("1");
        Map(c => c.Name).Not.Nullable().Length(100);
    }
}

我有一个查询 Foo 的函数,如下所示:

And I have a function for querying for Foo's, like follows:

public IList<Foo> SearchForFoos(string name)
{
    using (var session = _sessionFactory.OpenSession())
    {
        using (var tx= session.BeginTransaction())
        {
            var result = session.CreateQuery("from Foo where Name=:name").SetString("name", name).List<Foo>();
            tx.Commit();
            return result;
        }
    }        
}

现在,这是它失败的地方.这个函数的返回最初看起来很好,结果找到了.但是有一个问题 - Bar 的列表在调试器中显示以下异常:

Now, this is where it fails. The return from this function initially looks all fine, with the result found and all. But there is a problem - the list of Bar's has the following exception shown in debugger:

base {NHibernate.HibernateException} = {"Initializing[MyNamespace.Foo#14]-未能延迟初始化角色集合:MyNamespace.Foo.Bars,没有会话或会话被关闭"}

base {NHibernate.HibernateException} = {"Initializing[MyNamespace.Foo#14]-failed to lazily initialize a collection of role: MyNamespace.Foo.Bars, no session or session was closed"}

出了什么问题?我没有使用延迟加载,那么延迟加载怎么会出错呢?Bar 不应该与 Foo 一起加载吗?对我来说有趣的是,在生成查询中它不要求 Bar's:

What went wrong? I'm not using lazy loading, so how could there be something wrong in the lazy loading? Shouldn't the Bar's be loaded together with the Foo's? What's interesting to me is that in the generate query it doesn't ask for Bar's:

select foo0_.Id as Id4_, foo0_.Name as Name4_ from "Foo" foo0_ where foo0_.Name=@p0;@p0 = 'one'

select foo0_.Id as Id4_, foo0_.Name as Name4_ from "Foo" foo0_ where foo0_.Name=@p0;@p0 = 'one'

对我来说更奇怪的是,如果我正在调试代码 - 单步执行每一行 - 那么我就不会收到错误消息.我的理论是它在同一会话中以某种方式有时间检查 Bar's 因为事情进展得更慢,但我不知道..我是否需要告诉它也获取 Bar's - 明确的?我现在尝试了各种解决方案,但感觉这里缺少一些基本的东西.

What's even more odd to me is that if I'm debugging the code - stepping through each line - then I don't get the error. My theory is that it somehow gets time to check for Bar's during the same session cause things are moving slower, but I dunno.. Do I need to tell it to fetch the Bar's too - explicitly? I've tried various solutions now, but it feels like I'm missing something basic here.

推荐答案

这是一个典型的问题.使用 NHibernate 或 Fluent-NHibernate,你使用的映射到你的数据的每个类都用很多东西装饰(这就是为什么它们需要是虚拟的).这一切都在运行时发生.

This is a typical problem. Using NHibernate or Fluent-NHibernate, every class you use that maps to your data is decorated (which is why they need to be virtual) with a lot of stuff. This happens all at runtime.

您的代码在 using 语句中清楚地显示了会话的打开和关闭.在调试时,调试器非常好(或不是)在 using 语句结束后保持会话打开(在您停止单步执行后调用清理代码).在运行模式下(不单步执行),您的会话已正确关闭.

Your code clearly shows an opening and closing of a session in a using statement. When in debugging, the debugger is so nice (or not) to keep the session open after the end of the using statement (the clean-up code is called after you stop stepping through). When in running mode (not stepping through), your session is correctly closed.

会议在 NH 中至关重要.当您传递信息(结果集)时,会话必须仍处于打开状态.使用 NH 的正常编程模式是在请求开始时打开会话并在结束时关闭它(使用 asp.net)或保持打开一段时间.

The session is vital in NH. When you are passing on information (the result set) the session must still be open. A normal programming pattern with NH is to open a session at the beginning of the request and close it at the end (with asp.net) or keep it open for a longer period.

要修复您的代码,请将打开/关闭会话移至单例或可以处理此问题的包装器.或者将打开/关闭会话移动到调用代码(但过一会儿这会变得混乱).为了一般地解决这个问题,存在几种模式.您可以查看这篇 NHibernate 最佳实践文章,其中涵盖了所有内容.

To fix your code, either move the open/close session to a singleton or to a wrapper which can take care of that. Or move the open/close session to the calling code (but in a while this gets messy). To fix this generally, several patterns exist. You can look up this NHibernate Best Practices article which covers it all.

采取另一个极端:S#arp 架构 (download) 为您处理这些最佳实践和许多其他 NH 问题,完全掩盖了最终用户/程序员的 NH 复杂性.它有一点陡峭的学习曲线(包括 MVC 等),但是一旦你掌握了它的窍门......你就不能再没有了.不确定它是否很容易与 FluentNH 混合.

Taken to another extreme: the S#arp architecture (download) takes care of these best practices and many other NH issues for you, totally obscuring the NH intricacies for the end-user/programmer. It has a bit of a steep learning curve (includes MVC etc) but once you get the hang of it... you cannot do without anymore. Not sure if it is easily mixed with FluentNH though.

查看评论了解我为什么添加这个额外的章节".下面是一个用于 DAL 类的非常简单但可重用和可扩展的 Dao 包装器示例.我假设您已经设置了 FluentNH 配置以及典型的 POCO 和关系.

See comments for why I added this extra "chapter". Here's an example of a very simple, but reusable and expandable, Dao wrapper for your DAL classes. I assume you have setup your FluentNH configuration and your typical POCO's and relations.

以下包装器是我用于简单项目的包装器.它使用了上面描述的一些模式,但显然不是为了保持简单.如果您想知道,此方法也可用于其他 ORM.这个想法是为会话创建单例,但仍然保持关闭会话的能力(以节省资源)而不必担心必须重新打开.我省略了关闭会话的代码,但这只会是几行.思路如下:

The following wrapper is what I use for simple projects. It uses some of the patterns described above, but obviously not all to keep it simple. This method is also usable with other ORM's in case you'd wonder. The idea is to create singleton for the session, but still keep the ability to close the session (to save resources) and not worry about having to reopen. I left the code out for closing the session, but that'll be only a couple of lines. The idea is as follows:

// the thread-safe singleton
public sealed class SessionManager
{
    ISession session;
    SessionManager()
    {
        ISessionFactory factory = Setup.CreateSessionFactory();
        session = factory.OpenSession();
    }

    internal ISession GetSession()
    {
        return session;
    }

    public static SessionManager Instance
    {
        get
        {
            return Nested.instance;
        }
    }

    class Nested
    {
        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static Nested()
        {
        }

        internal static readonly SessionManager instance = new SessionManager();
    }
}


// the generic Dao that works with your POCO's
public class Dao<T>
    where T : class
{
    ISession m_session = null;

    private ISession Session
    {
        get
        {
            // lazy init, only create when needed
            return m_session ?? (m_session = SessionManager.Instance.GetSession());
        }
    }

    public Dao() { }

    // retrieve by Id
    public T Get(int Id)
    {
        return Session.Get<T>(Id);
    }

    // get all of your POCO type T
    public IList<T> GetAll(int[] Ids)
    {
        return Session.CreateCriteria<T>().
            Add(Expression.In("Id", Ids)).
            List<T>();
    }

    // save your POCO changes
    public T Save(T entity)
    {
        using (var tran = Session.BeginTransaction())
        {
            Session.SaveOrUpdate(entity);
            tran.Commit();
            Session.Refresh(entity);
            return entity;
        }
    }

    public void Delete(T entity)
    {
        using (var tran = Session.BeginTransaction())
        {
            Session.Delete(entity);
            tran.Commit();
        }
    }

    // if you have caching enabled, but want to ignore it
    public IList<T> ListUncached()
    {
        return Session.CreateCriteria<T>()
            .SetCacheMode(CacheMode.Ignore)
            .SetCacheable(false)
            .List<T>();
    }

    // etc, like:
    public T Renew(T entity);
    public T GetByName(T entity, string name);
    public T GetByCriteria(T entity, ICriteria criteria);

然后,在您的调用代码中,它看起来像这样:

Then, in your calling code, it looks something like this:

Dao<Foo> daoFoo = new Dao<Foo>();
Foo newFoo = new Foo();
newFoo.Name = "Johnson";
daoFoo.Save(newFoo);         // if no session, it creates it here (lazy init)

// or:
Dao<Bar> barDao = new Dao<Bar>();
List<Bar> allBars = barDao.GetAll();

很简单,不是吗?这个想法的进步是为从上述通用 Dao 类继承的每个 POCO 创建特定的 Dao,并使用访问器类来获取它们.这使得添加特定于每个 POCO 的任务变得更加容易,这基本上就是 NH Best Practices 的内容(简而言之,因为我遗漏了接口、继承关系以及静态与动态表).

Pretty simple, isn't it? The advancement to this idea is to create specific Dao's for each POCO which inherit from the above general Dao class and use an accessor class to get them. That makes it easier to add tasks that are specific for each POCO and that's basically what NH Best Practices was about (in a nutshell, because I left out interfaces, inheritance relations and static vs dynamic tables).

这篇关于引用类时的 NHibernate 映射问题(延迟加载问题?)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆