Skip to content

Commit f4f7851

Browse files
committed
init
1 parent dac9f8f commit f4f7851

File tree

17 files changed

+696
-2
lines changed

17 files changed

+696
-2
lines changed

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
name: Bug report
3+
about: Create a report to help us improve
4+
title: ''
5+
labels: bug
6+
assignees: ''
7+
8+
---
9+
10+
**Describe the bug**
11+
A clear and concise description of what the bug is.
12+
13+
**To Reproduce**
14+
Steps to reproduce the behavior:
15+
1. Go to '...'
16+
2. Click on '....'
17+
3. Scroll down to '....'
18+
4. See error
19+
20+
**Expected behavior**
21+
A clear and concise description of what you expected to happen.
22+
23+
**Screenshots**
24+
If applicable, add screenshots to help explain your problem.
25+
26+
**Additional context**
27+
Add any other context about the problem here.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
name: Feature request
3+
about: Suggest an idea for this project
4+
title: ''
5+
labels: enhancement
6+
assignees: ''
7+
8+
---
9+
10+
**Is your feature request related to a problem? Please describe.**
11+
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12+
13+
**Describe the solution you'd like**
14+
A clear and concise description of what you want to happen.
15+
16+
**Describe alternatives you've considered**
17+
A clear and concise description of any alternative solutions or features you've considered.
18+
19+
**Additional context**
20+
Add any other context or screenshots about the feature request here.

.github/workflows/codeql.yml

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# For most projects, this workflow file will not need changing; you simply need
2+
# to commit it to your repository.
3+
#
4+
# You may wish to alter this file to override the set of languages analyzed,
5+
# or to provide custom queries or build logic.
6+
#
7+
# ******** NOTE ********
8+
# We have attempted to detect the languages in your repository. Please check
9+
# the `language` matrix defined below to confirm you have the correct set of
10+
# supported CodeQL languages.
11+
#
12+
name: "CodeQL"
13+
14+
on:
15+
push:
16+
branches: [ "main" ]
17+
pull_request:
18+
# The branches below must be a subset of the branches above
19+
branches: [ "main" ]
20+
schedule:
21+
- cron: '24 10 * * 4'
22+
23+
jobs:
24+
analyze:
25+
name: Analyze
26+
runs-on: ubuntu-latest
27+
permissions:
28+
actions: read
29+
contents: read
30+
security-events: write
31+
32+
strategy:
33+
fail-fast: false
34+
matrix:
35+
language: [ 'csharp' ]
36+
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37+
# Use only 'java' to analyze code written in Java, Kotlin or both
38+
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
39+
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
40+
41+
steps:
42+
- name: Checkout repository
43+
uses: actions/checkout@v3
44+
45+
# Initializes the CodeQL tools for scanning.
46+
- name: Initialize CodeQL
47+
uses: github/codeql-action/init@v2
48+
with:
49+
languages: ${{ matrix.language }}
50+
# If you wish to specify custom queries, you can do so here or in a config file.
51+
# By default, queries listed here will override any specified in a config file.
52+
# Prefix the list here with "+" to use these queries and those in the config file.
53+
54+
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
55+
# queries: security-extended,security-and-quality
56+
57+
58+
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
59+
# If this step fails, then you should remove it and run the build manually (see below)
60+
- name: Autobuild
61+
uses: github/codeql-action/autobuild@v2
62+
63+
# ℹ️ Command-line programs to run using the OS shell.
64+
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
65+
66+
# If the Autobuild fails above, remove it and uncomment the following three lines.
67+
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
68+
69+
# - run: |
70+
# echo "Run, Build Application using script"
71+
# ./location_of_script_within_repo/buildscript.sh
72+
73+
- name: Perform CodeQL Analysis
74+
uses: github/codeql-action/analyze@v2
75+
with:
76+
category: "/language:${{matrix.language}}"

