Skip to content

Commit ec79f5e

Browse files
authored
Merge pull request #1 from msx752/development
Development
2 parents f4f7851 + 7e2459f commit ec79f5e

File tree

13 files changed

+174
-168
lines changed

13 files changed

+174
-168
lines changed
Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
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
1+
name: Dotnet
2+
43
on:
54
push:
6-
branches: [ "main" ]
7-
pull_request:
8-
branches: [ "main" ]
5+
pull_request: [ main ]
6+
97
jobs:
108
build:
11-
9+
1210
runs-on: ubuntu-latest
1311
strategy:
1412
matrix:
@@ -17,16 +15,23 @@ jobs:
1715
actions: read
1816
contents: read
1917
security-events: write
20-
18+
2119
steps:
2220
- uses: actions/checkout@v3
2321
- name: Setup .NET Core SDK ${{ matrix.dotnet-version }}
2422
uses: actions/setup-dotnet@v3
2523
with:
2624
dotnet-version: ${{ matrix.dotnet-version }}
27-
28-
- name: Install dependencies
25+
- name: Restore dependencies
2926
run: dotnet restore
30-
27+
- name: Build
28+
run: dotnet build -c Release
3129
- name: Test
32-
run: dotnet test test/SampleDotnet.RepositoryFactory.Tests/SampleDotnet.RepositoryFactory.Tests.csproj --no-restore --verbosity normal
30+
run: dotnet test test/SampleDotnet.RepositoryFactory.Tests/SampleDotnet.RepositoryFactory.Tests.csproj --no-build --verbosity normal
31+
32+
- name: publish
33+
id: SampleDotnet_RepositoryFactory
34+
uses: alirezanet/publish-nuget@v3.0.3
35+
with:
36+
PROJECT_FILE_PATH: src/SampleDotnet.RepositoryFactory/SampleDotnet.RepositoryFactory.csproj
37+
NUGET_KEY: ${{secrets.NUGET_KEY}}

README.md

