Nhibernate .Fetch调用在模拟的会话上失败 [英] Nhibernate .Fetch calls fail on a mocked session
问题描述
我爱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屋!