What is Design Pattern

Specification Design Pattern

Specification Pattern and Dynamic Query Building

ุชุนุฑูŠู ุงู„ู…ุดูƒู„ุฉ:

  1. ู…ุดุงูƒู„ Include:
    • ุงู„ุฎุฑู‚ ู„ู€ Open/Closed Principle:
      ุนู†ุฏ ุฅุถุงูุฉ ู…ูˆุฏูŠู„ ุฌุฏูŠุฏ (ู…ุซู„ Brand ุฃูˆ Category)ุŒ ุชุญุชุงุฌ ุชุนุฏูŠู„ ุงู„ูƒูˆุฏ ูˆุฅุถุงูุฉ Includes ุฌุฏูŠุฏุฉุŒ ู…ุง ูŠุฎุงู„ู ู…ุจุฏุฃ Closed for Modifications. ูƒู†ุง ุงุชูƒู„ู…ู†ุง ุนู†ู‡ุง (Open-Closed Principle) ุดูˆูู†ุง ุงู„ู…ุดูƒู„ุฉ ุฏูŠ ููŠ ุงู„ู€ Include
    • ุฃุฏุงุก ุบูŠุฑ ูุนุงู„:
      ุงุณุชุฎุฏุงู… Where ุจุฏู„ุงู‹ ู…ู† Find ูŠุคุฏูŠ ุฅู„ู‰ ุงุณุชุนู„ุงู…ุงุช ุบูŠุฑ ู…ุญุณู†ุฉุŒ ู„ุฃู† Where ุชุนู…ู„ ุนู„ู‰ ุงู„ุจูŠุงู†ุงุช ู…ุจุงุดุฑุฉ ู…ู† ู‚ุงุนุฏุฉ ุงู„ุจูŠุงู†ุงุชุŒ ุจูŠู†ู…ุง Find ุชุจุฏุฃ ุจุงู„ุจุญุซ ู…ุญู„ูŠู‹ุง (Local) ุซู… ุชู†ุชู‚ู„ ุฅู„ู‰ Remote ู„ุชุญุณูŠู† ุงู„ุฃุฏุงุก. ุดูˆูู†ุง ุงู„ู…ุดูƒู„ุฉ ุฏูŠ ููŠ ุงู„ุชุนุงู…ู„ ู…ุน Null ููŠ Brand ูˆ Category
  2. ุงู„ุญู„:
    • ุงุณุชุฎุฏุงู… Specification Pattern ู„ุจู†ุงุก ุงู„ุงุณุชุนู„ุงู…ุงุช ุจุดูƒู„ ุฏูŠู†ุงู…ูŠูƒูŠ ุฏูˆู† ุงู„ุญุงุฌุฉ ู„ุชุนุฏูŠู„ ุงู„ูƒูˆุฏ ุนู†ุฏ ุฅุถุงูุฉ ู…ูƒูˆู†ุงุช ุฌุฏูŠุฏุฉ.

Specification Pattern

  • ู…ุด ู…ู† ุถู…ู† ุงู„ู€ 23 ุงู„ู„ูŠ ู‚ูˆู„ู†ุง ุนู„ูŠู‡ู… ููŠ ุงู„ู€ What is Design Pattern

ุงู„ููƒุฑุฉ ุงู„ุฑุฆูŠุณูŠุฉ:

  • ุงู„ู€Specification ูŠุนุงู…ู„ ูƒู„ ุฌุฒุก ู…ู† ุงู„ุงุณุชุนู„ุงู… ูƒู€ ุฎุงุตูŠุฉ (Specification).
  • ุจู†ุงุก Method ุชุฃุฎุฐ ุงู„ู…ุนุงูŠูŠุฑ ุงู„ู…ุทู„ูˆุจุฉ ูˆุชูู†ุดุฆ ุงู„ุงุณุชุนู„ุงู… ุจุดูƒู„ ุฏูŠู†ุงู…ูŠูƒูŠ.
  • ุจูŠุณุงุนุฏู†ูŠ ุงู†ูŠ ุฃุจู†ูŠ ุงู„ู€ Query ุจุดูƒู„ DynamicุŒ ูุฃู†ุง ุจู‚ุณู… ุงู„ู€ Query ุจุชุงุนูŠ ู„ุฃุฌุฒุงุก ูˆุจุบูŠุฑู‡ุง ูƒู„ ู…ุฑุฉ ุนู„ู‰ ุญุณุจ ุงู„ู€ Business

ุฎุทูˆุงุช ุจู†ุงุก Specification Pattern

