NHibernate的引用上课的时候​​映射的问题(延迟加载的问题?)问题、加载、时候、NHibernate

2023-09-03 02:53:09 作者:有话床上说

我使用NHibernate +流利的处理我的数据库,我有一个问题在查询它引用的其他数据。我简单的问题:我需要定义一些的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; }
}

我对这些类创建映射。这是真的,我不知道我是否是正确的。我需要定义一个从捆扎机回美孚(的belongsTo等),或者是一种方式是否足够?或者,我需要定义从富的关系模型中的类扎得等?下面是映射:

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);
    }
}

和我有一个函数用于查询的富的,就像如下:

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;
        }
    }        
}

现在,这是它失败。从这个函数的返回最初看起来都很好,结果发现所有。但是有一个问题 - 栏的名单已在调试器显示以下异常:

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:

基地{NHibernate.HibernateException} = {初始化[MyNamespace.Foo#14] -failed懒洋洋地初始化角色的集合: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"}

出了什么问题?我不使用延迟加载,所以怎么可能有一些错误的延迟加载?如果没有律师的可与Foo的一起装?有趣的对我来说,在生成查询它不要求律师的:

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:

选择foo0_.Id为Id4_,foo0_.Name为Name4_从富foo0_哪里foo0_.Name=@p0; @ P0 =一

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

什么是更奇怪,我是说,如果我调试了code - 通过每一行步 - 然后我没有得到这个错误。我的理论是,它在某种程度上得到的时间相同的会话事业事情正在朝着更慢时检查酒吧的,但我不知道......我需要告诉它取了酒吧的太 - 明确?我已经试过各种解决方案了,但感觉就像我失去了一些东西基本在这里。

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和流利,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.

您code清楚地显示了一个会话中使用的语句的开幕式和闭幕式。当调试,调试器是如此漂亮(或没有),以保持该会话的using语句(清理code被调用后停止单步调试)结束后开放。当运行模式(不是单步调试),您的会话正确关闭。

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.

要解决您的code,要么将打开/关闭会话单身或包装,可采取的照顾。或将打开/关闭会话调用code(但在同时,这就会变得混乱)。要普遍解决此问题,几种模式存在。你可以看一下这个 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.

编辑:带到另一个极端:the小号#ARP建筑(下载)照顾这些最佳实践和许多其他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类的一个实例。我假设你已经设置你的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的情况下,你会想知道使用。我们的想法是创造了单届会,但仍保持关闭会话(节约资源),而不是担心有重新的能力。我离开了code OUT的会议结束时,但是那将是只有几行。的思想如下:

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);

然后,在你调用code,它看起来是这样的:

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();

pretty的简单,不是吗?推进这个想法是创建一个特定的道的每一个从上述的一般DAO类继承和使用一个访问器类,让他们POCO。这使得它更容易添加了特定于每个POCO而这基本上是NH最佳实践是关于任务(简单地说,因为我离开了接口,继承关系和静态VS动态表)。

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).