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。

Nhibernate C#为每个查询生成XML文件

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在讨论这个问题,但这个想法似乎更古老了。