使用 Linq、Criteria API 和 Query Over 扩展 NHibernate 的 ArdalisSpecification

使用 linq、criteria api 和 query over 扩展 nhibernate 的 ardalisspecification

ardalis.specification 是一个功能强大的库,支持查询数据库的规范模式,主要是为 entity framework core 设计的,但在这里我将演示如何扩展 ardalis.specification 以将 nhibernate 用作 orm。

这篇博文假设您对 ardalis.specification 有一定的经验,并且希望在使用 nhibernate 的项目中使用它。如果您还不熟悉 ardalis.specification,请参阅文档以了解更多信息。

首先,nhibernate 中有三种不同的内置方法来执行查询

linq 查询(使用 iqueryable)标准 api查询结束

我将介绍如何扩展 ardalis.specification 来处理所有 3 种方式,但由于 linq to query 也可以像 entity framework core 一样与 iqueryable 配合使用,因此我将首先介绍该选项。

linq 查询

在创建连接关系时,entity framework core 和 nhibernate 之间存在细微差别。在 entity framework core 中,我们在 iqueryable 上有扩展方法: include 和 theninclude (这些也是 ardalis.specification 中使用的方法名称)。

fetch、fetchmany、thenfetch 和 thenfetchmany 是 iqueryable 上进行连接的 nhibernate 特定方法。 ievaluator 为我们提供了使用 nhibernate 时调用正确扩展方法所需的可扩展性。

添加 ievaluator 的实现,如下所示:

public class fetchevaluator : ievaluator{   private static readonly methodinfo fetchmethodinfo = typeof(eagerfetchingextensionmethods)        .gettypeinfo().getdeclaredmethods(nameof(eagerfetchingextensionmethods.fetch))        .single();   private static readonly methodinfo fetchmanymethodinfo = typeof(eagerfetchingextensionmethods)       .gettypeinfo().getdeclaredmethods(nameof(eagerfetchingextensionmethods.fetchmany))       .single();   private static readonly methodinfo thenfetchmethodinfo       = typeof(eagerfetchingextensionmethods)           .gettypeinfo().getdeclaredmethods(nameof(eagerfetchingextensionmethods.thenfetch))           .single();   private static readonly methodinfo thenfetchmanymethodinfo       = typeof(eagerfetchingextensionmethods)           .gettypeinfo().getdeclaredmethods(nameof(eagerfetchingextensionmethods.thenfetchmany))           .single();    public static fetchevaluator instance { get; } = new fetchevaluator();    public iqueryable getquery(iqueryable query, ispecification specification) where t : class    {        foreach (var includeinfo in specification.includeexpressions)        {            query = includeinfo.type switch            {                includetypeenum.include => buildinclude(query, includeinfo),                includetypeenum.theninclude => buildtheninclude(query, includeinfo),                _ => query            };        }        return query;    }    public bool iscriteriaevaluator { get; } = false;    private iqueryable buildinclude(iqueryable query, includeexpressioninfo includeinfo)    {        _ = includeinfo ?? throw new argumentnullexception(nameof(includeinfo));        var methodinfo = (isgenericenumerable(includeinfo.propertytype, out var propertytype)            ? fetchmanymethodinfo             : fetchmethodinfo);       var method = methodinfo.makegenericmethod(includeinfo.entitytype, propertytype);       var result = method.invoke(null, new object[] { query, includeinfo.lambdaexpression });        _ = result ?? throw new targetexception();        return (iqueryable)result;    }    private iqueryable buildtheninclude(iqueryable query, includeexpressioninfo includeinfo)    {        _ = includeinfo ?? throw new argumentnullexception(nameof(includeinfo));        _ = includeinfo.previouspropertytype ?? throw new argumentnullexception(nameof(includeinfo.previouspropertytype));        var method = (isgenericenumerable(includeinfo.previouspropertytype, out var previouspropertytype)            ? thenfetchmanymethodinfo            : thenfetchmethodinfo);        isgenericenumerable(includeinfo.propertytype, out var propertytype);        var result = method.makegenericmethod(includeinfo.entitytype, previouspropertytype, propertytype)            .invoke(null, new object[] { query, includeinfo.lambdaexpression });        _ = result ?? throw new targetexception();        return (iqueryable)result;    }    private static bool isgenericenumerable(type type, out type propertytype)    {        if (type.isgenerictype && (type.getgenerictypedefinition() == typeof(ienumerable)))        {            propertytype = type.generictypearguments[0];            return true;        }        propertytype = type;        return false;    }}

