【问题标题】:Fluent NHibernate does not create IN part of WHERE clauseFluent NHibernate 不会创建 WHERE 子句的 IN 部分
【发布时间】:2013-01-16 20:18:45
【问题描述】:

我有 Fluent NHibernate Linq 查询,我根据运行时数组检查值。一个基本的例子是这样的:

var array = [1,2,3,4,5,6];
using (var session = SessionProvider.SessionFactory.OpenSession())
{
  return session.Query<MyObject>().Where(x => array.Contains(x.CompareVal)).ToList();
}

我希望生成的 SQL 语句看起来像这样:

SELECT CompareVal, Column1, Column2
FROM MyObject
WHERE CompareVal IN (1,2,3,4,5,6)

但是,我发现生成的 SQL 语句只是发出 WHERE 子句(通过在 Profiler 中观察证明)并选择整个表,然后似乎在获取所有数据后在内存中运行过滤器返回。

需要注意的一点 - 我有一个通用存储库类,所有这些调用都通过它汇集。查询方法如下:

public IList<T> Query(Func<T, bool> criteria)
{
  using (var session = SessionProvider.SessionFactory.OpenSession())
  {
    return session.Query<T>().Where(criteria).ToList();
  }
}

显然,这(缺少 where 子句)在具有大量数据的表中是不可接受的。我可以做些什么来强制 NHibernate 使用 WHERE 子句正确生成查询并仍然为存储库保留通用模式?

【问题讨论】:

  • Fluent NH 与 linq 提供程序无关。你必须使用linq吗? NH 有更多功能,QueryOver
  • @dotjoe 是的,Linq 对于这个系统的架构来说是必不可少的(或者我们会使用存储过程)。此调用也是作为通用存储库调用的一部分进行的。 QueryOver 不能使用,因为我传递的是 T 而不是引用类型。
  • 可能是数组类型与 CompareVal 类型匹配的问题。你能给出 array 的确切声明吗?您提供的那个似乎没有通过语法检查。
  • 我有 2 种类型的数组 - 两者都是简单类型。整数和字符串

标签: c# sql nhibernate fluent


【解决方案1】:

任意使用:

 return session.Query<MyObject>().Where(x => array.Any(y => y == x.CompareVal)).ToList();

您的存储库模式(使用普通 Func)会自动将您的查询具体化到列表中,如果您想延迟执行某些内容,请使用 IQueryable,不要仅使用 Func

注意事项 - 我有一个通用存储库类 这些电话通过汇集。查询方法如下:

public IList<T> Query(Func<T, bool> criteria)
{
  using (var session = SessionProvider.SessionFactory.OpenSession())
  {
    return session.Query<T>().Where(criteria).ToList();
  }
}

您的存储库只是模仿 NHibernate 开箱即用的内容

【讨论】:

  • 刚刚尝试过(通过更改我的 Func 再次通过通用存储库)并且没有任何区别 - 结果相同
  • 好的,谢谢 Michael - 如果我不使用模板化的通用存储库并传递一个具体类型而不是 T,那么它就可以工作。糟糕的解决方案,并且在一定程度上破坏了系统的设计,但我设法对其进行了重构,因此它仍然整洁且易于维护。附:不是对开箱即用功能的模仿 - 包装类允许我通过依赖注入将 SessionFactory 的知识从调用者那里抽象出来,这意味着调用者可以将 GenericRepository 用于任何具体的 POCO。
  • 我还制作了一个存储库组件(无耻插件):github.com/MichaelBuen/ToTheEfnhX 为了使存储库组件真正与 SessionFactory 无关,同时为 NHibernate 制作存储库组件,我还使存储库模式与实体框架兼容。这样做,它使存储库模式保持诚实,即它必须跨不同的 ORM 工作,不了解 SessionFactory 或 DbContext
  • ToTheEfnhX 存储库组件示例用法在这里:ienablemuch.com/2012/06/…
  • 我无法在 ToTheEfnhX 存储库组件上抽象出来的唯一部分是急切加载机制。很容易在两个 ORM 上的一个级别上抽象出急切加载机制,而对于两个或更多级别则很难。实际上是 NHibernate 很难抽象出来,在实体框架中,对更深层次的对象进行预加载仅仅是: db.Set().Include("Comments").Include("Answers").Include("答案。评论”)。在 NHibernate 中,你必须这样做:ienablemuch.com/2012/08/eager-loading-repository.html
【解决方案2】:

如果您将 Query 方法更改为以下内容会有所不同吗?

public IList<T> Query(Expression<Func<T, bool>> criteria)
{
  using (var session = SessionProvider.SessionFactory.OpenSession())
  {
    return session.Query<T>().Where(criteria).ToList();
  }
}

这就是我通常使用通用查询的方式:

    public List<TOut> GetEntitiesLinq<TIn,TOut>(Expression<Func<IQueryable<TIn>,IQueryable<TOut>>> myFunc)
    {
        var t = (myFunc.Compile())(_session.Query<TIn>()) ;
        return t.ToList();
    }

那么我将如何在您的情况下使用它:

var myObjList = myQueryManager.GetEntitiesLinq<MyObject,MyObject>(x=>x.Where(myObj => array.Contains(myObj.CompareVal)));

希望这会有所帮助

【讨论】:

  • 谢谢你 - 看起来很有希望。不幸的是,FNH 在执行查询时抛出以下异常: Boolean IsNullOrEmpty(System.String)
  • @BlackSpy:CompareVal 是直接属性吗? (不涉及代码?)
  • 是的 - 根据查询,它是 Int 或 String - 访问器后面没有代码
  • 这是正确的答案——您将一个委托传递给 Query 方法,但您需要传递一个表达式树。见stackoverflow.com/a/4738396/12752
  • @BlackSpy jbl 是正确的,因为您需要传递键入为 Expression> 的表达式,而不仅仅是 Func。在后一种情况下,NHibernate 将只能看到已编译的代码,并且无法将其转换为 SQL。您说您尝试过,但它导致了异常 - 请在此异常中发布更多信息。
【解决方案3】:

您可以改用 QueryOver 和 WhereRestrictionOn 吗?

session.QueryOver<MyObject>().WhereRestrictionOn(o => o.CompareVal).IsIn(array).List();

【讨论】:

  • 这里的问题是这是一个通用存储库的一部分,当我使用 QueryOver 时不会编译,因为它似乎需要一个具体的引用类型。
  • public IList Query(Func criteria) { using (var session = SessionProvider.SessionFactory.OpenSession()) { return session.Query().Where(标准).ToList(); } }
猜你喜欢
  • 2011-10-03
  • 2011-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多