首页 > 解决方案 > 是否可以创建通用 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; }
}

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

标签: c#asp.net-core

解决方案


是的,使用泛型和 的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
}

推荐阅读