接下来我们需要配置 ispecificationevaluator 以使用我们的 fetchevaluator(和其他评估器)。我们添加一个实现 ispecificationevaluator ,如下所示,并在构造函数中配置评估器。 whereevaluator、orderevaluator 和 paginationevaluator 都在 ardalis.specification 中,并且在 nhibernate 中也能很好地工作。

public class linqtoqueryspecificationevaluator : ispecificationevaluator{    private list evaluators { get; } = new list();    public linqtoqueryspecificationevaluator()    {        evaluators.addrange(new ievaluator[]        {            whereevaluator.instance,            orderevaluator.instance,            paginationevaluator.instance,            fetchevaluator.instance        });    }    public iqueryable getquery(iqueryable query, ispecification specification) where t : class    {        if (specification is null) throw new argumentnullexception(nameof(specification));        if (specification.selector is null && specification.selectormany is null) throw new selectornotfoundexception();        if (specification.selector is not null && specification.selectormany is not null) throw new concurrentselectorsexception();        query = getquery(query, (ispecification)specification);        return specification.selector is not null            ? query.select(specification.selector)            : query.selectmany(specification.selectormany!);    }    public iqueryable getquery(iqueryable query, ispecification specification, bool evaluatecriteriaonly = false) where t : class    {        if (specification is null) throw new argumentnullexception(nameof(specification));        var evaluators = evaluatecriteriaonly ? evaluators.where(x => x.iscriteriaevaluator) : evaluators;        foreach (var evaluator in evaluators)           query = evaluator.getquery(query, specification);        return query;    }}

现在我们可以在我们的存储库中创建对 linqtoqueryspecificationevaluator 的引用,可能如下所示:

public class repository : irepository{    private readonly isession _session;    private readonly ispecificationevaluator _specificationevaluator;    public repository(isession session)    {        _session = session;        _specificationevaluator = new linqtoqueryspecificationevaluator();    }     ... other repository methods    public ienumerable list(ispecification specification) where t : class    {        return _specificationevaluator.getquery(_session.query().asqueryable(), specification).tolist();    }    public ienumerable list(ispecification specification) where t : class    {            return _specificationevaluator.getquery(_session.query().asqueryable(), specification).tolist();    }    public void dispose()    {        _session.dispose();    }}

就是这样。现在,我们可以在规范中使用 linq to query,就像我们通常使用 ardalis 一样。规范:

public class trackbyname : specification{    public trackbyname(string trackname)    {        query.where(x => x.name == trackname);    }}

现在我们已经介绍了基于 linq 的查询,让我们继续处理 criteria api 和 query over,这需要不同的方法。

在 nhibernate 中混合 linq、criteria 和 query over

由于 criteria api 和 query over 有自己的实现来生成 sql,并且不使用 iqueryable,因此它们与 ievaluator 接口不兼容。我的解决方案是在这种情况下避免对这些方法使用 ievaluator 接口,而是关注规范模式的好处。但我也希望能够混搭
我的解决方案中包含 linq to query、criteria 和 query over(如果您只需要其中一种实现,您可以根据您的最佳需求进行挑选)。

为了能够做到这一点,我添加了四个继承specification或specification的新类

注意: 定义这些类的程序集需要对 nhibernate 的引用,因为我们为 criteria 和 queryover 定义操作,这可以在 nhibernate
中找到

