Nhibernate C#为每个查询生成XML文件
本文关键字:XML 文件 查询 Nhibernate | 更新日期: 2023-09-27 17:57:30
我正在努力学习如何使用Nhibernate的力量,在观看了一部turtoriar之后,我开始很好地掌握它。
然而,有一些事情困扰着我。
以以下查询为例:
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>();
现在,为了实现这一点,我必须执行以下步骤:
- 创建一个xml文件,映射我要使用的对象
- 创建对象类文件
- 创建查询并执行它,然后将结果插入对象(或列表)
现在我必须对我想做的每一个查询都这样做。在一个程序中,你很可能会做10+个查询(如果不是更多的话),这意味着我必须做10次这些步骤。
对我来说,这似乎不合逻辑。我很难理解这能为我节省一些时间和麻烦。
我的问题是,我每次都必须这样做是正确的吗?还是我错过了什么?当你有costum查询时,有没有更好的方法来使用Nhibernate。
Runtime NH围绕会话(ISession)的概念展开。正如您所知,您可以从会话工厂获得会话。
会话是与NH交互以执行查询和保存数据的中心对象。会话具有刷新模式属性。
重要的是要知道,通过使用查询从会话中获得的所有实体都链接到该会话。所以,如果您修改了一个已提取的实体,即使您没有显式调用会话。更新(obj),当刷新会话时,这些更改将被持久化。因此,您实际上必须调用的会话的唯一方法是session.saveOrUpdate(obj)和session.delete(obg),因为您必须向会话注册新创建的实体,并且必须注册要删除的实体。刷新会话时,这些更改将被持久化。
试着阅读会话刷新模式,以及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的任务,我可以建议使用拦截器来记录SQL。
在创建会话时注册每个会话的拦截器。对于这个任务,您应该只向会话注册一个拦截器对象。然后,当您实例化查询对象时,将拦截器对象作为构造函数参数传入,或者在您喜欢的configure方法中传入。当查询被执行时(在get-result方法中)-在开始时告诉拦截器对象您想要开始侦听。在拦截器的实现中,创建代码,然后将所有sql转发到侦听查询对象。在查询对象中,使用sql创建一个自定义xml。查询完成后,从拦截器对象中注销查询对象。
但请记住,如果在查询对象退出后执行额外的延迟加载sql语句,则不会记录get-result方法。
例如:
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。我读到的是Ayende在讨论这个问题,但这个想法似乎更古老了。