sti*_*k81 4 .net c# mapping nhibernate fluent-nhibernate
我正在使用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; }
}
Run Code Online (Sandbox Code Playgroud)
我已经为这些类创建了映射.这真的是我想知道我是否做对了.我是否需要从Bar("BelongsTo"等)定义一个回到Foo的绑定,还是一种方法?或者我是否需要在模型类中定义从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);
}
}
Run Code Online (Sandbox Code Playgroud)
我有一个查询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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在,这是它失败的地方.从这个函数返回最初看起来很好,结果找到了所有.但是有一个问题 - Bar的列表在调试器中显示以下异常:
base {NHibernate.HibernateException} = {"正在初始化[MyNamespace.Foo#14] - 无法初始化角色集合:MyNamespace.Foo.Bars,没有关闭会话或会话"}
什么地方出了错?我没有使用延迟加载,所以如何在延迟加载时出现问题?Bar不应该与Foo一起装载吗?对我来说有趣的是,在生成查询中它没有要求Bar:
选择foo0_.Id为Id4_,foo0_.Name为Name4_,来自"Foo"foo0_,其中foo0_.Name = @ p0; @ p0 ='one'
对我来说更奇怪的是,如果我正在调试代码 - 逐步执行每一行 - 那么我就不会得到错误.我的理论是它在同一个会话期间有时间检查Bar's,因为事情变得越来越慢,但是我不知道......我是否需要告诉它来取得Bar - 显然?我现在尝试了各种解决方案,但感觉我在这里缺少一些基本的东西.
这是一个典型的问题.使用NHibernate或Fluent-NHibernate,您使用的每个映射到您的数据的类都会被装饰(这就是为什么它们需要是虚拟的).这在运行时发生.
您的代码清楚地显示了在using语句中打开和关闭会话.在调试时,调试器非常好(或不是)在using语句结束后保持会话打开(在您停止单步执行后调用清理代码).处于运行模式(不是单步执行)时,会话正确关闭.
会议对NH来说至关重要.传递信息(结果集)时,会话仍必须打开.NH的正常编程模式是在请求开始时打开会话并在结束时关闭它(使用asp.net)或保持打开更长时间.
要修复代码,可以将打开/关闭会话移动到单例或可以处理它的包装器.或者将打开/关闭会话移动到调用代码(但在一段时间内会变得混乱).为了解决这个问题,存在几种模式.您可以查阅NHibernate最佳实践文章,该文章涵盖了所有内容.
编辑:采取另一种极端:S#arp架构(下载)为您处理这些最佳实践和许多其他NH问题,完全掩盖了最终用户/程序员的NH复杂性.它有一点陡峭的学习曲线(包括MVC等)但是一旦你掌握了它......你就不能没有了.不确定它是否很容易与FluentNH混合.
请参阅评论为什么我添加了这个额外的"章节".这是DAL类的一个非常简单但可重用且可扩展的Dao包装器的示例.我假设您已经设置了FluentNH配置和典型的POCO和关系.
以下包装器是我用于简单项目的包装器.它使用了上面描述的一些模式,但显然并非都是为了保持简单.如果您想知道,此方法也可用于其他ORM.我们的想法是为会话创建单例,但仍然保持关闭会话的能力(以节省资源),而不必担心必须重新打开.我把代码留给了关闭会话,但那只是几行.这个想法如下:
// 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);
Run Code Online (Sandbox Code Playgroud)
然后,在您的调用代码中,它看起来像这样:
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();
Run Code Online (Sandbox Code Playgroud)
很简单,不是吗?这个想法的进步是为每个POCO创建特定的Dao,它继承自上面的一般Dao类并使用一个访问器类来获取它们.这使得更容易添加特定于每个POCO的任务,这基本上就是NH最佳实践的意义(简而言之,因为我遗漏了接口,继承关系和静态与动态表).
| 归档时间: |
|
| 查看次数: |
2718 次 |
| 最近记录: |