public class criteriaspecification : specification{    private action? _action;    public action getcriteria() => _action ?? throw new notsupportedexception("the criteria has not been specified. please use usecriteria() to define the criteria.");    protected void usecriteria(action action) => _action = action;}public class criteriaspecification : specification{    private action? _action;    public action getcriteria() => _action ?? throw new notsupportedexception("the criteria has not been specified. please use usecriteria() to define the criteria.");    protected void usecriteria(action action) => _action = action;}public class queryoverspecification : specification{    private action<iqueryover>? _action;    public action<iqueryover> getqueryover() => _action ?? throw new notsupportedexception("the query over has not been specified. please use the usequeryover() to define the query over.");    protected void usequeryover(action<iqueryover> action) => _action = action;}public class queryoverspecification : specification{    private func<iqueryover, iqueryover>? _action;    public func<iqueryover, iqueryover> getqueryover() => _action ??  throw new notsupportedexception("the query over has not been specified. please use the usequeryover() to define the query over.");    protected void usequeryover(func<iqueryover, iqueryover> action) => _action = action;}

然后我们可以在存储库中使用模式匹配来更改使用 nhibernate 进行查询的方式

public ienumerable list(ispecification specification) where t : class{    return specification switch    {        criteriaspecification criteriaspecification =>             _session.createcriteria()                .apply(query => criteriaspecification.getcriteria().invoke(query))                .list(),        queryoverspecification queryoverspecification =>             _session.queryover()                .apply(queryover => queryoverspecification.getqueryover().invoke(queryover))                .list(),        _ => _specificationevaluator.getquery(_session.query().asqueryable(), specification).tolist()    };}public ienumerable list(ispecification specification) where t : class{    return specification switch    {        criteriaspecification criteriaspecification =>             _session.createcriteria()                .apply(query => criteriaspecification.getcriteria().invoke(query))                .list(),        queryoverspecification queryoverspecification =>            _session.queryover()                .apply(queryover => queryoverspecification.getqueryover().invoke(queryover))                .list(),        _ => _specificationevaluator.getquery(_session.query().asqueryable(), specification).tolist()    };}

上面的apply()方法是一个扩展方法,它将查询简化为一行:

public static class queryextensions{    public static t apply(this t obj, action action)    {        action(obj);        return obj;    }    public static tresult apply(this t obj, func func)    {        return func(obj);    }}

标准规范示例

注意: 定义这些类的程序集需要对 nhibernate 的引用,因为我们定义 criteria 的操作,可以在 nhibernate
中找到

public class trackbynamecriteria : criteriaspecification{    public trackbynamecriteria(string trackname)    {        this.usecriteria(criteria => criteria.add(restrictions.eq(nameof(track.name), trackname)));    }}

查询超规格示例

注意: 定义这些类的程序集需要对 nhibernate 的引用,因为我们定义 queryover 的操作,可以在 nhibernate
中找到

public class TrackByNameQueryOver : QueryOverSpecification{    public TrackByNameQueryOver(string trackName)    {        this.UseQueryOver(queryOver => queryOver.Where(x => x.Name == trackName));    }}

通过扩展 nhibernate 的 ardalis.specification,我们解锁了在单个存储库模式中使用 linq to query、criteria api 和 query over 的能力。这种方法为 nhibernate 用户提供了高度适应性和强大的解决方案

以上就是使用 Linq、Criteria API 和 Query Over 扩展 NHibernate 的 ArdalisSpecification的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1459374.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月18日 11:26:20
下一篇 2025年12月14日 19:49:36