1. ุฅู†ุดุงุก Interface ู„ู„ู€ Specification:

  • ู‡ู†ุฑูˆุญ ู„ุจุฑูˆุฌูƒุช ุงู„ู€ Core
  • ู‡ู†ุนู…ู„ ููˆู„ุฏุฑ ู†ุณู…ูŠู‡ Specifications
  • ูˆู†ุนู…ู„ ุฌูˆุงู‡ Interface ุงุณู…ู‡ ISpecification
  • ูŠูƒูˆู† Public ูˆูŠูƒูˆู† Generic
  • ูˆุชุญุท Constrain ุงู†ู‡ุง ู„ุงุฒู… ุชูƒูˆู† BaseEntity
  • ูŠุญุชูˆูŠ ุนู„ู‰ (Signatures) ู„ูƒู„ Property ุชู…ุซู„ ุฌุฒุก ู…ู† ุงู„ุงุณุชุนู„ุงู….
  • ู†ุฑูˆุญ ู†ุดูˆู ุงู„ู€ Query ุงู„ู„ูŠ ูƒู†ุช ุนู…ู„ุชู‡ุง Static ู‡ู†ุง ุงู„ุชุนุงู…ู„ ู…ุน Null ููŠ Brand ูˆ Category
  • ู‡ู†ู„ุงู‚ูŠ ุจูŠุชูƒุฑุฑ where ูˆ Includes
  • ุงู„ุฌุฒุก ุงู„ู„ูŠ ูุงู„ุขุฎุฑ ุจุชุงุน FirstOrDefaultAsync ูˆ ToListAsync ุฏุง ุจูŠุชุบูŠุฑ ูู…ุด ู‡ู†ูƒุชุจู‡ ูˆู†ูƒุชุจู‡ ุจุนุฏู‡ุง
  • ู…ู…ูƒู† ุชู‚ูˆู„ ู…ููŠุด where ููŠ ุงู„ู€ GetAll ู„ุฃู†ูŠ ู…ู…ูƒู† ุฃุณุชุฎุฏู…ู‡ุง ุจุนุฏ ูƒุฏุง ู„ูˆ ุนุงูŠุฒ ุฃุธู‡ุฑ ููŠ ุงู„ุจุฑุงู†ุฏ ุฃูˆ ุงู„ูƒุงุชูŠุฌูˆุฑูŠ (ููŠ ุงู„ู€ Filters ุนู…ูˆู…ู‹ุง)
  • ุงู„ู€ FirstOrDefault ุจูŠุฎู„ูˆุง ุงู„ู€ Query ูŠุชุนู…ู„ู‡ุง Immediate Execution
    • Three Categories for Immediate (Element Operators - Casting Operator - Aggregation Operator)
public interface ISpecification<T> where T : BaseEntity
{
	// ุฏูŠ ุงู„ู„ูŠ ู‡ุจุนุชู‡ุง ู„ู„ูˆูŠุฑ
    Expression<Func<T, bool>> Criteria { get; set;}
    List<Expression<Func<T, object>>> Includes { get; set; }
}
  • ุจูŠุงุฎุฏูˆุง Cs Delegates
  • ุงู„ุงุชู†ูŠู† ุจูŠุนู…ู„ูˆุง Return ูู‡ู†ุจุนุฏ ุนู† ุงู„ู€ Action ูˆู†ุณุชุฎุฏู… ุงู„ู€ Func
  • ุงู„ุฃูˆู„ where ุจูŠุนู…ู„ Return ู„ู€ True ุฃูˆ False ูˆุจูŠุงุฎุฏ ุงู„ู€ Product ุจุชุงุนูŠ ู…ุซู„ู‹ุง
  • ุงู†ู…ุง ุงู„ุชุงู†ูŠ ุจูŠุฑุฌุน ู…ู† ุงู„ู€ DB ูู…ู…ูƒู† ุญุฑููŠู‹ุง ูŠุฑุฌุน ุฃูŠ ุญุงุฌุฉ ูุจู†ู‚ูˆู„ ุงู†ู‡ ุจูŠุฑุฌุน object

2. ุฅู†ุดุงุก Class ู„ุชู†ููŠุฐ ุงู„ู€ Interface:

  • ุงู„ูƒู„ุงุณ ูŠุญู…ู„ ุชูุงุตูŠู„ ุงู„ุงุณุชุนู„ุงู….
  • ู‡ู†ุนู…ู„ู‡ ููŠ ุงู„ู€ Core ููŠ ููˆู„ุฏุฑ ุงู„ู€ Specifications ุจุฑุถูˆ
  • ู…ู…ูƒู† ุฃุนู…ู„ู‡ุง Full property ุนุงุฏูŠ ู„ูˆ ู‡ุนู…ู„ Validation ุฃูˆ ูƒุฏุง ูˆุงุชูƒู„ู…ู†ุง ุนู† ุงู„ู…ูˆุถูˆุน ุฏุง ููŠ ุงู„ู€ Cs Properties
