gpt4 book ai didi

c# - 引用类时 NHibernate 映射问题(延迟加载问题?)

转载 作者:太空狗 更新时间:2023-10-30 00:36:25 25 4
gpt4 key购买 nike

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

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 返回到 Foo 的绑定(bind)(“BelongsTo”等),还是一种方法就足够了?或者我是否也需要在模型类中定义从 Foo 到 Bar 的关系,等等?以下是映射:

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 的函数,如下所示:

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 的列表在调试器中显示以下异常:

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 的:

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

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

最佳答案

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

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

session 在新罕布什尔州至关重要。当您传递信息(结果集)时, session 必须仍然打开。使用 NH 的正常编程模式是在请求开始时打开一个 session 并在结束时关闭它(使用 asp.net)或保持打开更长时间。

要修复您的代码,请将打开/关闭 session 移动到单例或可以处理该问题的包装器。或者将打开/关闭 session 移动到调用代码(但在一段时间内这会变得困惑)。为了普遍解决这个问题,存在几种模式。你可以查一下这个NHibernate Best Practices article这涵盖了所有内容。

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

使用 FluentNH 和一个简单的 Dao 包装器

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

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

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

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

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 最佳实践的内容(简而言之,因为我遗漏了接口(interface)、继承关系和静态与动态表)。

关于c# - 引用类时 NHibernate 映射问题(延迟加载问题?),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2011950/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com