代码之家  ›  专栏  ›  技术社区  ›  Nicolas

是否可以创建泛型api get操作?

  •  4
  • Nicolas  · 技术社区  · 6 年前

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

    不过,上面的示例可以工作,这意味着我需要实现50多个接口属性和50多个 GetAll 每个DTO类的实现(因此不是相对于在端点本身中更改它的改进)。

    有没有办法让它更通用? 我认为 IEnumerable 在界面和 获取 函数应该是泛型类型(这样我就可以在端点本身提供正确的viewmodel/dto)。

    我已经想出了这样的办法:

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

    有人能告诉我正确的方向吗?

    1 回复  |  直到 6 年前
        1
  •  3
  •   DavidG    6 年前

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