lundi 24 juillet 2023

Specification pattern for commands

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<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);
        }
    }

Specification to include related entities:

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

Specification extension class for entity framework context

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);
        }

Method in repository class:

public Task<Media?> 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.

Aucun commentaire:

Enregistrer un commentaire