Универсальный контроллер Web Api для поддержки любой модели

Возможно ли иметь общий веб-api, который будет поддерживать любую модель в вашем проекте?

class BaseApiController<T> :ApiController
{
    private IRepository<T> _repository;

    // inject repository

    public virtual IEnumerable<T> GetAll()
    {
       return _repository.GetAll();
    }

    public virtual T Get(int id)
    {
       return _repositry.Get(id);
    }

    public virtual void Post(T item)
    {
       _repository.Save(item);
    }
    // etc...
}

class FooApiController : BaseApiController<Foo>
{
   //..

}

class BarApiController : BaseApiController<Bar>
{
   //..
}

Будет ли это хороший подход?

В конце концов, я просто повторяю методы CRUD? Могу ли я использовать этот базовый класс для выполнения моей работы?

это нормально? вы бы это сделали? любые лучшие идеи?

Ответы

Ответ 1

Я сделал это для небольшого проекта, чтобы получить что-то и запустить демонстрацию для клиента. После того, как я понял специфику бизнес-правил, валидацию и другие соображения, мне пришлось переопределить методы CRUD из моего базового класса, поэтому он не выходил за рамки долгосрочной реализации.

Я столкнулся с проблемами с маршрутизацией, потому что не все использовали ID того же типа (я работал с существующей системой). В некоторых таблицах были int первичные ключи, у некоторых - strings, а у других - guids.

У меня также были проблемы с этим. В конце концов, пока он казался скользким, когда я впервые это сделал, на самом деле его использование в реальной реализации мира оказалось другим делом и не поставило меня дальше дальше.

Ответ 2

Это определенно возможно. У меня никогда не было причин делать это раньше, но если это работает для вашей ситуации, это должно быть хорошо.

Если все ваши модели могут быть сохранены и извлечены точно так же, возможно, они должны просто быть в одном контроллере, но?

Ответ 3

Ничего плохого в этом, пока вы справляетесь со всей тяжелой работой в ваших репозиториях. Вы можете захотеть обернуть/обработать исключения modelstate в вашем базовом контроллере.

Я действительно делаю что-то подобное для большого проекта, где пользователи могут определять свои собственные сущности и API-интерфейсы - то есть: один пользователь может захотеть иметь пользователей и учетные записи, а другой может отслеживать автомобили и что-то еще. Все они используют один и тот же внутренний контроллер, но каждый из них имеет свои конечные точки.

Не уверен, насколько полезен наш код для вас, поскольку мы не используем generics (каждый объект поддерживается как метаданные и управляется/передается взад и вперед как словари JObject), но вот какой-то код, чтобы дать вам представление о том, что мы делают и могут обеспечить пищу для размышлений:

[POST("{primaryEntity}", RouteName = "PostPrimary")]
public async Task<HttpResponseMessage> CreatePrimary(string primaryEntity, JObject entity)
{
   // first find out which params are necessary to accept the request based on the entity mapped metadata type
   OperationalParams paramsForRequest = GetOperationalParams(primaryEntity, DatasetOperationalEntityIntentIntentType.POST);

   // map the passed values to the expected params and the intent that is in use
   IDictionary<string, object> objValues = MapAndValidateProperties(paramsForRequest.EntityModel, paramsForRequest.IntentModel, entity);

   // get the results back from the service and return the data to the client.
   QueryResults results = await paramsForRequest.ClientService.CreatePrimaryEntity(paramsForRequest.EntityModel, objValues, entity, paramsForRequest.IntentModel);
        return HttpResponseMessageFromQueryResults(primaryEntity, results);

}

Ответ 4

Если у вас есть предопределенные классы времени разработки, например, созданные из EF-модели или кода First, это слишком сложно для вашей системы. Это здорово, если у вас нет предопределенных классов (например, в моем проекте, где классы сущностей данных генерируются во время выполнения).

Мое решение (еще не правильно реализовано) заключалось в создании пользовательского IHttpControllerSelector, который выбирает мой общий контроллер для всех запросов, там я могу установить тип дескриптора контроллера на конкретный из общего с помощью основного параметра настройки отражения в зависимости от пути запроса.

Также хорошей отправной точкой является http://entityrepository.codeplex.com/ (я нашел это где-то здесь в stackoverflow)

Ответ 5

То, что вы делаете, безусловно возможно, как говорили другие. Но для зависимостей репозитория вы должны использовать инъекцию зависимостей. Мой типичный контроллер (Api или MVC) будет следующим.

public class PatientCategoryApiController : ApiController
{

    private IEntityRepository<PatientCategory, short> m_Repository;
    public PatientCategoryApiController(IEntityRepository<PatientCategory, short> repository)
    {
        if (repository == null)
            throw new ArgumentNullException("entitiesContext is null");

        m_Repository = repository;
    }
}

Это типичный шаблон инъекции конструктора. Вам необходимо иметь четкое представление о DI и контейнерах, таких как NInject или Autofac. Если вы не знаете DI, то у вас есть долгий путь вперед. Но это отличный подход. Взгляните на эту книгу. https://www.manning.com/books/dependency-injection-in-dot-net

Ответ 6

Это абсолютно возможно, как сказано в предыдущем ответе. Это хороший подход и определенно хорошая архитектура. Но я не понимаю, почему ваши контроллеры не являются публичными. Может быть, это ваша проблема, и из-за этого ваш код не работал?