C# 带有简单注入器的CQR中作为横切关注点的分页
在我的应用程序设计中,我尝试将模式作为应用于模式的一部分来实现 我也有一个观点,我认为分页不是业务逻辑的一部分(因此是一个交叉关注点)。这是已经做出的决定,不应在本主题中讨论 在我的设计中,目的是表示层可以使用具有特定封闭泛型类型的分页查询C# 带有简单注入器的CQR中作为横切关注点的分页,c#,.net,design-patterns,pagination,simple-injector,C#,.net,Design Patterns,Pagination,Simple Injector,在我的应用程序设计中,我尝试将模式作为应用于模式的一部分来实现 我也有一个观点,我认为分页不是业务逻辑的一部分(因此是一个交叉关注点)。这是已经做出的决定,不应在本主题中讨论 在我的设计中,目的是表示层可以使用具有特定封闭泛型类型的分页查询 IQueryHandler<GetAllItemsQuery, PaginatedQuery<Item>> 因此,处理程序的返回类型是IEnumerable。这样,处理程序就被强制执行。 我面临的问题可能是我使用的方式。因为我正在注
IQueryHandler<GetAllItemsQuery, PaginatedQuery<Item>>
因此,处理程序的返回类型是IEnumerable
。这样,处理程序就被强制执行。
我面临的问题可能是我使用的方式。因为我正在注册我的IQueryHandler
like
container.Register(typeof(IQueryHandler<,>), assemblies);
关于操作是什么以及为什么无效,异常并不十分清楚。也许我做错了什么
以下是拦截器的实现:
public class PaginatedQueryHandlerInterceptor<TQuery, TModel> : IQueryHandler<TQuery, PaginatedResult<TModel>>
where TQuery : PaginatedQuery<TModel>
{
private readonly IQueryHandler<TQuery, IEnumerable<TModel>> _queryHandler;
public PaginatedQueryHandlerInterceptor(IQueryHandler<TQuery, IEnumerable<TModel>> queryHandler)
{
_queryHandler = queryHandler;
}
public PaginatedResult<TModel> Handle(TQuery query)
{
return (dynamic) _queryHandler.Handle(query);
}
}
公共类分页QueryHandlerInterceptor:IQueryHandler
其中TQuery:PaginatedQuery
{
专用只读IQueryHandler\u queryHandler;
公共分页queryHandler拦截器(IQueryHandler queryHandler)
{
_queryHandler=queryHandler;
}
公共分页结果句柄(TQuery查询)
{
返回(动态)\ u查询句柄(查询);
}
}
还有装饰师:
public class PaginationQueryHandlerDecorator<TQuery, TResult> : IQueryHandler<TQuery, TResult>
where TQuery : class, IQuery<TResult>
{
private readonly IQueryHandler<TQuery, TResult> _decoratee;
public PaginationQueryHandlerDecorator(
IQueryHandler<TQuery, TResult> decoratee)
{
_decoratee = decoratee;
}
public TResult Handle(TQuery query)
{
query.ThrowIfNull(nameof(query));
var result = _decoratee.Handle(query);
if (query.IsPaginationQuery(out var paginatedQuery))
{
return Paginate(result, paginatedQuery.Pagination);
}
return result;
}
private static TResult Paginate(TResult result, Pagination pagination)
{
return Paginate(result as dynamic, pagination.Page, pagination.ItemsPerPage);
}
private static PaginatedResult<TModel> Paginate<TModel>(IEnumerable<TModel> result, int page, int itemsPerPage)
{
var items = result as TModel[] ?? result.ToArray();
var paginated = items.Skip(page * itemsPerPage).Take(itemsPerPage).ToArray();
return new PaginatedResult<TModel>
{
Items = paginated,
Count = items.Length
};
}
}
公共类分页QueryHandlerDecorator:IQueryHandler
提问地点:课堂、课堂
{
私有只读IQueryHandler\u装饰对象;
公共分页查询HandlerDecorator(
IQueryHandler(装饰者)
{
_被装饰者=被装饰者;
}
公共TResult句柄(TQuery查询)
{
query.ThrowIfNull(name of(query));
var result=_decoratee.Handle(查询);
if(query.IsPaginationQuery(out var paginatedQuery))
{
返回Paginate(结果,paginatedQuery.Pagination);
}
返回结果;
}
私有静态TResult分页(TResult result,分页分页)
{
返回分页(结果为dynamic、pagination.Page、pagination.ItemsPerPage);
}
私有静态分页结果分页(IEnumerable result,int page,int itemsPerPage)
{
var items=结果为TModel[]??结果.ToArray();
var paginated=items.Skip(page*itemsPerPage).Take(itemsPerPage.ToArray();
返回新的分页结果
{
项目=分页,
计数=项目。长度
};
}
}
这是已经做出的决定,不应在本主题中讨论
嗯。。。。如果你坚持:)
但至少要防止这些查询返回IEnumerable
,而是返回IQueryable
。使用IEnumerable
将导致从数据库返回所有数据,即使您对其进行翻页
也就是说,我不确定您的代码出了什么问题,但我想建议一种稍微不同的方法:
public class PagedQueryHandler<TQuery, TItem>
: IQueryHandler<PagedQuery<TQuery, TItem>, Paged<TItem>>
where TQuery : IQuery<IQueryable<TItem>>
{
private readonly IQueryHandler<TQuery, IQueryable<TItem>> handler;
public PagedQueryHandler(IQueryHandler<TQuery, IQueryable<TItem>> handler)
{
this.handler = handler;
}
public Paged<TItem> Handle(PagedQuery<TQuery, TItem> query)
{
var paging = query.PageInfo ?? new PageInfo();
IQueryable<TItem> items = this.handler.Handle(query.Query);
return new Paged<TItem>
{
Items = items.Skip(paging.PageIndex * paging.PageSize)
.Take(paging.PageSize).ToArray(),
Paging = paging,
};
}
}
PageInfo
和Paged
源自此Github回购协议:
PagedQueryHandler
可以按如下方式注册:
public class Paged<T>
{
public PageInfo Paging { get; set; }
public T[] Items { get; set; }
}
public class PageInfo
{
public int PageIndex { get; set; }
public int PageSize { get; set; } = 20;
}
public class PagedQuery<TQuery, TItem> : IQuery<Paged<TItem>>
where TQuery : IQuery<IQueryable<TItem>>
{
public TQuery Query { get; set; }
public PageInfo PageInfo { get; set; }
}
container.Register(typeof(IQueryHandler<,>), typeof(PagedQueryHandler<,>));
container.Register(typeof(IQueryHandler),typeof(PagedQueryHandler));
通过此类及其注册,您只需将可分页查询处理程序注入使用者,例如:
public class ItemsController
{
IQueryHandler<PagedQuery<GetAllItemsQuery, Item>, Paged<Item>> handler;
public ItemsController(
IQueryHandler<PagedQuery<GetAllItemsQuery, Item>, Paged<Item>> handler)
{
this.handler = handler;
}
public ActionResult Index(PagedQuery<GetAllItemsQuery, Item> query)
{
return View(this.handler.Handle(query));
}
}
公共类ItemsController
{
IQueryHandler;
公共项目控制器(
IQueryHandler(处理程序)
{
this.handler=handler;
}
公共操作结果索引(PagedQuery查询)
{
返回视图(this.handler.Handle(查询));
}
}
实际问题确实是我做错了,但与简单的喷油器无关
当使用注册表条件时,在谓词中调用此扩展方法:
bool PaginationInterceptorPredicate(PredicateContext predicateContext) =>
predicateContext.ServiceType.GetGenericArguments()[0].IsPaginatedQuery();
IsPaginatedQuery
的实现错误,导致异常:
public static bool IsPaginatedQuery(this Type queryType) =>
queryType.GetInterfaces().Any(i => i.GetGenericTypeDefinition() == typeof(PaginatedQuery<>));
publicstaticboolispaginatedquery(此类型queryType)=>
queryType.GetInterfaces().Any(i=>i.GetGenericTypeDefinition()==typeof(PaginatedQuery));
由于查询还实现了非泛型接口
IPagination
,因此GetGenericTypeDefinition()
方法导致了异常。您的解决方案存在一个小问题。也就是说,当PagedQueryHandler
调用.ToArray()
时,我的DbContext
被释放。因为我正在用LifetimeScopeQueryHandlerProxy
装饰我的查询处理程序。这意味着您不应该装饰返回IQueryable
实例的查询处理程序。或者确保LifetimeScopeQueryHandlerProxy检测它是否嵌套,并防止它启动和处理DbContext。但这并不是我的设计的真正问题,因为你的原始帖子也包含同样的问题。我已经将我的初始设计重构为你的略有不同的方法,现在效果非常好。我在原始设计中使用IEnumerable
的原因是,我的查询将继承自PaginatedQuery
,强制处理程序返回IEnumerable
,但让业务部门决定它是否是隐藏在引擎盖下的IQueryable
(可能底层数据模型的性能非常出色)。在您的设计中,查询仍然是自然的,并且泛型类型约束强制处理程序返回IQueryable
。缺点是处理程序只能返回查询。TBH我个人坚持让查询处理程序直接返回页面结果,而不使用应用分页的中间映射。如你可以看到,它增加的噪音是最小的,在我看来是合理的。
container.Register(typeof(IQueryHandler<,>), typeof(PagedQueryHandler<,>));
public class ItemsController
{
IQueryHandler<PagedQuery<GetAllItemsQuery, Item>, Paged<Item>> handler;
public ItemsController(
IQueryHandler<PagedQuery<GetAllItemsQuery, Item>, Paged<Item>> handler)
{
this.handler = handler;
}
public ActionResult Index(PagedQuery<GetAllItemsQuery, Item> query)
{
return View(this.handler.Handle(query));
}
}
bool PaginationInterceptorPredicate(PredicateContext predicateContext) =>
predicateContext.ServiceType.GetGenericArguments()[0].IsPaginatedQuery();
public static bool IsPaginatedQuery(this Type queryType) =>
queryType.GetInterfaces().Any(i => i.GetGenericTypeDefinition() == typeof(PaginatedQuery<>));