【问题标题】:NHibernate IQueryable propertiesNHibernate IQueryable 属性
【发布时间】:2015-03-21 03:50:42
【问题描述】:

我有一些代码:

public class Hippo
{
    public virtual int Id { get; set; }
    public virtual string Name {get;set;}
}
public class Zoo
{
    public virtual int Id { get; set; }
    public virtual IQueryable<Hippo> Hippos { get; set; } 
}

我可以在 NHibernate(或流利的 nhibernate)中创建这样的 Hippos 吗? 或者我可以将 Session 传递给这个对象吗?

我该怎么做? (在 NHibernate.Linq 或 NHibernate.CollectionQuery 的帮助下?)

我的目标是这样的:

_session.Query<Zoo>().Where(e => e.Hippos.Where(h=>h.Name=="Bobby").Any())

【问题讨论】:

  • 对于发布的查询没有什么特别需要的,只需IList&lt;Hippo&gt; Hippos { get; protected set; }
  • 我知道 IList,但默认情况下,当我调用此属性时,它会从数据库返回所有 Hippo,但我需要 QUERY。我的动物园有 100500 只河马,我想在 ZooForm 中使用页面
  • 那么每个页面都有_session.Query&lt;Zoo&gt;().SelectMany(e =&gt; e.Hippos).Where(h =&gt; h.Name=="Bobby").Skip(pageindex * pagesize).Take(pagesize).OrderBy(h =&gt; h.Id).ToList&lt;Hippo&gt;(),因此不需要IQueryable
  • 是的,但是对于每个小页面,即使我需要一页,SQL 也会返回所有记录。在来自用户的每个 http 请求中,一页 (50) 的所有记录 (100500)...

标签: nhibernate iqueryable


【解决方案1】:

当您通过session.Query&lt;&gt; 定义查询时,看起来集合已被访问,但实际上并非如此。它只是在转换为 sql 的结果表达式中引用。因此查询页面会将限制转发到数据库,因此只会获取几条记录。例如下面的查询只会返回 50 只河马:

const int pagesize = 50;
_session.Query<Zoo>()
    .SelectMany(e => e.Hippos)
        .Where(h => h.Name=="Bobby")
    .Skip(pageindex * pagesize).Take(pagesize)
    .OrderBy(h => h.Id)
    .ToList<Hippo>();

当你像someZoo.Hippos.Where(h=&gt;h.Name=="Bobby").ToList() 这样查询集合时不要混淆。这将触发延迟加载并为动物园获取所有河马。因此,DomainModel 上可能包含大量项目的集合是一个坏主意。有人会在代码中使用它,并且在使用小数据集进行开发时不会崩溃。如果可能,最好只使用反向引用somehippo.ContainingZoo 并查询

const int pagesize = 50;
_session.Query<Hippo>()
    .Where(h => h.Name=="Bobby")
    .Skip(pageindex * pagesize).Take(pagesize)
    .OrderBy(h => h.Id)
    .ToList<Hippo>();

更新:

可查询属性的实验代码。不漂亮但好用

class ZooMap : ClassMap<Zoo>
{
    public ZooMap()
    {
        ...

        Map(x => x.Hippos)
            .CustomType<QueryableHipposUserType>()
            .Formula("1")
            .ReadOnly();
    }
}

class QueryableHipposUserType : ICompositeUserType
{
    public object Assemble(object cached, ISessionImplementor session, object owner)
    {
        return cached;
    }

    public object DeepCopy(object value)
    {
        return value;
    }

    public object Disassemble(object value, ISessionImplementor session)
    {
        return value;
    }

    public bool Equals(object x, object y)
    {
        return true;
    }

    public int GetHashCode(object x)
    {
        return 0;
    }

    public object GetPropertyValue(object component, int property)
    {
        return null;
    }

    public bool IsMutable
    {
        get { return false; ; }
    }

    public object NullSafeGet(IDataReader dr, string[] names, ISessionImplementor session, object owner)
    {
        var zooId = ((Zoo)owner).Id;
        return ((NHibernate.ISession)session).Query<Hippo>().Where(hippo => hippo.ContainingZoo.Id == zooId);
    }

    public void NullSafeSet(IDbCommand cmd, object value, int index, bool[] settable, ISessionImplementor session)
    {
    }

    public string[] PropertyNames
    {
        get { return new string[0]; }
    }

    public IType[] PropertyTypes
    {
        get { return new IType[]{ NHibernateUtil.Int32 }; }
    }

    public object Replace(object original, object target, ISessionImplementor session, object owner)
    {
        return original;
    }

    public Type ReturnedClass
    {
        get { return typeof(IQueryable<Hippo>); }
    }

    public void SetPropertyValue(object component, int property, object value)
    {
    }
}

var someZoo = session.Get<Zoo>(zooId);
var theHappyHippos = someZoo.Hippos.Where(h => h.Name=="Bobby").ToList();

【讨论】:

  • 我了解身份证。我知道 _session.Query() 是如何工作的。在我的问题中,我问,我如何才能使这个“t 看起来像访问了集合,但实际上不是。它只是在结果表达式中被引用,它被转换为 sql。”具有导航属性。我知道,默认情况下 NHibernate 从 DB 获取所有记录(进入导航属性)
  • 对不起我的英语,也许你不明白我的问题?
  • 会话管理使用简单的属性会很尴尬,所以我能想到的最好的方法是:public IQueryable&lt;Hippo&gt; Hippos(ISession session) { return session.Query&lt;Hippo&gt;().Where(h =&gt; h.ContainingZoo.Id == this.Id); }。请注意,会话持有连接、事务和缓存,因此使用另一个会话可能会搞砸。
  • 在 MVC 模式中(在我的应用程序中)模型现在不关于会话(或来自实体框架的上下文),只关于 IQueryable 属性:(因此我不能从客户端(模型)传递会话,只有使用会话中的 IQueryable 集合...
  • 我强烈反对它,但您可以实现 ICompositeUserType 以在对象脱水时设置 IQueryable。当会话关闭时,它会导致过时的 IQueryables。如果需要,我可以发布一些代码
猜你喜欢
  • 2011-02-15
  • 2012-08-05
  • 1970-01-01
  • 2020-09-17
  • 2011-11-14
  • 2010-11-14
  • 1970-01-01
  • 1970-01-01
  • 2011-06-04
相关资源
最近更新 更多