{"id":19927147,"url":"https://github.com/murilobeltrame/hello-dotnet-specification-pattern","last_synced_at":"2026-04-28T00:32:06.132Z","repository":{"id":80974935,"uuid":"563549725","full_name":"murilobeltrame/hello-dotnet-specification-pattern","owner":"murilobeltrame","description":"My Take on Specification Pattern with .Net Core and EF Core","archived":false,"fork":false,"pushed_at":"2022-11-28T13:35:15.000Z","size":41,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-20T11:45:48.827Z","etag":null,"topics":["csharp","dotnet-core","entity-framework-core","patterns"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/murilobeltrame.png","metadata":{"files":{"readme":"README.MD","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-11-08T21:02:57.000Z","updated_at":"2025-04-12T03:12:38.000Z","dependencies_parsed_at":null,"dependency_job_id":"60a67c49-f612-475b-aea3-d8247b4b2005","html_url":"https://github.com/murilobeltrame/hello-dotnet-specification-pattern","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/murilobeltrame/hello-dotnet-specification-pattern","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/murilobeltrame%2Fhello-dotnet-specification-pattern","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/murilobeltrame%2Fhello-dotnet-specification-pattern/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/murilobeltrame%2Fhello-dotnet-specification-pattern/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/murilobeltrame%2Fhello-dotnet-specification-pattern/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/murilobeltrame","download_url":"https://codeload.github.com/murilobeltrame/hello-dotnet-specification-pattern/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/murilobeltrame%2Fhello-dotnet-specification-pattern/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32361477,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-27T20:07:02.737Z","status":"ssl_error","status_checked_at":"2026-04-27T20:07:00.910Z","response_time":128,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","dotnet-core","entity-framework-core","patterns"],"created_at":"2024-11-12T22:32:26.682Z","updated_at":"2026-04-28T00:32:06.110Z","avatar_url":"https://github.com/murilobeltrame.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Specification Pattern tryout\n\nThis repo covers my take on how to implement Specification Pattern using .Net Core and Entity Framework Core.\u003cbr /\u003e\nSpecification Pattern is specially useful when expressing business logic throught repository manipulation. More on [Specification Pattern](https://en.wikipedia.org/wiki/Specification_pattern).\u003cbr /\u003e\nI preffer to implement Specification Pattern combined with Repository Pattern, so there`s the highlights:\n\n[Specification generic base class](src/SpecificationPattern.Application/Specifications/_BaseSpecification_T_.cs):\n\n```cs\npublic abstract class BaseSpecification\u003cTEntity, TProjection\u003e : ISpecification\u003cTEntity, TProjection\u003e where TEntity : BaseEntity\n{\n    public IList\u003cExpression\u003cFunc\u003cTEntity, bool\u003e\u003e\u003e WhereExpressions { get; protected set; } = new List\u003cExpression\u003cFunc\u003cTEntity, bool\u003e\u003e\u003e();\n\n    public IList\u003cExpression\u003cFunc\u003cTEntity, object\u003e\u003e\u003e IncludeExpressions { get; protected set; } = new List\u003cExpression\u003cFunc\u003cTEntity, object\u003e\u003e\u003e();\n\n    public IList\u003cExpression\u003cFunc\u003cTProjection, object\u003e\u003e\u003e OrderByExpressions { get; protected set; } = new List\u003cExpression\u003cFunc\u003cTProjection, object\u003e\u003e\u003e();\n\n    public ushort? Take { get; protected set; }\n\n    public uint? Skip { get; protected set; }\n\n    public Expression\u003cFunc\u003cTEntity, TProjection\u003e\u003e? SelectExpression { get; protected set; }\n}\n```\n\n[Repository genric base class](src/SpecificationPattern.Infra/Repositories/Repository_T_.cs):\n\n```cs\npublic class Repository\u003cTEntity\u003e : IRepository\u003cTEntity\u003e where TEntity : BaseEntity\n{\n    private readonly ApplicationContext _db;\n\n    public Repository(ApplicationContext db)\n    {\n        _db = db;\n    }\n\n    public async Task\u003cTEntity\u003e CreateAsync(TEntity record, CancellationToken cancellationToken = default) =\u003e\n        (await _db.Set\u003cTEntity\u003e().AddAsync(record, cancellationToken)).Entity;\n\n    public async Task DeleteAsync(Guid id, CancellationToken cancellationToken = default)\n    {\n        var entitity = await GetAsync(new GetByIdSpecification\u003cTEntity\u003e(id), cancellationToken);\n        _db.Set\u003cTEntity\u003e().Remove(entitity);\n    }\n\n    public async Task\u003cbool\u003e ExistsAsync(ISpecification\u003cTEntity\u003e specification, CancellationToken cancellationToken = default) =\u003e\n            (await GetAsync(specification, cancellationToken)) != null;\n\n    public async Task\u003cTProjection\u003e GetAsync\u003cTProjection\u003e(ISpecification\u003cTEntity, TProjection\u003e specification, CancellationToken cancellationToken = default)\n    {\n        var query = ProcessQuery(specification);\n\n        var result = await query.FirstOrDefaultAsync(cancellationToken);\n        if (result == null) throw new NotFoundException();\n        return result;\n    }\n\n    public async Task\u003cIEnumerable\u003cTProjection\u003e\u003e FetchAsync\u003cTProjection\u003e(ISpecification\u003cTEntity, TProjection\u003e specification, CancellationToken cancellationToken = default)\n    {\n        var query = ProcessQuery(specification);\n        if (specification.Skip.HasValue)\n        {\n            query = query.Skip((int)specification.Skip.Value);\n        }\n        if (specification.Take.HasValue)\n        {\n            query = query.Take(specification.Take.Value);\n        }\n        return await query.ToListAsync(cancellationToken);\n    }\n\n    public Task UpdateAsync(TEntity record, CancellationToken cancellationToken = default)\n    {\n        throw new NotImplementedException();\n    }\n\n    public async Task SaveChangesAsync(CancellationToken cancellationToken = default) =\u003e\n        await _db.SaveChangesAsync(cancellationToken);\n\n    private IQueryable\u003cTProjection\u003e ProcessQuery\u003cTProjection\u003e(ISpecification\u003cTEntity, TProjection\u003e specification)\n    {\n        IQueryable\u003cTProjection\u003e projectedQuery;\n        var query = _db.Set\u003cTEntity\u003e().AsQueryable();\n\n        if (specification.WhereExpressions.Any())\n        {\n            foreach (var whereExpression in specification.WhereExpressions)\n            {\n                query = query.Where(whereExpression);\n            }\n        }\n\n        if (specification.IncludeExpressions.Any())\n        {\n            foreach (var includeExpression in specification.IncludeExpressions)\n            {\n                query = query.Include(includeExpression);\n            }\n        }\n\n        if (specification.SelectExpression != null)\n        {\n            projectedQuery = query.Select(specification.SelectExpression);\n        } else\n        {\n            projectedQuery = (IQueryable\u003cTProjection\u003e)query;\n        }\n\n        if (specification.OrderByExpressions.Any())\n        {\n            for (int i = 0; i \u003c specification.OrderByExpressions.Count(); i++)\n            {\n                var orderByExpression = specification.OrderByExpressions.ElementAt(i);\n                if (i == 0) projectedQuery = projectedQuery.OrderBy(orderByExpression);\n                else projectedQuery = ((IOrderedQueryable\u003cTProjection\u003e)projectedQuery).ThenBy(orderByExpression);\n            }\n        }\n\n        return projectedQuery;\n    }\n}\n```\n\n## How to run\n\n- Run the DB Server container:\n\n```sh\n$ ./src/scripts/db.sh\n```\n\n- Run Create Database script\n\n```sh\n$ ./src/scripts/database.sh\n```\n\n- Set user secret from \"API\" and \"Infra.Migration\" projects. To do that run the following command inside `src/SpecificationPattern.Api` **and** `src/SpecificationPattern.Infra.Migration` directory:\n\n```sh\n$ dotnet user-secrets set \"ConnectionStrings:ApplicationContext\" \"Host=localhost;Database=WineStore;Username=postgres;Password=mysecretpassword;\"\n```\n\n- Restore tools. Will restore de Entity Framework Core CLI to run migrations:\n\n```sh\n$ dotnet tool restore\n```\n\n- Run the migrations command inside `src/SpecificationPattern.Infra.Migration`directory. Will create the database schema:\n\n```sh\n$ dotnet ef database update\n```\n\n- Run the API project\n\n## Dependencies\n\n- [.NET 6](https://dotnet.microsoft.com/en-us/download)\n- [Docker](https://docs.docker.com/get-docker/) (or [Rancher Desktop](https://rancherdesktop.io) running `dockerd`)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmurilobeltrame%2Fhello-dotnet-specification-pattern","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmurilobeltrame%2Fhello-dotnet-specification-pattern","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmurilobeltrame%2Fhello-dotnet-specification-pattern/lists"}