Можно ли создать операцию GET Generic API? - PullRequest
0 голосов
/ 03 июля 2018

Недавно я создал сервис, который имеет некоторую логику для конечной точки GET в моем API (возвращая все значения некоторой таблицы базы данных).

Причиной создания службы для этого является тот факт, что я хочу изменить логику GET в одном месте, а не менять ее на всех моих конечных точках в будущем.

Я создал тестовый сервис, который работает, но поскольку у меня более 50 таблиц (классы DTO), я хочу сделать сервис более универсальным.

Теперь я реализовал следующее, что является просто примером одной операции GET с одним классом DTO:

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 (и, следовательно, не улучшать его в отличие от изменения его в самих конечных точках).

Есть ли способ сделать это более общим? Я думаю, что часть DTO IEnumerable в интерфейсе и функция GetAll должны быть общего типа (чтобы я мог просто предоставить правильный ViewModel / DTO в самой конечной точке).

Я уже придумал что-то вроде этого:

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

Может ли кто-нибудь указать мне правильное направление?

1 Ответ

0 голосов
/ 03 июля 2018

Да, используя обобщенные значения и метод 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
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...