22using Microsoft . EntityFrameworkCore ;
33using TinyRepository . Entities ;
44using TinyRepository . Interfaces ;
5+ using TinyRepository . Paging ;
56
67namespace TinyRepository ;
78
89public class EfRepository < T , TKey > : IRepository < T , TKey >
9- where T : class , IEntity < TKey >
10- where TKey : IEquatable < TKey >
10+ where T : class , IEntity < TKey >
11+ where TKey : IEquatable < TKey >
1112{
1213 protected readonly DbContext _context ;
1314 protected readonly DbSet < T > _dbSet ;
@@ -20,6 +21,7 @@ public EfRepository(DbContext context)
2021
2122 public virtual async Task < T ? > GetByIdAsync ( TKey id , CancellationToken cancellationToken = default )
2223 {
24+ // FindAsync returns tracked entity by default
2325 return await _dbSet . FindAsync ( [ id ] , cancellationToken ) ;
2426 }
2527
@@ -48,11 +50,26 @@ public virtual void Update(T entity)
4850 _dbSet . Update ( entity ) ;
4951 }
5052
53+ public virtual Task UpdateRangeAsync ( IEnumerable < T > entities , CancellationToken cancellationToken = default )
54+ {
55+ // EF Core does not have an async UpdateRange; updating entity states is in-memory and quick.
56+ _dbSet . UpdateRange ( entities ) ;
57+
58+ return Task . CompletedTask ;
59+ }
60+
5161 public virtual void Remove ( T entity )
5262 {
5363 _dbSet . Remove ( entity ) ;
5464 }
5565
66+ public virtual Task RemoveRangeAsync ( IEnumerable < T > entities , CancellationToken cancellationToken = default )
67+ {
68+ // Removing ranges is in-memory (marking entities Deleted). Return completed task for async signature.
69+ _dbSet . RemoveRange ( entities ) ;
70+ return Task . CompletedTask ;
71+ }
72+
5673 public virtual async Task RemoveByIdAsync ( TKey id , CancellationToken cancellationToken = default )
5774 {
5875 var entity = await GetByIdAsync ( id , cancellationToken ) ;
@@ -63,15 +80,75 @@ public virtual async Task RemoveByIdAsync(TKey id, CancellationToken cancellatio
6380 }
6481 }
6582
66- public virtual async Task < bool > ExistsAsync ( TKey id , CancellationToken cancellationToken = default )
83+ public virtual async Task < bool > PatchAsync ( TKey id , Action < T > patchAction , CancellationToken cancellationToken = default )
6784 {
68- var entity = await GetByIdAsync ( id , cancellationToken ) ;
85+ if ( patchAction == null )
86+ {
87+ throw new ArgumentNullException ( nameof ( patchAction ) ) ;
88+ }
6989
70- return entity is not null ;
90+ // Use FindAsync to get a tracked entity (so changes are tracked)
91+ var entity = await _dbSet . FindAsync ( new object [ ] { id } , cancellationToken ) ;
92+
93+ if ( entity is null )
94+ {
95+ return false ;
96+ }
97+
98+ patchAction ( entity ) ;
99+
100+ // Mark modified to ensure changes are persisted even if change tracking misses something
101+ _context . Entry ( entity ) . State = EntityState . Modified ;
102+ return true ;
103+ }
104+
105+ public virtual IQueryable < T > Query ( bool asNoTracking = true )
106+ {
107+ return asNoTracking ? _dbSet . AsNoTracking ( ) : _dbSet ;
108+ }
109+
110+ public virtual IQueryable < T > Query ( Expression < Func < T , bool > > predicate , bool asNoTracking = true )
111+ {
112+ var q = asNoTracking ? _dbSet . AsNoTracking ( ) : _dbSet ;
113+
114+ return q . Where ( predicate ) ;
115+ }
116+
117+ public virtual async Task < PagedResult < T > > GetPagedAsync ( int pageNumber , int pageSize , Func < IQueryable < T > , IOrderedQueryable < T > > ? orderBy = null ,
118+ Expression < Func < T , bool > > ? filter = null , bool asNoTracking = true , CancellationToken cancellationToken = default )
119+ {
120+ ArgumentOutOfRangeException . ThrowIfNegativeOrZero ( pageNumber ) ;
121+ ArgumentOutOfRangeException . ThrowIfNegativeOrZero ( pageSize ) ;
122+
123+ var query = asNoTracking ? _dbSet . AsNoTracking ( ) . AsQueryable ( ) : _dbSet . AsQueryable ( ) ;
124+
125+ if ( filter is not null )
126+ {
127+ query = query . Where ( filter ) ;
128+ }
129+
130+ var totalCount = await query . CountAsync ( cancellationToken ) ;
131+
132+ if ( orderBy is not null )
133+ {
134+ query = orderBy ( query ) ;
135+ }
136+
137+ var skip = ( pageNumber - 1 ) * pageSize ;
138+ var items = await query . Skip ( skip ) . Take ( pageSize ) . ToListAsync ( cancellationToken ) ;
139+
140+ return new PagedResult < T > ( items , totalCount , pageNumber , pageSize ) ;
71141 }
72142
73143 public virtual async Task < int > CountAsync ( CancellationToken cancellationToken = default )
74144 {
75145 return await _dbSet . CountAsync ( cancellationToken ) ;
76146 }
147+
148+ public virtual async Task < bool > ExistsAsync ( TKey id , CancellationToken cancellationToken = default )
149+ {
150+ var entity = await GetByIdAsync ( id , cancellationToken ) ;
151+
152+ return entity is not null ;
153+ }
77154}
0 commit comments