public static class BaseSpecifications<T> : ISpecification<T> where T : BaseEntity
{
    public Expression<Func<T, bool>> Criteria { get; private set; }
    public List<Expression<Func<T, object>>> Includes { get;} = new List<Expression<Func<T, object>>>();
 
	public BaseSpecification()
	{
	}
 
    public BaseSpecification(Expression<Func<T, bool>> criteria)
    {
        Criteria = criteria;
    }
 
/*
    public void AddInclude(Expression<Func<T, object>> includeExpression)
    {
        Includes.Add(includeExpression);
    }
*/
}
  • ู„ู…ุง ู†ุนู…ู„ ุงู„ู€ Method ูˆุจุชุงุฎุฏ ุงู„ุฅุชู†ูŠู† ุจุฑุงู…ุชุฑ
  • ู„ูˆ ุงู„ู€ Method ู‡ุชุนู…ู„ Query ุชุฌูŠุจู„ูŠ ูƒู„ ุงู„ู€ Products ูŠุจู‚ุง ุงู„ู€ Criteria ุจู€Null ุงู†ู…ุง ู„ูˆ ู‡ูŠุฌูŠุจ ุญุงุฌุงุช ู…ุญุฏุฏุฉ ูุงู„ู…ูุฑูˆุถ ุชุจู‚ุง ุจู€ Expression
  • ูุงู„ู„ูŠ ู‡ูŠุญุฏุฏ ุงู„ู€ Method
  • ูุฃู†ุง ู‡ุนู…ู„ ุงุชู†ูŠู† Ctor ูˆุงุญุฏ ุจูŠุญุท ุงู„ู€ criteria ุจู€ Null ูˆุงู„ุชุงู†ูŠ ุจูŠุญุทู‡ ุจุงู„ู€ Expression
  • ููŠ ุงู„ุนุงุฏูŠ ู„ูˆ ุนู…ู„ุช Empty ctor ุงู„ู„ูŠ ุจูŠุญุตู„ ุงู† ู‡ูˆ ุจูŠุญุท ูƒู„ ุงู„ู€ Properties ุจู€ Null ุฃูˆ ุจุงู„ู‚ูŠู…ุฉ ุงู„ู€ Default ุจุชุงุนู†ุงุŒ ูุงุญู†ุง ู‡ู†ุนู…ู„ ูˆุงุญุฏ ูˆู†ุนู…ู„ Initialization ู„ู„ู€ Includes ูˆู†ุณูŠุจ ุงู„ู€ Criteria ุนุดุงู† ุชูุถู„ ุจู€ Null
  • ู…ู…ูƒู† ู†ุญุทู‡ุง ุจู‚ูŠู…ุฉ ุนู„ู‰ ุทูˆู„ ุจุฏู„ ู…ุง ุฃุณุชู†ู‰ ุงู„ู€ Ctor ูŠุชู†ูุฐ

3. ุฅู†ุดุงุก Method ู„ุชุทุจูŠู‚ ุงู„ู€ Specification:

  • ู‡ู†ุนู…ู„ู‡ุง ููŠ ุงู„ู€ Repository
  • ู‡ุญุชุงุฌ ุฃุนู…ู„ Class ุงุณู…ู‡ SpecificationEvaluator ูˆุญุทูŠุชู‡ ููŠ ุงู„ู€ Repository ุนุดุงู† ุฃู†ุง ู‡ุณุชุฎุฏู…ู‡ ู…ุน ูƒู„ุงุณ ุงู„ู€ Generic Repository
  • ุชุฃุฎุฐ:
    1. ุงู„ู€DbSet: ุงู„ุฌุฏูˆู„ ุงู„ุฐูŠ ุชุนู…ู„ ุนู„ูŠู‡.
    2. ุงู„ู€Specification Object: ุชูุงุตูŠู„ ุงู„ุงุณุชุนู„ุงู… (CriteriaุŒ Includes).
internal static class SpecificationsEvaluator<T> where T : BaseEntity
 
public static IQueryable<T> GetQuery(IQueryable<T> inputQuery, ISpecification<T> spec)
{
	var query = inputQuery;
    // Apply Criteria
    if (spec.Criteria != null)
    {
        query = query.Where(spec.Criteria);
    }
 
    // Apply Includes
    query = spec.Includes.Aggregate(query, (currentQuery, includeExpression) 
	    => currentQuery.Include(includeExpression));
 
	// query = _dbContext.Set<Product>().Where(P => P.Id==1)
	// Includes
		// 1. P => P.Brand
		// 2. P => P.Category
// _dbContext.Set<Product>().Where(P => P.Id==1).Include(P => P.Brand)
// _dbContext.Set<Product>().Where(P => P.Id==1).Include(P => P.Brand).Include(P => P.Category)
    return query;
}
Aggregate