Lines changed: 7 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -47,68 +47,16 @@ then we call transient scoped DbContext
4747
```
4848

4949
# 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-
50+
- If `IHasDateTimeOffset` interfece used on Entity object then value of the the CreatedAt and UpdatedAt properties will be updated automatically.
5651
``` 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)
52+
public class TestUserEntity : IHasDateTimeOffset
8253
{
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;
54+
public Guid Id { get; set; }
55+
public string Name { get; set; }
56+
public string Surname { get; set; }
9357

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-
}
58+
public DateTimeOffset? CreatedAt { get; set; }
59+
public DateTimeOffset? UpdatedAt { get; set; }
10460
}
105-
}
106-
}
107-
```
108-
109-
- ServiceCollection Definition (Singleton scope)
110-
``` c#
111-
services.AddSingleton<IRepositoryEntryNotifier, MyRpositoryNotifier>();
11261
```
11362

114-
- after that call `var repository = _contextFactory.CreateRepository();` and add or delete entity
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace SampleDotnet.RepositoryFactory.Interfaces;
2+
3+
public interface IHasDateTimeOffset
4+
{
5+
DateTimeOffset? CreatedAt { get; set; }
6+
DateTimeOffset? UpdatedAt { get; set; }
7+
}

src/SampleDotnet.RepositoryFactory/Interfaces/IRepositoryEntryNotifier.cs

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/SampleDotnet.RepositoryFactory/Repository.cs

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ public class Repository<TDbContext>
1212
public Repository(TDbContext dbContext)
1313
{
1414
_context = dbContext;
15-
RepositoryEntryEventHandler(_context);
1615
}
1716

1817
public IQueryable<T> AsQueryable<T>() where T : class
@@ -62,17 +61,32 @@ public void Dispose()
6261

6362
public void Insert<T>(T entity) where T : class
6463
{
64+
if (entity is IHasDateTimeOffset dt)
65+
dt.CreatedAt = DateTimeOffset.Now;
66+
6567
_context.Set<T>().Add(entity);
6668
}
6769

6870
public void Insert<T>(params T[] entities) where T : class
6971
{
70-
_context.Set<T>().AddRange(entities);
72+
_context.Set<T>().AddRange(entities.Select(f =>
73+
{
74+
if (f is IHasDateTimeOffset dt)
75+
dt.CreatedAt = DateTimeOffset.Now;
76+
77+
return f;
78+
}));
7179
}
7280

7381
public void Insert<T>(IEnumerable<T> entities) where T : class
7482
{
75-
_context.Set<T>().AddRange(entities);
83+
_context.Set<T>().AddRange(entities.Select(f =>
84+
{
85+
if (f is IHasDateTimeOffset dt)
86+
dt.CreatedAt = DateTimeOffset.Now;
87+
88+
return f;
89+
}));
7690
}
7791

7892
public int SaveChanges()
@@ -85,6 +99,9 @@ public void Update<T>(T entity) where T : class
8599
{
86100
var entry = _context.Entry(entity);
87101
entry.State = EntityState.Modified;
102+
103+
if (entry.Entity is IHasDateTimeOffset dt)
104+
dt.UpdatedAt = DateTimeOffset.Now;
88105
}
89106

90107
public void Update<T>(params T[] entities) where T : class
@@ -110,33 +127,10 @@ protected virtual void Dispose(bool disposing)
110127
{
111128
if (disposing)
112129
{
113-
RepositoryEntryEventHandler(_context, true);
114130
_context.Dispose();
115131
}
116132

117133
disposedValue = true;
118134
}
119135
}
120-
121-
private static void RepositoryEntryEventHandler(DbContext _context, bool disposing = false)
122-
{
123-
try
124-
{
125-
var serviceProvider = _context.GetInfrastructure();
126-
var entryEventNotifier = (IRepositoryEntryNotifier?)serviceProvider?.GetService(typeof(IRepositoryEntryNotifier));
127-
if (serviceProvider != null && entryEventNotifier != null)
128-
{
129-
_context.ChangeTracker.Tracked -= (sender, e) => entryEventNotifier.RepositoryEntryEvent(sender, e, _context, serviceProvider);
130-
if (!disposing)
131-
_context.ChangeTracker.Tracked += (sender, e) => entryEventNotifier.RepositoryEntryEvent(sender, e, _context, serviceProvider);
132-
133-
_context.ChangeTracker.StateChanged -= (sender, e) => entryEventNotifier.RepositoryEntryEvent(sender, e, _context, serviceProvider);
134-
if (!disposing)
135-
_context.ChangeTracker.StateChanged += (sender, e) => entryEventNotifier.RepositoryEntryEvent(sender, e, _context, serviceProvider);
136-
}
137-
}
138-
catch (Exception e)
139-
{
140-
}
141-
}
142136
}

src/SampleDotnet.RepositoryFactory/SampleDotnet.RepositoryFactory.csproj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
1212
<id>SampleDotnet.RepositoryFactory</id>
1313
<Title>SampleDotnet.RepositoryFactory</Title>
14-
<Description>EFCore DbContext RepositoryFactory Pattern</Description>
14+
<Description>EFCore DbContextFactory Pattern used on RepositoryFactory</Description>
1515
<Summary></Summary>
1616
<Authors>Mustafa Salih ASLIM;</Authors>
1717
<PackageProjectUrl>https://github.com/msx752/SampleDotnet.RepositoryFactory</PackageProjectUrl>
@@ -30,6 +30,9 @@
3030
<PublishRepositoryUrl>True</PublishRepositoryUrl>
3131
<ContinuousIntegrationBuild>True</ContinuousIntegrationBuild>
3232
<EmbedUntrackedSources>True</EmbedUntrackedSources>
33+
<Copyright>Copyright 2023</Copyright>
34+
<AssemblyVersion>1.1.0</AssemblyVersion>
35+
<Version>1.1.0</Version>
3336
</PropertyGroup>
3437

3538
<ItemGroup>

src/SampleDotnet.RepositoryFactory/_GlobalUsings.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
global using Microsoft.EntityFrameworkCore;
2-
global using Microsoft.EntityFrameworkCore.ChangeTracking;
32
global using Microsoft.EntityFrameworkCore.Infrastructure;
43
global using SampleDotnet.RepositoryFactory;
54
global using SampleDotnet.RepositoryFactory.Interfaces;
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
namespace SampleDotnet.RepositoryFactory.Tests.Cases;
2+
3+
public class DateTimeOffsetTests
4+
{
5+
public DateTimeOffsetTests()
6+
{
7+
}
8+
9+
[Fact]
10+
public void Case_set_CreatedAt_DateTimeOffset()
11+
{
12+
IHostBuilder host = Host.CreateDefaultBuilder().ConfigureServices((services) =>
13+
{
14+
services.AddDbContextFactory<TestApplicationDbContext>(options =>
15+
options.UseInMemoryDatabase("Case_set_CreatedAt_DateTimeOffset"));
16+
});
17+
18+
IHost b = host.Build();
19+
20+
//scope1
21+
using (IServiceScope scope = b.Services.CreateScope())
22+
{
23+
IDbContextFactory<TestApplicationDbContext> dbcontext = scope.ServiceProvider.GetRequiredService<IDbContextFactory<TestApplicationDbContext>>();
24+
using (IRepository<TestApplicationDbContext> repo = dbcontext.CreateRepository())
25+
{
26+
TestUserEntity userEntity = new TestUserEntity();
27+
userEntity.Name = "TestName";
28+
userEntity.Surname = "TestSurname";
29+
30+
repo.Insert(userEntity);
31+
repo.SaveChanges();
32+
33+
userEntity.CreatedAt.ShouldNotBeNull();
34+
}
35+
}
36+
}
37+
38+
[Fact]
39+
public void Case_set_UpdatedAt_DateTimeOffset()
40+
{
41+
IHostBuilder host = Host.CreateDefaultBuilder().ConfigureServices((services) =>
42+
{
43+
services.AddDbContextFactory<TestApplicationDbContext>(options =>
44+
options.UseInMemoryDatabase("Case_set_UpdatedAt_DateTimeOffset"));
45+
});
46+
47+
IHost b = host.Build();
48+
49+
//scope1
50+
using (IServiceScope scope = b.Services.CreateScope())
51+
{
52+
IDbContextFactory<TestApplicationDbContext> dbcontext = scope.ServiceProvider.GetRequiredService<IDbContextFactory<TestApplicationDbContext>>();
53+
using (IRepository<TestApplicationDbContext> repo = dbcontext.CreateRepository())
54+
{
55+
TestUserEntity userEntity = new TestUserEntity();
56+
userEntity.Name = "TestName";
57+
userEntity.Surname = "TestSurname";
58+
59+
userEntity.CreatedAt.ShouldBeNull();
60+
userEntity.UpdatedAt.ShouldBeNull();
61+
62+
repo.Insert(userEntity);
63+
repo.SaveChanges();
64+
65+
userEntity.CreatedAt.ShouldNotBeNull();
66+
userEntity.UpdatedAt.ShouldBeNull();
67+
}
68+
69+
//scope2
70+
using (IRepository<TestApplicationDbContext> repo = dbcontext.CreateRepository())
71+
{
72+
TestUserEntity? userEntity = repo.FirstOrDefault<TestUserEntity>(f => f.Name == "TestName" && f.Surname == "TestSurname");
73+
74+
userEntity.ShouldNotBeNull();
75+
userEntity.UpdatedAt.ShouldBeNull();
76+
77+
repo.Update(userEntity);
78+
repo.SaveChanges();
79+
80+
userEntity.CreatedAt.ShouldNotBeNull();
81+
userEntity.UpdatedAt.ShouldNotBeNull();
82+
}
83+
}
84+
}
85+
}

test/SampleDotnet.RepositoryFactory.Tests/Class1.cs

Lines changed: 0 additions & 51 deletions
This file was deleted.

0 commit comments

Comments
 (0)