- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
在我的应用程序设计中,我试图实现 Pagination作为Cross-Cutting Concern与 Decorator应用于 an implementation 的图案的 CQRS图案。
我还有一个 multi-layered architecture我认为分页不是业务逻辑的一部分(因此是一个横切关注点)。这是已经做出的决定,不应在本主题中讨论。
在我的设计中,意图是表示层可以使用具有特定封闭通用类型的分页查询
IQueryHandler<GetAllItemsQuery, PaginatedQuery<Item>>
具有以下签名:
public class GetAllItemsQuery : PaginatedQuery<Item>
public class PaginatedQuery<TModel> :
IQuery<PaginatedResult<TModel>>, IQuery<IEnumerable<TModel>>
public class PaginatedResult<TModel>
这个想法是消费者应该收到一个 PaginatedResult
对于特定模型,它包含分页项和一些元数据(例如,在未应用分页的情况下执行的查询的项目总数),以便 UI 可以呈现它的分页。
我设计的主要理念是查询处理程序应该只应用它的业务逻辑(例如获取所有项目)。它只描述了它将如何执行此操作,不一定非要执行查询。
在我的例子中,查询处理程序上的装饰器实际上对查询应用分页并执行它(例如,通过在 .ToArray()
查询上调用 Linq to Entities)。
我想要的是我的查询处理程序应该像这样实现:
public class GetAllItemsQueryHandler : IQueryHandler<GetAllItemsQuery, IEnumerable<Item>>
所以处理程序的返回类型是IEnumerable<Item>
.这样处理程序就被强制为 Single Responsible .我面临的问题可能是我使用 Simple Injector 的方式.因为我正在注册我的 IQueryHandler<,>
喜欢
container.Register(typeof(IQueryHandler<,>), assemblies);
这不会验证我的设计,因为明显的无效配置:我正在注入(inject) IQueryHandler<GetAllItemsQuery, PaginatedResult<Item>>
进入我的消费者,但实际上并没有实现它。相反,处理程序实现了 IQueryHandler<GetAllItemsQuery, IEnumerable<Item>>
.
因此,作为一种解决方案,我尝试实现一个 Interceptor并有条件地注册(注意 C# 7.0 local functions 的用法):
Type PaginationInterceptorFactory(TypeFactoryContext typeContext)
{
// IQueryHandler<TQuery, TResult> where TResult is PaginatedResult<TModel>
var queryType = typeContext.ServiceType.GetGenericArguments()[0]; // TQuery
var modelType = typeContext.ServiceType.GetGenericArguments()[1].GetGenericArguments()[0]; // TModel in PaginatedResult<TModel> as TResult
return typeof(PaginatedQueryHandlerInterceptor<,>).MakeGenericType(queryType, modelType);
}
bool PaginationInterceptorPredicate(PredicateContext predicateContext) =>
predicateContext.ServiceType.GetGenericArguments()[0].IsPaginatedQuery(); // if TQuery is of type PaginatedQuery<>
container.RegisterConditional(typeof(IQueryHandler<,>), PaginationInterceptorFactory, Lifestyle.Singleton, PaginationInterceptorPredicate);
但这给了我一个验证异常:
System.InvalidOperationException occurred
Message=The configuration is invalid. Creating the instance for type [TYPE] failed. This operation is only valid on generic types.
Source=SimpleInjector
StackTrace:
at SimpleInjector.InstanceProducer.VerifyExpressionBuilding()
at SimpleInjector.Container.VerifyThatAllExpressionsCanBeBuilt(InstanceProducer[] producersToVerify)
at SimpleInjector.Container.VerifyThatAllExpressionsCanBeBuilt()
at SimpleInjector.Container.VerifyInternal(Boolean suppressLifestyleMismatchVerification)
at SimpleInjector.Container.Verify()
Inner Exception 1:
ActivationException: This operation is only valid on generic types.
Inner Exception 2:
InvalidOperationException: This operation is only valid on generic types.
关于操作 是什么以及为什么它无效的异常并不是很清楚。也许我做错了什么?
拦截器的实现如下:
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);
}
}
和装饰器:
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
};
}
}
最佳答案
This is a decision already made and should not be discussed in this topic.
好吧....如果你坚持的话:)
但至少要防止这些查询返回 IEnumerable<T>
, 但返回 IQueryable<T>
反而。 IEnumerable<T>
的使用将导致从数据库返回所有数据,即使您翻页也是如此。
也就是说,我不确定您的代码有什么问题,但我想建议一种稍微不同的方法:
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,
};
}
}
这个通用 IQueryHandler
实现可以将分页查询映射到非分页查询。这里Paged<T>
和 PageInfo
和 PagedQuery<TQuery, TItem>
定义如下:
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; }
}
PageInfo
和 Paged<T>
源自此 Github 存储库:https://github.com/dotnetjunkie/solidservices/tree/master/src/Contract/
PagedQueryHandler<TQuery, TItem>
可以注册如下:
container.Register(typeof(IQueryHandler<,>), typeof(PagedQueryHandler<,>));
通过此类及其注册,您可以简单地将可分页查询处理程序注入(inject)消费者,例如:
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));
}
}
关于c# - 分页作为带有简单注入(inject)器的 cqrs 中的横切关注点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46777396/
我了解了 CQRS 的一般概念,但是当涉及到超越示例代码和幻灯片以处理现实世界问题时,我有一些问题。 验证 当您需要对涉及检查数据库值的命令进行验证时,您会怎么做? 注册一项服务,我必须输入一个唯一的
我目前正在尝试了解如何构建事件存储的内部结构。到目前为止我得到了什么: 一个事件存储有两个表(集合,...),一个用于聚合,一个用于事件。 聚合表包含以下数据:aggregateId (可能是一个 G
我最近阅读了很多关于 CQRS 的文章,对我来说,它似乎与 Event Sourcing 密切相关。 但就像这个答案说的https://stackoverflow.com/a/9217461/2770
我有 CQRS+ES 设计的应用程序。这是我在过去一年里一直在 CQRS+ES 世界中阅读的新内容,它非常有意义,但实现完美的意义并不总是那么容易。 无论如何,我的一个或多个问题是: 包含多个命令(步
我想知道如何在 CQRS 中创建多个聚合根。 示例:我有一个手机聚合根和 Simcard 聚合根。身份证 这些聚合应该是订阅聚合根的一部分。 我需要创建一个基于 SimCard 的订阅聚合,并且系统中
用户在我们的站点上注册并登录。RegisterUserCommand 被发送到异步命令处理程序。 用户想要更改他们的地址,但 RegisterUserCommand 尚未处理。系统中没有用户记录。 这
事件溯源和 CQRS 很棒,因为它使开发人员摆脱了一个预先建模的数据库的困扰,除非有大数据迁移项目,否则开发人员必须在应用程序的整个生命周期中使用该数据库。 CQRS 和 ES 还具有其他好处,例如扩
我想我理解了 ES + CQRS 上下文中读取模型的思想(如果不正确请指正)。然而,我对在“严肃”报道的背景下使用它仍有一些疑问。假设我使用关系数据库和一些 ORM 来处理我的读取模型。一个基本的“摘
我正在使用 Akka 持久性实现 CQRS 系统,并且我正在尝试了解 CQRS 的请求响应位。 关于如何将响应发送回客户端和 this article 的答案很少。还提到了一些不错的模式。但是,不是使
在基于 CQRS/ES 的系统中,您将事件存储在事件存储中。这些事件涉及一个聚合,并且它们相对于它们所属的聚合有一个顺序。此外,聚合是一致性/事务边界,这意味着任何事务保证仅在每个聚合级别上给出。 现
根据CQRS à la Greg Young ,事件处理程序(和下游事件反规范化器)对事件发布者之前发布的传入事件使用react。 现在让我们假设在运行时我们想要添加一个新的事件反规范化器:基本上,这
是否存在单个命令导致 CQRS 中有多个事件的情况?任何人都可以举一个例子吗? 我问是因为聚合是事务边界,编写聚合的实现取决于这个问题的答案。 最佳答案 是的,例如考虑命令 AssignUserToG
大概我们可以通过应用相同的命令集来恢复状态,那么为什么不简单地存储命令而不是事件呢? 最佳答案 事件,传达“这发生在我们的系统中”。当一个命令被接受和处理时,事件就会发生。没有人可以拒绝或改变它发生的
我正在学习什么是CQRS模式,并了解到还有CQS模式。 当我尝试搜索时,我发现了很多关于CQRS的图表和信息,但没有找到太多关于CQS的信息。 CQRS 模式中的关键点 在 CQRS 中,有一种要写入
我的CQRS / ES设计中有时间问题。为了便于讨论,让我们基于Microsoft的 有关此主题的示例, session 管理(https://msdn.microsoft.com/en-us/lib
我只是想知道我将尝试实现的设计是否有效 CQRS。 我将有一个查询处理程序,它本身会向其他子处理程序发送更多查询。它的主要任务是聚合来自多个服务的结果。 从处理程序内部发送查询可以吗?我已经可以在我的
我第一次尝试使用 CQRS 和事件溯源,我有几点需要一些指导。我想实现一个 SO 风格的声誉系统。这似乎非常适合这种架构。 以 SO 为例。假设一个问题被投票,这会生成一个 UpvoteCommand
我的问题与this one有关.虽然相关的问题和答案说明了我们为什么要将它们分开,但我想确保我对意图的理解是正确的。在我见过的所有示例中,命令的意图似乎是它可以被拒绝,并且它更新内存中的对象,然后事件
这更像是一个与 CQRS 和 Axon 相关的理论问题。下面是一个不言自明的设置,代码是 伪代码 ,它并不意味着编译。 假设为了处理来自聚合“Foo”的命令,我们首先需要查询另一个聚合“Bar”的状态
亲们,我正在考虑将 Dapper.net 等 microORM 用于 CQRS 应用程序 (Asp.Net MVC) 的读取访问组件,并使用 Entity Framework 来操作域。 这是 CQR
我是一名优秀的程序员,十分优秀!