mik*_*ajs 1 c# linq entity-framework-core
我想修改现有的通用存储库以添加可选select功能,例如 Entity Framework Core 中的功能。
期望的结果:
private readonly IUnitOfWork _unit;
// ...
// without using select functionality
IEnumerable<Entity> entities = await _unit.Account.AllAsync();
Entity? x = await _unit.Account.SingleAsync(x => x == id);
// using select functionality
IEnumerable<DTO> y = await _unit.Account.AllAsync(select: x => new DTO
{
Name = x.Name
});
DTO? y = await _unit.Account.SingleAsync(x => x == id, select: x => new DTO
{
Name = x.Name
});
Run Code Online (Sandbox Code Playgroud)
我尝试实现此问题的解决方案Select certain columns in a genericrepository function但参数是必需的,我希望它是可选的。
为简单起见,我只在通用存储库中保留要添加此功能的方法:
在IBaseRepository.cs
public interface IBaseRepository<T> where T : BaseEntity
{
Task<IEnumerable<T>> AllAsync(
Expression<Func<T, bool>>? filter = null,
Func<IQueryable<T>, IOrderedQueryable<T>>? order = null,
Func<IQueryable<T>, IIncludableQueryable<T, object>>? include = null,
int skip = 0,
int take = int.MaxValue,
Track track = Track.NoTracking);
Task<T?> SingleAsync(
Expression<Func<T, bool>> filter, Func<IQueryable<T>,
IIncludableQueryable<T, object>>? include = null,
Track track = Track.Tracking);
}
Run Code Online (Sandbox Code Playgroud)
在BaseRepository.cs
public class BaseRepository<T> : IBaseRepository<T> where T : BaseEntity
{
private readonly DataContext _context;
internal DbSet<T> _set;
public BaseRepository(DataContext context)
{
_context = context;
_set = context.Set<T>();
}
public async Task<IEnumerable<T>> AllAsync(
Expression<Func<T, bool>>? filter = null,
Func<IQueryable<T>, IOrderedQueryable<T>>? order = null,
Func<IQueryable<T>, IIncludableQueryable<T, object>>? include = null,
int skip = 0, int take = int.MaxValue, Track track = Track.NoTracking)
{
IQueryable<T> query = _set;
switch (track)
{
case Track.NoTracking:
query = query.AsNoTracking();
break;
case Track.NoTrackingWithIdentityResolution:
query = query.AsNoTrackingWithIdentityResolution();
break;
default:
query = query.AsTracking();
break;
}
query = skip == 0 ? query.Take(take) : query.Skip(skip).Take(take);
query = filter is null ? query : query.Where(filter);
query = order is null ? query : order(query);
query = include is null ? query : include(query);
return await query.ToListAsync();
}
public async Task<T?> SingleAsync(
Expression<Func<T, bool>> filter,
Func<IQueryable<T>, IIncludableQueryable<T, object>>? include = null,
Track track = Track.Tracking)
{
IQueryable<T> query = _set;
switch (track)
{
case Track.NoTracking:
query = query.AsNoTracking();
break;
case Track.NoTrackingWithIdentityResolution:
query = query.AsNoTrackingWithIdentityResolution();
break;
default:
query = query.AsTracking();
break;
}
query = filter is null ? query : query.Where(filter);
query = include is null ? query : include(query);
return await query.SingleOrDefaultAsync();
}
}
Run Code Online (Sandbox Code Playgroud)
in BaseEntity.cs(每个类 dbtable 都会继承自BaseEntity)
public abstract class BaseEntity
{
public Guid Id { get; set; } = Guid.NewGuid();
public DateTime CreatedAt { get; set; } = DateTime.Now;
public DateTime? UpdatedAt { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
Ste*_* Py 10
诚实地问自己:“为什么我认为我需要通用存储库?”
答:“因为我想在 EF 上添加一个抽象层,这样我就可以在需要时替换它。”
答:“因为我不想让领域知识或 EF 特定知识污染我的业务逻辑。”
答:“因为我希望能够引入一个抽象点,这样我就可以更轻松地编写单元测试。”
。
IQueryable<T> All();
IQueryable<T> Single(int id);
Run Code Online (Sandbox Code Playgroud)
就是这样。您的消费者可以支持过滤、排序、分页、存在检查、计数、投影,甚至可以自行确定操作的开销是否async是必要的。模拟起来非常简单,只需增加一点复杂性即可支持传回数据async使用。存储库方法可以强加基本级别的过滤,例如IsActive软删除系统,或检查当前用户的授权并将其纳入返回的结果中。
当您想要引入一个解决方案来满足“DNRY”(不要重复自己)或在您的应用程序中强制执行一定程度的一致性时,或者在相同的情况下(不仅仅是相似)的关注点(例如 Web API 服务和 Web 应用程序)之间,可以通过移交给一个公共服务来完成,该服务使用存储库以一致的方式获取和打包数据以返回 DTO。否则,像对待 MVC 控制器一样对待存储库,将其范围限定为对一个使用者负责,并且只有一个理由进行更改。
反对退货的典型论点IQueryable是,这是一个薄弱或有漏洞的抽象,并且给了消费者一把上膛的猎枪。这是 100% 真实的,它是一个弱抽象,这使得它很容易被模拟。如果您不需要模拟它进行单元测试,那么您只是“不需要它”。就为开发人员提供一个装载武器而言,恕我直言,对于一个项目来说,为实施开发人员提供他们需要的工具,使他们能够针对领域编写有效的表达式以及如何正确执行和纠正的培训/知识要好得多。如果发现错误;而不是尝试将技术抽象出来以“简化”,或者从本质上讲,最终会引入同样多的复杂性和制造混乱的能力,只是在界面上使用您的名字而不是 EF。
| 归档时间: |
|
| 查看次数: |
2330 次 |
| 最近记录: |