ู…ูˆุฌูˆุฏุฉ ููŠ ุงู„ู€ LINQ ูˆุดุฑุญู‡ุง ู‡ู†ุง Cs Aggregate ุจู†ุนู…ู„ Accumulate ู„ูƒู„ ุงู„ู€ Includes ู…ุน ุจุนุถ ูู‡ู†ุณุชุฎุฏู… ุงู„ู€ Aggregate

string[] Names = {"Corn", "Pop"};
string Message = "Hello,";
Message = Names.Aggregate(Message, (x, y) => $"{x} {y}");
 
Console.WriteLine(Message);

ู‡ูˆ ูƒู„ ุงู„ู„ูŠ ู‡ูŠุนู…ู„ู‡ ุจุชุฏูŠู„ู‡ ุฃูˆู„ ู‚ูŠู…ุฉ ุงู„ู„ูŠ ู‡ูŠุจู†ูŠ ุนู„ูŠู‡ุง ุงู„ู„ูŠ ู‡ูŠ ู‡ู†ุง Message ูˆุจุนุฏู‡ุง ุจุชุฏูŠู„ู‡ ุงู„ู€ Parameters ูˆุจูŠุจู‚ุง ุฃูˆู„ Parameter ููŠ ุงู„ุฃูˆู„ ุงู„ู„ูŠ ู‡ูŠ ุฃูˆู„ ู‚ูŠู…ุฉ ุนู†ุฏูŠ ูˆุงู„ุชุงู†ูŠ ุจูŠุงุฎุฏ ุฃูˆู„ ู‚ูŠู…ุฉ ููŠ ุงู„ู€ Collection ุงู„ู„ูŠ ู†ุงุฏูŠุช ุจูŠู‡ุง ุงู„ู„ูŠ ู‡ูŠ ู‡ู†ุง ุงู„ู€ Names ูˆู‡ูƒุฐุง ู„ุญุฏ ู…ุง ุชุฎู„ุต


4. ุงุณุชุฎุฏุงู… ุงู„ู€ Method

  • ูƒู†ุง ุจู†ุญุท ู…ุณูƒู† ููŠ ุงู„ู€ Generic Repository ู„ูˆ ุชูุชูƒุฑ ููŠ ุงู„ู€ Include
  • ู‡ู†ุนู…ู„ ุงุชู†ูŠู† Method ุชุงู†ูŠูŠู† ูˆุงุญุฏ ุงุณู…ู‡ GetAllWithSpecAsync ูˆุงู„ุชุงู†ูŠ GetWithSpecAsync ูˆู†ุญุทู‡ ููŠ ุงู„ู€ Interface ู…ุชู†ุณุงุด
Task<IEnumerable<T>> GetAllWithSpecAsync(ISpecifications<T> spec);
Task<T?> GetWithSpecAsync(ISpecifications<T> spec);

ูˆุจุนุฏูŠู† ู†ุฑูˆุญ ุนู„ู‰ Class ุงู„ู€ GenericRepository ูˆู†ุนู…ู„ู‡ู… Implement

public Task<IEnumerable<T>> GetAllWithSpecAsync(ISpecifications<T> spec)
{
	return await SpecificationsEvaluator<T>.GetQuery(_dbContext.Set<T>(), spec).ToListAsync();
}
 
public Task<IEnumerable<T>> GetWithSpecAsync(ISpecifications<T> spec)
{
	return await SpecificationsEvaluator<T>.GetQuery(_dbContext.Set<T>(), spec).FirstOrDefaultAsync();
}

ุจุณ ุงู†ุง ุนู†ุฏูŠ ูƒุฏุง ุฌุฒุก ู…ุชูƒุฑุฑ ููŠ ุงู„ู€ GetAll ูˆุงู„ู€ Get ูู‡ุนู…ู„ Method Private

private IQueryable<T> ApplySpecifications(ISpecifications<T> spec)
{
	return SpecificationsEvaluator<T>.GetQuery(_dbContext.Set<T>(), spec);
}

ูˆู†ุบูŠุฑ ุจู‚ุง ุงู„ู€ Methods ูˆุชุจู‚ุง ูƒุฏุง

public Task<IEnumerable<T>> GetAllWithSpecAsync(ISpecifications<T> spec)
{
	return await ApplySpecifications(spec).ToListAsync();
}