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