c# - 是否可以创建通用 API GET 操作?
问题描述
最近我创建了一个服务,它在我的 API 中为 GET 端点提供了一些逻辑(返回某个数据库表的所有值)。
为此创建服务的原因是我想在某一时刻修改 GET 逻辑,而不是将来在我的所有端点上更改它。
我创建了一个可以工作的测试服务,但是因为我有超过 50 个表(DTO 类),所以我想让服务更通用。
我现在已经实现了以下内容,这只是一个带有一个 DTO 类的 GET 操作的示例:
public interface IOMSService
{
IEnumerable<CommodityViewModel> GetAll(); // Can I use <T> for this? - now I need to make interface properties for every OMS class (50+)
}
public class OMSService : IOMSService
{
private MyDBContext _context;
private IMapper _mapper;
public OMSService(MyDBContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}
public IEnumerable<CommodityViewModel> GetAll() // How to make this more generic?
{
var result = this._context.Commodity
.Include(i => i.OmsCommodityMaterial);
var CommodityVM = _mapper.Map<IList<CommodityViewModel>>(result);
return CommodityVM;
}
}
上面的示例有效,但是,这意味着我需要GetAll
为每个 DTO 类实现超过 50 个接口属性和 50 个实现(因此与在端点本身中更改它相比,这并不是一种改进)。
有没有办法让这个更通用?IEnumerable
我认为接口和函数中的 DTO 部分GetAll
应该是 Generic 类型(这样我就可以在端点本身提供正确的 ViewModel / DTO)。
我已经想出了这样的事情:
public interface IOMSService<T, U>
where T : IEnumerable<U>
{
T GetAll { get; }
}
有人能指出我正确的方向吗?
解决方案
是的,使用泛型和 的Set<T>()
方法DbContext
,您可以执行以下操作:
//Note we need to make the entity and the model it maps to generic
public IEnumerable<TModel> GetAll<TEntity, TModel>(
params Expression<Func<TEntity, object>>[] includes)
where TEntity : class
{
var result = _context.Set<TEntity>().AsQueryable();
if(includes != null)
{
foreach (var include in includes)
{
result = result.Include(include);
}
}
return _mapper.Map<IList<TModel>>(result);
}
并这样称呼它:
var allTheThings = GetAll<Commodity, CommodityViewModel>(i => i.OmsCommodityMaterial);
但是,返回所有行几乎可以肯定是个坏主意,所以为什么不在我们这里添加过滤器:
public IEnumerable<TModel> Get<TEntity, TModel>(
Expression<Func<TEntity, bool>> predicate,
params Expression<Func<TEntity, object>>[] includes)
where TEntity : class
{
var result = _context.Set<TEntity>()
.Where(predicate);
if(includes != null)
{
foreach (var include in includes)
{
result = result.Include(include);
}
}
return _mapper.Map<IList<TModel>>(result);
}
现在我们这样称呼它:
var someOfTheThings = Get<Commodity, CommodityViewModel>(
x => x.SomeProperty == 42,
i => i.OmsCommodityMaterial);
如果您想从此方法中提取接口,我可能会将该接口设为通用:
public interface IOMSService<TEntity>
{
IEnumerable<TModel> Get<TModel>(
Expression<Func<TEntity, bool>> predicate,
params Expression<Func<TEntity, object>>[] includes)
}
然后是一个基类:
public abstract class BaseOMSService<TEntity> : IOMSService<TEntity>
where TEntity : class
{
private MyDBContext _context;
private IMapper _mapper;
public BaseOMSService(MyDBContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}
public IEnumerable<TModel> Get<TModel>(
Expression<Func<TEntity, bool>> predicate,
params Expression<Func<TEntity, object>>[] includes)
{
var result = _context.Set<TEntity>()
.Where(predicate);
if(includes != null)
{
foreach (var include in includes)
{
result = result.Include(include);
}
}
return _mapper.Map<IList<TModel>>(result);
}
}
现在您可以创建特定的派生类:
public class CheeseOMSService : BaseOMSService<Cheese>
{
// snip
}
public class ZombieOMSService : BaseOMSService<Zombie>
{
// snip
}
推荐阅读
- angular - Angular 7.2.5 将订阅分配给 EventEmitter 失败错误 TS2740
- python - 如何从字符串中的特定单词开始提取文本?
- python - 我可以使用字典本身中的 for 循环直接在字典中定义一个值吗?
- c# - EmguCV C# - 拆分后合并 hsv 通道
- ios - 当应用程序在后台时,iOS 中的 Ionic3 无法将坐标流式传输到我的 API
- java - 十进制值以科学计数法显示 - HBase (Phoenix)
- c++ - for_each 算法导致 basic_string::_M_construct null 在 C++ 中无效
- scala - Scala 数字类型的隐式转换是否特殊?
- bash - 在从“find | xargs”运行的命令中使用参数扩展来防止输出覆盖
- php - 内网 PHP 页面上的文件 URL “不允许加载本地资源”