
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
查询超规格示例
注意: 定义这些类的程序集需要对 nhibernate 的引用,因为我们定义 queryover 的操作,可以在 nhibernate
中找到
public class TrackByNameQueryOver : QueryOverSpecification
通过扩展 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
微信扫一扫
支付宝扫一扫