Nhibernate .Fetch调用在模拟的会话上失败 [英] Nhibernate .Fetch calls fail on a mocked session

查看:57
本文介绍了Nhibernate .Fetch调用在模拟的会话上失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我爱NHibernate(和NHibernate.Linq).我没有过早地进行优化,但是有时我会遇到一个非常讨厌的N + 1问题.对于N + 1的推荐解决方法是使用NH的Fetch扩展方法.

I love NHibernate (and NHibernate.Linq). I don't prematurely optimize, but sometimes I'll hit a really nasty N+1 issue. The recommended fix for the N+1 is to use NH's Fetch extension method.

当我创建ISession的模拟文件时,就会出现问题.我将创建一个List<User>并将我的模拟设置为每当有人调用_session.Query<User>()时返回该列表.当我在查询中添加Fetch调用时(即_session.Query<User>().Fetch(u => u.Address),我收到以下错误消息:

The problem arises when I create a Mock of the ISession. I'll create a List<User> and set my mock to return the list whenever someone calls _session.Query<User>(). When I add a Fetch call to the query (i.e. _session.Query<User>().Fetch(u => u.Address), I get the following error message:

There is no method 'Fetch' on type 'NHibernate.Linq.EagerFetchingExtensionMethods' 
that matches the specified arguments

NHibernate的获取接受普通的旧IQueryable<T>,但尝试将其强制转换为特定的NH实现,如果无法实现则失败.

NHibernate's fetch accepts a plain old IQueryable<T> but tries to cast it as specific NH implementations and fails if it can't.

我真的希望Fetch如果在非NH实现(即列表)上被调用且不会被忽略,则不会出错,因此我仍然可以在单元测试中使用它.救命!

I would really like Fetch to not error if it is called on a non-NH implementation (i.e. a list) and just be ignored so I can still use it in my unit tests. Help!

推荐答案

好吧,我尝试自己实施此方法,但谢天谢地,我发现有人已经完成了这项工作.

Well, I tried to implement this myself, but thank god I found someone who already did the legwork.

您唯一需要做的就是调用EagerlyFetch,而不只是Fetch.

The only thing you have to do is call EagerlyFetch instead of just Fetch.

我已在下面复制了相关代码,因为他的博客已经存在相当多的http 500错误和CSS问题.我认为它没有得到维护.

I've copied the relevant code below because his blog already has a fair amount of http 500 errors and css issues. I don't think it is being maintained.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using NHibernate.Linq;
using Remotion.Linq;

namespace LittleFish.Persistence.Extensions
{
    /// <summary>
    /// Provides extension method wrappers for NHibernate methods 
    /// to allow consuming source code to avoid "using" NHibernate.
    /// </summary>
    public static class NHibernateExtensions
    {
        /// <summary>
        /// Eager-loads a projection of the specified queryable, 
        /// referencing a mapped child object.
        /// </summary>
        public static IFetchRequest<T, TRel> EagerlyFetch<T, TRel>(
            this IQueryable<T> queryable,
            Expression<Func<T, TRel>> expression)
        {
            if (queryable is QueryableBase<T>)
                return FetchHelper.Create(queryable.Fetch(expression));
            else
                return FetchHelper.CreateNonNH<T, TRel>(queryable);
        } 

        /// <summary>
        /// Eager-loads a second-level projection of the specified queryable, 
        /// referencing a mapped child of the first eager-loaded child.
        /// </summary>
        public static IFetchRequest<T, TRel2> ThenEagerlyFetch<T, TRel, TRel2>(
            this IFetchRequest<T, TRel> queryable,
            Expression<Func<TRel, TRel2>> expression)
        {
            if (queryable is QueryableFetchHelper<T, TRel>)
                return FetchHelper.CreateNonNH<T, TRel2>(queryable);
            else
                return FetchHelper.Create(queryable.ThenFetch(expression));
        } 

        /// <summary>
        /// Eager-loads a projection of the specified queryable, 
        /// referencing a mapped child object.
        /// </summary>
        public static IFetchRequest<T, TRel> EagerlyFetchMany<T, TRel>(
            this IQueryable<T> queryable,
            Expression<Func<T, IEnumerable<TRel>>> expression)
        {
            if(queryable is QueryableBase<T>)
                return FetchHelper.Create(queryable.FetchMany(expression));
            else
                return FetchHelper.CreateNonNH<T, TRel>(queryable);
        } 

        /// <summary>
        /// Eager-loads a second-level projection of the specified queryable, 
        /// referencing a mapped child of the first eager-loaded child.
        /// </summary>
        public static IFetchRequest<T, TRel2> ThenEagerlyFetchMany
            <T, TRel, TRel2>(
            this IFetchRequest<T, TRel> queryable,
            Expression<Func<TRel, IEnumerable<TRel2>>> expression)
        {
            if (queryable is QueryableFetchHelper<T, TRel>)
                return FetchHelper.CreateNonNH<T, TRel2>(queryable);
            else
                return FetchHelper.Create(queryable.ThenFetchMany(expression));
        }
    } 

    /// <summary>
    /// Provides a wrapper for NHibernate's FetchRequest interface, 
    /// so libraries that run eager-loaded queries don't have to reference 
    /// NHibernate assemblies.
    /// </summary>
    public interface IFetchRequest<TQuery, TFetch> :
        INhFetchRequest<TQuery, TFetch>
    {
    } 

    internal class NhFetchHelper<TQuery, TFetch> : IFetchRequest<TQuery, TFetch>
    {
        private readonly INhFetchRequest<TQuery, TFetch> realFetchRequest;

        //this is the real deal for NHibernate queries
        internal NhFetchHelper(INhFetchRequest<TQuery, TFetch> realFetchRequest)
        {
            this.realFetchRequest = realFetchRequest;
        } 

        public IEnumerator<TQuery> GetEnumerator()
        {
            return (realFetchRequest).GetEnumerator();
        } 

        IEnumerator IEnumerable.GetEnumerator()
        {
            return (realFetchRequest).GetEnumerator();
        } 

        public Expression Expression
        {
            get { return (realFetchRequest).Expression; }
        } 

        public Type ElementType
        {
            get { return (realFetchRequest).ElementType; }
        } 

        public IQueryProvider Provider
        {
            get { return (realFetchRequest).Provider; }
        }
    } 

    internal class QueryableFetchHelper<TQuery, TFetch> :
        IFetchRequest<TQuery, TFetch>
    {
        private readonly IQueryable<TQuery> queryable;

        //for use against non-NH datastores
        internal QueryableFetchHelper(IQueryable<TQuery> queryable)
        {
            this.queryable = queryable;
        } 


        public IEnumerator<TQuery> GetEnumerator()
        {
            return (queryable).GetEnumerator();
        } 

        IEnumerator IEnumerable.GetEnumerator()
        {
            return (queryable).GetEnumerator();
        } 


        public Expression Expression
        {
            get { return (queryable).Expression; }
        } 

        public Type ElementType
        {
            get { return (queryable).ElementType; }
        } 

        public IQueryProvider Provider
        {
            get { return (queryable).Provider; }
        }
    } 

    /// <summary>
    /// The static "front door" to FetchHelper, with generic factories allowing 
    /// generic type inference.
    /// </summary>
    internal static class FetchHelper
    {
        public static NhFetchHelper<TQuery, TFetch> Create<TQuery, TFetch>(
            INhFetchRequest<TQuery, TFetch> nhFetch)
        {
            return new NhFetchHelper<TQuery, TFetch>(nhFetch);
        } 

        public static NhFetchHelper<TQuery, TFetch> Create<TQuery, TFetch>(
            IFetchRequest<TQuery, TFetch> nhFetch)
        {
            return new NhFetchHelper<TQuery, TFetch>(nhFetch);
        } 

        public static IFetchRequest<TQuery, TRel> CreateNonNH<TQuery, TRel>(
            IQueryable<TQuery> queryable)
        {
            return new QueryableFetchHelper<TQuery, TRel>(queryable);
        }
    }
}

这篇关于Nhibernate .Fetch调用在模拟的会话上失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