.github/workflows/xunit-tests.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# This workflow will build a .NET project
2+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
3+
name: xunit-tests
4+
on:
5+
push:
6+
branches: [ "main" ]
7+
pull_request:
8+
branches: [ "main" ]
9+
jobs:
10+
build:
11+
12+
runs-on: ubuntu-latest
13+
strategy:
14+
matrix:
15+
dotnet-version: [ '7.0.x' ]
16+
permissions:
17+
actions: read
18+
contents: read
19+
security-events: write
20+
21+
steps:
22+
- uses: actions/checkout@v3
23+
- name: Setup .NET Core SDK ${{ matrix.dotnet-version }}
24+
uses: actions/setup-dotnet@v3
25+
with:
26+
dotnet-version: ${{ matrix.dotnet-version }}
27+
28+
- name: Install dependencies
29+
run: dotnet restore
30+
31+
- name: Test
32+
run: dotnet test test/SampleDotnet.RepositoryFactory.Tests/SampleDotnet.RepositoryFactory.Tests.csproj --no-restore --verbosity normal

README.md

Lines changed: 114 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,114 @@
1-
# SampleDotnet.RepositoryFactory
2-
SampleDotnet.RepositoryFactory
1+
[![Nuget](https://img.shields.io/badge/package-SampleDotnet.RepositoryFactory-brightgreen.svg?maxAge=259200)](https://www.nuget.org/packages/SampleDotnet.RepositoryFactory)
2+
[![CodeQL](https://github.com/msx752/SampleDotnet.RepositoryFactory/actions/workflows/codeql.yml/badge.svg?branch=main)](https://github.com/msx752/SampleDotnet.RepositoryFactory/actions/workflows/codeql.yml)
3+
[![MIT](https://img.shields.io/badge/License-MIT-blue.svg?maxAge=259200)](https://github.com/msx752/SampleDotnet.RepositoryFactory/blob/master/LICENSE.md)
4+
5+
# EFCore DbContext RepositoryFactory Pattern managed by DbContextFactory
6+
EntityFrameworkCore doesn't support multiple parallel operations, when we need parallel actions in different threads such as adding or deleting on the same DbContext, It throws an exception when calling SaveChanges [source](https://learn.microsoft.com/en-us/ef/core/dbcontext-configuration/#avoiding-dbcontext-threading-issues).
7+
8+
NOTE: **DbContext service scope set as Transient which managed by IServiceScopeFactory**
9+
10+
# How to Use
11+
``` c#
12+
using SampleDotnet.RepositoryFactory;
13+
```
14+
ServiceCollection Definition
15+
``` c#
16+
services.AddDbContextFactory<UserDbContext>(opt =>
17+
opt.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
18+
```
19+
then we call transient scoped DbContext
20+
``` c#
21+
public class UserController : ControllerBase
22+
{
23+
private readonly IDbContextFactory<UserDbContext> _contextFactory;
24+
25+
public UserController(IDbContextFactory<UserDbContext> contextFactory)
26+
{
27+
_contextFactory = contextFactory;
28+
}
29+
30+
[HttpGet("{id}")]
31+
public ActionResult Get(Guid id)
32+
{
33+
using (var repository = _contextFactory.CreateRepository())
34+
{
35+
var personal = repository.FirstOrDefault<UserEntity>(f => f.Id == id);
36+
37+
//some operations goes here....
38+
39+
repository.Delete(personal);
40+
41+
//some operations goes here....
42+
43+
repository.SaveChanges();
44+
}
45+
}
46+
}
47+
```
48+
49+
# Additional Feature
50+
- Changes Tracker for each Repository using 'IRepositoryEntryNotifier'
51+
- helps to manage entities before SaveChanges such as updating Adding or Updating Time on Entity
52+
53+
# Usage Example of the IRepositoryEntryNotifier
54+
- we have a User entity which derived from [IHasTimestamps](https://learn.microsoft.com/en-us/ef/core/logging-events-diagnostics/events#example-timestamp-state-changes), when we add a new User or delete a User this event will be triggered by Repository
55+
56+
``` c#
57+
using Microsoft.EntityFrameworkCore;
58+
using Microsoft.EntityFrameworkCore.ChangeTracking;
59+
using SampleDotnet.RepositoryFactory.Interfaces;
60+
61+
namespace SampleDotnet.RepositoryFactory.Tests
62+
{
63+
public class UserEntity : IHasTimestamps
64+
{
65+
public Guid Id { get; set; }
66+
public string Name { get; set; }
67+
public string SurName { get; set; }
68+
69+
public DateTime CreatedAt { get; set; }
70+
public DateTime UpdatedAt { get; set; }
71+
}
72+
73+
public interface IHasTimestamps
74+
{
75+
DateTime CreatedAt { get; set; }
76+
DateTime UpdatedAt { get; set; }
77+
}
78+
79+
public class MyRpositoryNotifier : IRepositoryEntryNotifier
80+
{
81+
public void RepositoryEntryEvent(object sender, EntityEntryEventArgs e, DbContext dbContext, IServiceProvider serviceProvider)
82+
{
83+
if (e.Entry.State == EntityState.Unchanged || e.Entry.State == EntityState.Detached)
84+
return;
85+
86+
if (e.Entry.Entity is IHasTimestamps entityWithTimestamps)
87+
{
88+
switch (e.Entry.State)
89+
{
90+
case EntityState.Added:
91+
entityWithTimestamps.CreatedAt = DateTime.UtcNow;
92+
break;
93+
94+
case EntityState.Modified:
95+
entityWithTimestamps.UpdatedAt = DateTime.UtcNow;
96+
break;
97+
98+
case EntityState.Deleted:
99+
//entity deleted on the database
100+
//entityWithTimestamps.DeletedAt = DateTime.UtcNow;
101+
break;
102+
}
103+
}
104+
}
105+
}
106+
}
107+
```
108+
109+
- ServiceCollection Definition (Singleton scope)
110+
``` c#
111+
services.AddSingleton<IRepositoryEntryNotifier, MyRpositoryNotifier>();
112+
```
113+
114+
- after that call `var repository = _contextFactory.CreateRepository();` and add or delete entity

SECURITY.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Security Policy
2+
3+
## Supported Versions
4+
5+
Use this section to tell people about which versions of your project are
6+
currently being supported with security updates.
7+
8+
| Version | Supported |
9+
| ------- | ------------------ |
10+
| < 5.0 | :x: |
11+
| 5.0.x | :white_check_mark: |
12+
| 6.0.x | :white_check_mark: |
13+
| 7.0.x | :white_check_mark: |
14+
15+
## Reporting a Vulnerability
16+
17+
Use this section to tell people how to report a vulnerability.
18+
19+
Tell them where to go, how often they can expect to get an update on a
20+
reported vulnerability, what to expect if the vulnerability is accepted or
21+
declined, etc.

SampleDotnet.RepositoryFactory.sln

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.4.33213.308
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleDotnet.RepositoryFactory", "src\SampleDotnet.RepositoryFactory\SampleDotnet.RepositoryFactory.csproj", "{836ED2EE-E76B-43E5-AF31-A3697DEABB3A}"
7+
EndProject
8+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{163596FF-95DE-462F-83CD-23FD93C83353}"
9+
EndProject
10+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{5096EE52-D2B3-4173-A90A-5432310F47EC}"
11+
EndProject
12+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleDotnet.RepositoryFactory.Tests", "test\SampleDotnet.RepositoryFactory.Tests\SampleDotnet.RepositoryFactory.Tests.csproj", "{CBF9A238-6E47-4BD5-9E21-A597CC3C011C}"
13+
EndProject
14+
Global
15+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
16+
Debug|Any CPU = Debug|Any CPU
17+
Release|Any CPU = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
20+
{836ED2EE-E76B-43E5-AF31-A3697DEABB3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21+
{836ED2EE-E76B-43E5-AF31-A3697DEABB3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
22+
{836ED2EE-E76B-43E5-AF31-A3697DEABB3A}.Release|Any CPU.ActiveCfg = Release|Any CPU
23+
{836ED2EE-E76B-43E5-AF31-A3697DEABB3A}.Release|Any CPU.Build.0 = Release|Any CPU
24+
{CBF9A238-6E47-4BD5-9E21-A597CC3C011C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25+
{CBF9A238-6E47-4BD5-9E21-A597CC3C011C}.Debug|Any CPU.Build.0 = Debug|Any CPU
26+
{CBF9A238-6E47-4BD5-9E21-A597CC3C011C}.Release|Any CPU.ActiveCfg = Release|Any CPU
27+
{CBF9A238-6E47-4BD5-9E21-A597CC3C011C}.Release|Any CPU.Build.0 = Release|Any CPU
28+
EndGlobalSection
29+
GlobalSection(SolutionProperties) = preSolution
30+
HideSolutionNode = FALSE
31+
EndGlobalSection
32+
GlobalSection(NestedProjects) = preSolution
33+
{836ED2EE-E76B-43E5-AF31-A3697DEABB3A} = {163596FF-95DE-462F-83CD-23FD93C83353}
34+
{CBF9A238-6E47-4BD5-9E21-A597CC3C011C} = {5096EE52-D2B3-4173-A90A-5432310F47EC}
35+
EndGlobalSection
36+
GlobalSection(ExtensibilityGlobals) = postSolution
37+
SolutionGuid = {51209A02-797D-416A-8E80-9C15EC4805D3}
38+
EndGlobalSection
39+
EndGlobal
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
public static class RepositoryExtensions
2+
{
3+
public static IRepository<TDbContext> CreateRepository<TDbContext>(this IDbContextFactory<TDbContext> contextFactory)
4+
where TDbContext : DbContext
5+
{
6+
//https://learn.microsoft.com/en-us/ef/core/dbcontext-configuration/#avoiding-dbcontext-threading-issues
7+
var dbContext = contextFactory.CreateDbContext();
8+
return new Repository<TDbContext>(dbContext);
9+
}
10+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
namespace SampleDotnet.RepositoryFactory.Interfaces;
2+
3+
public interface IRepository<TDbContext> : IDisposable
4+
where TDbContext : DbContext
5+
{
6+
DatabaseFacade Database { get; }
7+
8+
IQueryable<T> AsQueryable<T>() where T : class;
9+
10+
void Delete<T>(T entity) where T : class;
11+
12+
void Delete<T>(params T[] entities) where T : class;
13+
14+
void Delete<T>(IEnumerable<T> entities) where T : class;
15+
16+
T? Find<T>(params object[] keyValues) where T : class;
17+
18+
T? FirstOrDefault<T>(Expression<Func<T, bool>> predicate) where T : class;
19+
20+
T? GetById<T>(object id) where T : class;
21+
22+
void Insert<T>(T entity) where T : class;
23+
24+
void Insert<T>(params T[] entities) where T : class;
25+
26+
void Insert<T>(IEnumerable<T> entities) where T : class;
27+
28+
int SaveChanges();
29+
30+
void Update<T>(T entity) where T : class;
31+
32+
void Update<T>(params T[] entities) where T : class;
33+
34+
void Update<T>(IEnumerable<T> entities) where T : class;
35+
36+
IQueryable<T> Where<T>(Expression<Func<T, bool>> predicate) where T : class;
37+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace SampleDotnet.RepositoryFactory.Interfaces;
2+
3+
public interface IRepositoryEntryNotifier
4+
{
5+
void RepositoryEntryEvent(object sender, EntityEntryEventArgs e, DbContext dbContext, IServiceProvider serviceProvider);
6+
}

0 commit comments

Comments
 (0)