相关推荐

  • C++ 函数指针:扩展类库和创建自定义数据结构

    c++++ 函数指针是一种指向函数的变量,它允许开发人员创建可扩展类库和灵活数据结构。定义函数指针:使用 typedef 创建指向函数的函数指针,指定函数类型和返回类型。使用函数指针:创建一个指向函数的函数指针,并通过调用运算符调用函数。扩展类库:使用函数指针向现有类库添加自定义功能,例如通过自定义…

    好文分享 2025年12月18日
    000
  • C++ 函数指针:函数调用的动态化与灵活性

    C++ 函数指针:函数调用的动态化与灵活性 函数指针是一种强大的 C++ 特性,它允许将函数地址存储在变量中,从而实现函数调用的动态化和灵活性。 语法 函数指针的类型是一个指向函数的指针,语法如下: returntype (*function_name)(argument_type1, argume…

    2025年12月18日
    000
  • C++ 函数指针:面向协程编程的非阻塞解决方案

    函数指针在 c++++ 中提供了一种非阻塞的协程编程解决方案,具有以下优点:高效的协程创建和执行自定义回调逻辑的灵活性轻松添加或删除协程的可扩展性 C++ 函数指针:面向协程编程的非阻塞解决方案 简介 协程是一种协作式的多任务编程模型,可让多个任务在同一线程上并行执行。在 C++ 中,函数指针提供了…

    2025年12月18日
    000
  • C++ 函数指针:探索游戏开发中的强大特性

    在游戏开发中,函数指针是一种有用的工具,它允许将函数作为参数传递给其他函数。它提供了灵活性,可创建可重用的代码:函数指针存储指向函数的内存地址,可像其他数据类型一样使用。使用取地址运算符 (&) 获取函数地址并存储在函数指针中。回调函数是函数指针的应用,在满足条件时调用传递的函数。函数指针的…

    2025年12月18日
    000
  • 探索 C++ 函数在高性能计算中的潜力

    c++++ 函数在高性能计算中发挥着关键作用,提供以下特性:函数指针:允许动态调用函数。lambda 表达式:提供匿名函数,简化函数定义。内联函数:消除调用开销,提升效率。实战案例:在并行编程中,函数可轻松包装代码块,进行并行化。 探索 C++ 函数在高性能计算中的潜力 简介 C++ 因其强大的性能…

    2025年12月18日
    000
  • C++ 函数指针:理解 Lambda 表达式和闭包的底层实现

    C++ 函数指针:理解 Lambda 表达式和闭包的底层实现 简介 函数指针是 C++ 中强大的工具,它允许我们将函数作为参数传递给其他函数或对象。通过结合函数指针,我们可以创建高度可定制和可重用的代码。Lambda 表达式和闭包进一步扩展了函数指针的概念,提供了新的方式来创建和使用内联函数。 函数…

    2025年12月18日
    000
  • C++ 函数指针的进阶用法探讨:揭秘指针的奥秘

    函数指针是一种指向函数的指针,用于动态调用函数,常见于回调函数、事件处理和多态。本篇文章演示了如何使用函数指针对数组进行排序,通过比较函数指针的不同实现,可以实现升序或降序排序。 C++ 函数指针的进阶用法探讨:揭秘指针的奥秘 函数指针简介 函数指针是一种指向函数的指针。它存储函数在内存中的地址,允…

    2025年12月18日
    000
  • C++ 函数指针的深入浅出解析:掌握函数操控的大杀器

    c++++ 函数指针是一种操作函数作为数据的工具,用于回调、多态和动态内存分配。其语法为 returntype (*functionptrname)(parameterlist),使用方式包括定义回调函数类型、创建指向函数的函数指针,以及调用函数指针。需要注意函数指针与函数类型一致,且不可指向不存在…

    2025年12月18日
    000
  • 函数指针与 lambda:C++ 函数式编程的权衡

    函数指针与 lambda 在 c++++ 函数式编程中各有优劣:简洁性:lambda 表达式语法更简洁,尤其是在参数较多或函数体较短的情况下。类型安全:函数指针需要显式类型转换,这可能会导致类型错误。lambda 则提供了类型推断,提高了代码安全性。效率:函数指针可以实现比 lambda 更直接的函…

    2025年12月18日
    000
  • 如何优化 C++ 函数调用以提升程序效率?

    如何优化 C++ 函数调用提升程序效率 在 C++ 中,函数调用会带来一定的开销,包括参数传递、函数体执行和返回值传递。优化函数调用可以有效提升程序效率。本文将介绍常见的优化技巧,并通过实战案例说明其应用。 内联函数 将函数声明为内联函数可以消除函数调用开销,因为编译器会在调用处直接插入函数体代码。…

    2025年12月18日
    000
  • C++ 中的函数指针如何受到函数调用约定影响?

    函数指针在 c++++ 中受函数调用约定影响,包括:存在 cdecl 和 stdcall 两种调用约定。cdecl 由编译器压栈参数,而 stdcall 由调用者压栈参数并提供 this 指针。函数指针类型必须与函数调用约定匹配,否则会导致未定义行为。 C++ 中函数指针受到函数调用约定影响 函数指…

    2025年12月18日
    000
  • C++ lambda 表达式与闭包在机器学习中的应用

    在机器学习中,lambda 表达式和闭包用于数据预处理、特征工程、模型构建和闭包。具体应用包括:数据规范化等数据预处理操作。创建新特征或转换现有特征。向模型添加自定义的损失函数、激活函数等组件。利用闭包访问外部变量,用于计算特定特征的平均值等目的。 C++ Lambda 表达式与闭包在机器学习中的应…

    2025年12月18日
    000
  • C++ 自身函数详解及应用:设计模式与软件设计

    c++++ 自身函数在设计模式和软件设计中发挥重要作用,包括容器类函数(容器操作)和算法类函数(元素操作)。实战案例展示了如何使用这些函数实现单例模式、工厂模式和迭代器模式。c++ 自身函数的灵活性和功能性,使开发人员能够高效并可靠地编写高质量代码。 C++ 自身函数详解及应用:设计模式与软件设计 …

    2025年12月18日
    000
  • C++ lambda 表达式与函数指针的对比

    lambda 表达式和函数指针都是 c++++ 中用于定义函数的方式。lambda 表达式更简洁,可以捕获局部变量,但可读性稍差。函数指针更有效,指向函数指针的变量易于传递和存储,但语法繁琐。对于需要简单、匿名函数且要捕获局部变量的情况,建议使用 lambda 表达式;对于需要效率优化或要将函数指针…

    2025年12月18日
    000
  • C++ 函数的优化与调试技巧:函数指针与函数模板的优化之道

    函数指针和函数模板优化之道:函数指针优化:使用函数指针进行动态调用和回调,以实现灵活的函数调用。函数模板优化:利用函数模板自动生成函数,实现代码重用和优化,通过编译器内联提升性能。实战案例:在排序算法中使用函数指针和函数模板,根据不同类型自定义排序函数,提升排序效率。 C++ 函数的优化与调试技巧:…

    2025年12月18日
    000
  • c语言高精度除法运算源代码

    在 C 语言中,高精度除法运算可以通过使用额外的辅助函数和数据结构来实现。首先,需要定义一个高精度整数结构,其中包含数字数组和长度信息。随后,可以使用数组来存储数字,并通过初始化、创建和除法运算函数进行处理。除法运算需要归一化、更新商和减去乘积。最后,去除余数中的前导零并释放内存,即可得到商。 C …

    2025年12月18日
    000
  • c语言如何对除法保留高精度小数

    在 C 语言中保留除法的精度:使用浮点数:可以使用浮点数类型保留小数精度,但当精度要求较高时存在局限性。使用 long double:long double 类型提供了比浮点数更高的精度,适用于需要保留更多小数位的情况。使用自定义函数:对于需要更高级精度控制的情况,可以编写自定义除法函数,根据需要保…

    2025年12月18日
    000
  • C++ 函数的跨平台特性和兼容性探究

    跨平台 c++++ 函数具有跨操作系统和硬件架构运行的能力,其兼容性需考虑:数据类型:使用 typedef 或 using 保持不同平台上数据类型的一致性。库函数:采用平台无关库或条件编译以确保兼容性。操作系统 api:使用平台抽象层或条件编译封装平台特定 api。 C++ 函数的跨平台特性和兼容性…

    2025年12月18日
    000
  • C 代码片段:)

    数据类型 #include // struct datatypestruct person { char name[50]; int age; float salary;};// enum datatypeenum color {red, green, blue};int main() { // b…

    2025年12月18日
    000
  • C语言实现浮点除法(高精度)

    C语言中实现浮点除法高精度的步骤如下:准备工作:定义必要的宏和类型,以及浮点数操作函数。除法算法:使用长除法算法将尾数部分逐位相除,并调整余数和尾数。指数调整:除数的指数减去被除数的指数,得到结果的指数,并调整尾数的小数点位置。符号处理:如果除数和被除数符号相同,则结果为正;否则为负。归一化:将尾数…

    2025年12月18日
    000

发表回复

登录后才能评论
关注微信