命令的规范模式

huangapple go评论81阅读模式
英文:

Specification pattern for commands

问题

我正在编写一个ASP.NET Web API,并使用CQRS模式与中介者、仓储模式+工作单元,现在我想添加一个规范模式,我已经像这样实现了该模式(显示每个类的一部分):

public abstract class BaseSpecification<T> : ISpecification<T>
{
    protected BaseSpecification(Expression<Func<T, bool>> criteria)
    {
        Criteria = criteria;
    }
    protected BaseSpecification()
    {

    }
    public Expression<Func<T, bool>> Criteria { get; }
    public List<Expression<Func<T, object>>> Includes { get; } = new();

    protected virtual void AddInclude(Expression<Func<T, object>> includeExpression)
    {
        Includes.Add(includeExpression);
    }
}

包含相关实体的规范:

public MediaIncludePeopleAndGenres(int mediaId) 
    : base(x => x.MediaId == mediaId)
{
    AddInclude(x => x.MediaGenres);
    AddInclude(x => x.MediaPeople);
}

用于Entity Framework上下文的规范扩展类:

public static IQueryable<T> Specify<T>(this IQueryable<T> query, ISpecification<T> spec) where T : class
{
    var resultWithIncludes = spec
        .Includes
        .Aggregate(query, (current, include) => current.Include(include));

    return resultWithIncludes.Where(spec.Criteria);
}

仓储类中的方法:

public Task<Media?> GetMediaByIdAsync(int id)
{
    return _context.Media
        .Specify(new MediaIncludePeopleAndGenres(id))
        .FirstOrDefaultAsync();
}

它的效果与我预期的一样,但是我没有找到类似的方法来处理命令(如POST或PUT),我应该在规范中放入哪些功能?能否请给我一个符合我的方法的示例?
现在,我正在这样调用媒体的POST方法:

仓储类:

public void AddSingleMedia(Media media)
{
    _context.Media.Add(media);
}

P.S:我没有显示命令或查询处理程序,因为我只在那里进行映射并调用仓储方法。

更新:
对于某些POST和PUT方法,我正在使用https://entityframework-extensions.net/bulk-extensions提供的一组用于批量操作的方法,它看起来像这样:

public Task BulkInsertMediaAsync(List<Media> mediaList)
{
    return _context.Media.BulkInsertAsync(mediaList, options => options.IncludeGraph = true);
}

希望这能帮助你了解如何在规范中处理命令功能。

英文:

I am writing an asp.net web api and I am using a cqrs pattern with mediator, repository pattern + unit of work, and now I want to add a specification pattern, I already implemented the pattern like this (showing a part of each class):

public abstract class BaseSpecification&lt;T&gt; : ISpecification&lt;T&gt;
    {
        protected BaseSpecification(Expression&lt;Func&lt;T, bool&gt;&gt; criteria)
        {
            Criteria = criteria;
        }
        protected BaseSpecification()
        {

        }
        public Expression&lt;Func&lt;T, bool&gt;&gt; Criteria { get; }
        public List&lt;Expression&lt;Func&lt;T, object&gt;&gt;&gt; Includes { get; } = new();

        protected virtual void AddInclude(Expression&lt;Func&lt;T, object&gt;&gt; includeExpression)
        {
            Includes.Add(includeExpression);
        }
    }

Specification to include related entities:

public MediaIncludePeopleAndGenres(int mediaId) 
            : base(x =&gt; x.MediaId == mediaId)
        {
            AddInclude(x =&gt; x.MediaGenres);
            AddInclude(x =&gt; x.MediaPeople);
        }

Specification extension class for entity framework context

public static IQueryable&lt;T&gt; Specify&lt;T&gt;(this IQueryable&lt;T&gt; query, ISpecification&lt;T&gt; spec) where T : class
        {
            var resultWithIncludes = spec
                .Includes
                .Aggregate(query, (current, include) =&gt; current.Include(include));

            return resultWithIncludes.Where(spec.Criteria);
        }

Method in repository class:

public Task&lt;Media?&gt; GetMediaByIdAsync(int id)
        {
            return _context.Media
                .Specify(new MediaIncludePeopleAndGenres(id))
                .FirstOrDefaultAsync();
        }

It works just as I expected, but I didn't find a similar approach for commands (like post or put), what functionality should I put in the specification? Can I please get an example according to my approach?
Now I am calling post media method like this:
repository class

public void AddSingleMedia(Media media)
    {
         _context.Media.Add(media);
    }

P.S: I didn't show commands or queries handlers, because I only do mapping there, and calling the repository methods.
UPDATED:
For some post and put methods I am using a https://entityframework-extensions.net/bulk-extensions set of methods for bulk operations and it looks like this

public Task BulkInsertMediaAsync(List&lt;Media&gt; mediaList)
        {
            return _context.Media.BulkInsertAsync(mediaList, options =&gt; options.IncludeGraph = true);
        }

答案1

得分: 1

根据定义,例如来自维基

> 在计算机编程中,规范模式是一种特定的软件设计模式,通过使用布尔逻辑将业务规则链接在一起,可以重新组合业务规则。该模式经常在领域驱动设计的上下文中使用。

在数据访问的上下文中,术语"业务逻辑"不太合适,但我们可以用"过滤"来替代它,因此对于POST和PUT端点,过滤主要用于批量更新(而且我假设你正在使用EF,在第7版之前和/或没有批量扩展的情况下,EF并不太适合,除非你创建一个执行批量更新的命令(例如类似于update ... set ... where some_filter,其中some_filter应该是"动态"构建的),否则规范可能不太相关。

P.S.

查看ardalis的规范。它可以为你的实现提供一些启发(或者你可能希望直接使用它)。

英文:

As per definition, for example from Wiki:

> In computer programming, the specification pattern is a particular software design pattern, whereby business rules can be recombined by chaining the business rules together using boolean logic. The pattern is frequently used in the context of domain-driven design.

In context of data access the term business logic is not appropriate one, but we can substitute it with "filtering", so for POST and PUT endpoints filtering can be mainly applied for batch updates (and EF, which I assume you are using, is not very suitable for, at least before 7th version and/or without batch extensions), so unless you will create a command which performs some batch update (i.e. something like update ... set ... where some_filter, where the some_filter should be build "dynamically") the specification would not be that relevant.

P.S.

Check out Specification by ardalis. It can give you some inspiration for implementation (or maybe you will want to just go straightly use it).

huangapple
  • 本文由 发表于 2023年7月24日 17:07:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/76752930.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定