是否可以创建通用API GET操作?

时间:2018-07-03 15:16:40

标签: c# asp.net-core

最近,我创建了一个服务,该服务在我的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;

    }
}

上面的示例有效,但是,这意味着我需要为每个DTO类实现50多个接口属性和50个GetAll实现(因此,与在端点本身中进行更改相比,这不是一种改进)。

有没有办法使它更通用? 我认为接口IEnumerable中的GetAll的DTO部分应该是Generic类型的(这样我就可以在端点本身上提供正确的ViewModel / DTO)。

我已经想出了这样的东西:

public interface IOMSService<T, U>
        where T : IEnumerable<U>
{
    T GetAll { get; }
}

有人可以指出我正确的方向吗?

1 个答案:

答案 0 :(得分:3)

是的,使用泛型和Set<T>()的{​​{1}}方法,您可以执行以下操作:

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);
}

但是,要返回 all 行几乎是一个坏主意,所以为什么不在我们这里时添加过滤器:

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);
    }
}