简体   繁体   English

是否可以创建Generic API GET操作?

[英]Is it possible to create a Generic API GET operation?

Recently I created a service which has some logic for a GET endpoint in my API (returning all values of some database table). 最近我创建了一个服务,它在我的API中有一些GET端点的逻辑(返回一些数据库表的所有值)。

The reason for creating a service for this is the fact that I want to modify GET logic at one point instead of having to change it on all my endpoints in the future. 为此创建服务的原因是我想在一个点上修改GET逻辑,而不是在将来必须在我的所有端点上更改它。

I created a test service which works, but because I have over 50 tables (DTO classes) I want to make the service more generic. 我创建了一个有效的测试服务,但因为我有超过50个表(DTO类),我想让服务更通用。

I now have implemented the following, which is just an example of one GET operation with one DTO class : 我现在已经实现了以下内容,这只是一个带有一个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;

    }
}

The above example works, however, this would mean I need to implement over 50 interface properties and 50 GetAll implementations for every DTO class (and thus not an improvement as opposed to changing it in the endpoints itself). 上面的例子GetAll ,但这意味着我需要为每个DTO类实现50多个接口属性和50个GetAll实现(因此不是改进而不是在端点本身中更改它)。

Is there a way to make this more generic? 有没有办法让这更通用? I think the DTO part of the IEnumerable in the interface and GetAll function should be of a Generic type (so that I can just supply the right ViewModel / DTO at the endpoint itself). 我认为接口和GetAll函数中IEnumerable的DTO部分应该是Generic类型(这样我才能在端点本身提供正确的ViewModel / DTO)。

I already came up with something like this: 我已经想到了这样的事情:

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

Could someone point me in the right direction? 有人能指出我正确的方向吗?

Yes, using generics and the Set<T>() method of DbContext , you can do something like this: 是的,使用泛型和DbContextSet<T>()方法,你可以这样做:

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

And call it like this: 并称之为:

var allTheThings = GetAll<Commodity, CommodityViewModel>(i => i.OmsCommodityMaterial);

However, to return all your rows is almost certainly a bad idea, so why not add in a filter while we are here: 但是,要返回所有行几乎肯定是一个坏主意,所以为什么不在我们这里时添加一个过滤器:

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

Now we call it like this: 现在我们称之为:

var someOfTheThings = Get<Commodity, CommodityViewModel>(
    x => x.SomeProperty == 42,
    i => i.OmsCommodityMaterial);

If you want to extract an interface from this method, I'd probably make the interface generic: 如果你想从这个方法中提取一个接口,我可能会使接口通用:

public interface IOMSService<TEntity>
{
    IEnumerable<TModel> Get<TModel>(
        Expression<Func<TEntity, bool>> predicate, 
        params Expression<Func<TEntity, object>>[] includes) 
}

And then a base class: 然后是一个基类:

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

And now you can make specific derived classes: 现在您可以创建特定的派生类:

public class CheeseOMSService : BaseOMSService<Cheese>
{
    // snip
}

public class ZombieOMSService : BaseOMSService<Zombie>
{
    // snip
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM