QueryRaw and QueryInterpolated now always return results with AsNoTracking applied, ensuring entities are not tracked by EF Core. Both methods are now virtual, allowing for easier customization in derived classes. This change improves performance and prevents unintended side effects from entity tracking.
162 lines
6.0 KiB
C#
162 lines
6.0 KiB
C#
using AutoMapper;
|
|
using DigitalData.Core.Abstraction.Application.Repository;
|
|
using DigitalData.Core.Infrastructure.Factory;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using System.Linq.Expressions;
|
|
using Microsoft.EntityFrameworkCore.Query;
|
|
#if NETFRAMEWORK
|
|
using System.Collections.Generic;
|
|
using System;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
#endif
|
|
|
|
namespace DigitalData.Core.Infrastructure
|
|
{
|
|
public class DbRepository<TDbContext> : IRepository where TDbContext : DbContext
|
|
{
|
|
protected internal readonly TDbContext Context;
|
|
|
|
public DbRepository(TDbContext context)
|
|
{
|
|
Context = context;
|
|
}
|
|
|
|
public Task<int> ExecuteQueryRawAsync([NotParameterized] string sql, IEnumerable<object> parameters, CancellationToken cancel = default)
|
|
{
|
|
return Context.Database.ExecuteSqlRawAsync(sql, parameters, cancel);
|
|
}
|
|
|
|
public Task<int> ExecuteQueryInterpolatedAsync(FormattableString sql, CancellationToken cancel = default)
|
|
{
|
|
return Context.Database.ExecuteSqlInterpolatedAsync(sql, cancel);
|
|
}
|
|
|
|
public int ExecuteQueryRaw([NotParameterized] string sql, params object[] parameters)
|
|
{
|
|
return Context.Database.ExecuteSqlRaw(sql, parameters);
|
|
}
|
|
|
|
public int ExecuteQueryInterpolated(FormattableString sql)
|
|
{
|
|
return Context.Database.ExecuteSqlInterpolated(sql);
|
|
}
|
|
}
|
|
|
|
public class DbRepository<TDbContext, TEntity> : IRepository<TEntity> where TDbContext : DbContext where TEntity : class
|
|
{
|
|
protected internal readonly TDbContext Context;
|
|
|
|
protected internal readonly DbSet<TEntity> Entities;
|
|
|
|
public IMapper
|
|
#if NET
|
|
?
|
|
#endif
|
|
Mapper { get; }
|
|
|
|
public DbRepository(TDbContext context, DbSetFactory<TDbContext, TEntity> factory, IMapper
|
|
#if NET
|
|
?
|
|
#endif
|
|
mapper = null)
|
|
{
|
|
Context = context;
|
|
Entities = factory.Create(context);
|
|
Mapper = mapper;
|
|
}
|
|
|
|
#region Create
|
|
public virtual async Task<TEntity> CreateAsync(TEntity entity, CancellationToken cancel = default)
|
|
{
|
|
Entities.Add(entity);
|
|
await Context.SaveChangesAsync(cancel);
|
|
return entity;
|
|
}
|
|
|
|
public virtual async Task<IEnumerable<TEntity>> CreateAsync(IEnumerable<TEntity> entities, CancellationToken cancel = default)
|
|
{
|
|
Entities.AddRange(entities);
|
|
await Context.SaveChangesAsync(cancel);
|
|
return entities;
|
|
}
|
|
|
|
public virtual Task<TEntity> CreateAsync<TDto>(TDto dto, CancellationToken cancel = default)
|
|
=> CreateAsync(Mapper
|
|
#if NET
|
|
!
|
|
#endif
|
|
.Map<TEntity>(dto), cancel);
|
|
|
|
public virtual Task<IEnumerable<TEntity>> CreateAsync<TDto>(IEnumerable<TDto> dtos, CancellationToken cancel = default)
|
|
=> CreateAsync(Mapper
|
|
#if NET
|
|
!
|
|
#endif
|
|
.Map<IEnumerable<TEntity>>(dtos), cancel);
|
|
#endregion Create
|
|
|
|
#region Read
|
|
public virtual IQueryable<TEntity> Query => Entities.AsNoTracking();
|
|
|
|
public virtual IQueryable<TEntity> QueryRaw([NotParameterized] string sql, params object[] parameters) => Entities.FromSqlRaw(sql, parameters).AsNoTracking();
|
|
|
|
public virtual IQueryable<TEntity> QueryInterpolated([NotParameterized] FormattableString sql) => Entities.FromSqlInterpolated(sql).AsNoTracking();
|
|
|
|
public virtual IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> expression) => Entities.AsNoTracking().Where(expression);
|
|
|
|
public virtual IEnumerable<TEntity> GetAll() => Entities.AsNoTracking().ToList();
|
|
|
|
public virtual async Task<IEnumerable<TEntity>> GetAllAsync(CancellationToken cancel = default) => await Entities.AsNoTracking().ToListAsync(cancel);
|
|
#endregion Read
|
|
|
|
#region Update
|
|
public virtual Task UpdateAsync<TDto>(TDto dto, Expression<Func<TEntity, bool>> expression, CancellationToken cancel = default) => UpdateAsync(dto, q => q.Where(expression), cancel);
|
|
|
|
public virtual Task UpdateAsync<TDto>(TDto dto, Func<IQueryable<TEntity>, IQueryable<TEntity>> query, CancellationToken cancel = default)
|
|
=> UpdateAsync(entity => Mapper
|
|
#if NET
|
|
!
|
|
#endif
|
|
.Map(dto, entity), query, cancel);
|
|
|
|
public virtual async Task UpdateAsync(Action<TEntity> modification, Func<IQueryable<TEntity>, IQueryable<TEntity>> query, CancellationToken cancel = default)
|
|
{
|
|
var entities = await query(Entities).ToListAsync(cancel);
|
|
|
|
for (int i = entities.Count - 1; i >= 0; i--)
|
|
modification.Invoke(entities[i]);
|
|
|
|
await Context.SaveChangesAsync(cancel);
|
|
}
|
|
|
|
public virtual Task UpdateAsync(Action<TEntity> modification, Expression<Func<TEntity, bool>> expression, CancellationToken cancel = default)
|
|
=> UpdateAsync(modification, q => q.Where(expression), cancel);
|
|
#endregion Update
|
|
|
|
#region Delete
|
|
public virtual Task DeleteAsync(Expression<Func<TEntity, bool>> expression, CancellationToken cancel = default) => DeleteAsync(q => q.Where(expression), cancel);
|
|
|
|
public virtual async Task DeleteAsync(Func<IQueryable<TEntity>, IQueryable<TEntity>> query, CancellationToken cancel = default)
|
|
{
|
|
var entities = await query(Entities).ToListAsync(cancel);
|
|
|
|
for (int i = entities.Count - 1; i >= 0; i--)
|
|
{
|
|
Entities.Remove(entities[i]);
|
|
}
|
|
|
|
await Context.SaveChangesAsync(cancel);
|
|
}
|
|
#endregion Delete
|
|
|
|
#region Obsolete
|
|
[Obsolete("Use IRepository<TEntity>.Where")]
|
|
public virtual IQueryable<TEntity> Read() => Entities.AsQueryable();
|
|
|
|
[Obsolete("Use IRepository<TEntity>.Get")]
|
|
public virtual IQueryable<TEntity> ReadOnly() => Entities.AsNoTracking();
|
|
#endregion
|
|
}
|
|
} |