我正在努力学习如何使用Nhibernate的力量,在观看了一个turtorial后,我开始对它进行良好的抓握。
然而,有些事情困扰着我。
以下面的查询为例:
var query = "SELECT * " +
"from DAGE_I_KS WHERE DATO in (:orderYear));";
var session = mySessionFactory.OpenSession();
var result = session.CreateSQLQuery(query)
.AddEntity(typeof(DAGE_I_KS))
.SetString("orderYear", "2012")
.List<DAGE_I_KS>();
现在要开始工作,我必须执行以下步骤:
现在我必须为我想要做的每一个查询执行此操作。在一个程序中,您很可能会进行10次以上的查询(如果不是更多),这意味着我必须执行10次这些步骤。
对我而言似乎是不合逻辑的。而且我很难理解这可以为我节省一些时间和麻烦。
我的问题是,这是正确的,我必须每次都这样做,或者是否有一些我错过的东西,并且有更好的方法来使用Nhibernate,当你有costum querys。
答案 0 :(得分:1)
Runtime NH围绕会话(ISession)的概念展开。如您所知,您从会话工厂获得会话。
会话是您与NH交互以执行查询和保存数据的中心对象。会话具有刷新模式属性。
重要的是要知道通过使用查询从会话中获得的所有实体都链接到该会话。因此,如果修改了被提取的实体,即使您没有显式调用session.Update(obj),当刷新会话时,这些更改也将保持不变。因此,您实际必须调用的会话的唯一方法是session.saveOrUpdate(obj)和session.delete(obj),因为您必须向会话注册新创建的实体,并且必须注册实体以进行删除。刷新会话时,这些更改将保持不变。
尝试阅读会话刷新模式以及ITransaction接口。
现在,这与查询有什么关系?
好吧,我已经了解到最好将查询封装到查询对象中。这不是我的想法,而是我从Fabio Maulo的博客中找到的,或者Ayende不确定但是在寻找与NH相关的查询对象一词应该是提供信息的,如果你发现谁会赞同这个想法我会更新答案:)
很明显,查询对象需要会话才能工作。所以,你必须编写管道来给它一个。在我的实现中,查询对象将是:
public class ShiftByNameWithOccurences : AbstractQueryObject<IList<Shift>>
{
private string Name;
public ShiftByNameWithOccurences(string name)
{
this.Name = name;
}
public override IList<Shift> GetResult()
{
var list =
(
from shift in this.Session.Query<Shift>()
where shift.Name == this.Name
select shift
)
.Fetch(p => p.Occurrences)
.ToList();
return list;
}
}
显然,很容易为所有数据库实体实现通用名称查询。 在这个例子中,急切地加载了移位的出现,以避免N + 1选择问题。
很容易看出封装查询如何使您的OO驱动的应用程序设计受益。
此外,由于您拥有ISession实例,因此您可以使用您选择的NH查询方法。
供参考:
public abstract class AbstractQueryObject<TResult> : IQueryObject<TResult>
{
protected ISession Session;
public void Configure(object parameter)
{
if (!(parameter is ISession))
throw new ArgumentException(String.Format("Argument of wrong type. Expecting {0} but got {1}.", typeof(ISession), parameter != null ? parameter.GetType().ToString() : "null"), "parameter");
this.Session = parameter as ISession;
}
public abstract TResult GetResult();
}
public interface IQueryObject<TResult>
{
void Configure(object parameter);
TResult GetResult();
}
甚至,这个界面被设想为不与NH相关联,但也可以将它与EF一起使用,或者如果需要可以将其用于其他未来的ORM。抽象查询对象位于x.NH名称空间中:)
修改强>:
对于记录每个查询的SQL的任务,我建议使用interceptors来记录sql。
创建会话时注册每个会话拦截器。对于此任务,您应该只在会话中注册一个拦截器对象。然后,当您实例化查询对象时,将拦截器对象作为构造函数参数传递,或者在configure方法中传递任何您喜欢的方法。当执行查询时(在get result方法中) - 在开始时告诉拦截器对象你想要开始监听。在拦截器的实现中创建代码,然后将所有sql转发给侦听查询对象。在查询对象中使用sql创建自定义xml。查询完成后,从拦截器对象中取消注册查询对象。
但请记住,如果在查询对象退出后执行其他延迟加载sql语句,则不会记录get result方法。
。<强> E.g 强>:
public class UseCase
{
public void Method()
{
//when instantiating a session pass the interceptor to it.
//then, also pass this sniffer to the query objects you create.
//make the query objects listeners.
//when the query object is to be executed (start of the get result method)
//call the set active listener method on the sniff notifier given to the q.o.
//in the on prepare statement method of the q.o. do whatever with the sql.
SqlSniffer myInterceptor = new SqlSniffer();
var session = this.SessionFactory.OpenSession(myInterceptor);
}
}
public interface ISqlSniffListener
{
void OnPrepareStatement(string sql);
}
public interface ISqlSniffNotifier
{
void SetActiveListener(ISqlSniffListener listener);
}
public class SqlSniffer : EmptyInterceptor, ISqlSniffNotifier
{
private ISqlSniffListener ActiveListener;
public void SetActiveListener(ISqlSniffListener listener)
{
this.ActiveListener = listener;
}
public override NHibernate.SqlCommand.SqlString OnPrepareStatement(NHibernate.SqlCommand.SqlString sql)
{
if (this.ActiveListener != null)
this.ActiveListener.OnPrepareStatement(sql.ToString());
return base.OnPrepareStatement(sql);
}
}
或者甚至更好,实现一个未集成在查询对象中的侦听器(以尊重SRP)可能更好..但是你必须向它发出信号,然后......它可以是...如果您有一个负责执行查询对象的对象,这会为他们提供会话,并且还可能创建它,那么很容易;)
P.S。 我读过discussing the q.o.,而idea seems older。
是Ayende