通用Web Api控制器,支持任何模型

是否可以使用支持项目中任何模型的通用Web API?

class BaseApiController :ApiController { private IRepository _repository; // inject repository public virtual IEnumerable 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 { //.. } class BarApiController : BaseApiController { //.. } 

这会是一个好方法吗?

毕竟,我只是重复CRUD方法? 我可以使用这个基类为我做这项工作吗?

这个可以吗? 你会这样做吗? 任何更好的想法?

我为一个小项目做了这个,以获得一些东西并运行到客户端的演示。 一旦我了解了业务规则,validation和其他注意事项的细节,我最终不得不从我的基类中覆盖CRUD方法,因此它不会成为长期实现。

我遇到了路由问题,因为并非所有内容都使用相同类型的ID (我正在使用现有系统)。 有些表有int主键,有些表有strings ,有些表有guids

我最终也遇到了问题。 最后,虽然当我第一次使用它时看起来很光滑,但实际上在现实世界中使用它被certificate是另一回事,并没有让我更进一步。

这绝对是可能的。 我以前从来没有理由这样做,但如果它适合你的情况,它应该是好的。

如果你的所有模型都可以用完全相同的方式保存和检索,那么它们应该只是在同一个控制器中而不是?

  public class GenericApiController : BaseApiController where TEntity : class, new() { [HttpGet] [Route("api/{Controller}/{id}")] public IHttpActionResult Get(int id) { try { var entity = db.Set().Find(id); if(entity==null) { return NotFound(); } return Ok(entity); } catch(Exception ex) { return InternalServerError(ex); } } [HttpGet] [Route("api/{Controller}")] public IHttpActionResult Post(TEntity entity) { if (!ModelState.IsValid) { return BadRequest(ModelState); } try { var primaryKeyValue = GetPrimaryKeyValue(entity); var primaryKeyName = GetPrimaryKeyName(entity); var existing = db.Set().Find(primaryKeyValue); ReflectionHelper.Copy(entity, existing, primaryKeyName); db.Entry(existing).State = EntityState.Modified; db.SaveChanges(); return Ok(entity); } catch (Exception ex) { return InternalServerError(ex); } } [HttpGet] [Route("api/{Controller}/{id}")] public IHttpActionResult Put(int id, TEntity entity) { try { if (!ModelState.IsValid) { return BadRequest(ModelState); } var existing = db.Set().Find(id); if (entity == null) { return NotFound(); } ReflectionHelper.Copy(entity, existing); db.SaveChanges(); return Ok(entity); } catch (Exception ex) { return InternalServerError(ex); } } [HttpDelete] [Route("api/{Controller}/{id}")] public IHttpActionResult Delete(int id) { try { var entity = db.Set().Find(id); if(entity==null) { return NotFound(); } db.Set().Remove(entity); db.SaveChanges(); return Ok(); } catch (Exception ex) { return InternalServerError(ex); } } protected internal int GetPrimaryKeyValue(TEntity entity) { return ReflectionHelper.GetPrimaryKeyValue(entity); } protected internal string GetPrimaryKeyName(TEntity entity) { return ReflectionHelper.GetPrimaryKeyName(entity); } protected internal bool Exists(int id) { return db.Set().Find(id) != null; } } 

只要您处理存储库中的所有繁重工作,这没有任何问题。 您可能希望在基本控制器中包装/处理modelstateexception。

我实际上正在为一个大型项目做类似的事情,用户可以在其中定义自己的实体和API – 即:一个用户可能想要拥有用户和帐户,而另一个用户可能想要跟踪汽车和其他任何内容。 它们都使用相同的内部控制器,但它们各自都有自己的端点。

不知道我们的代码对你有多大用处,因为我们不使用generics(每个对象都作为元数据进行维护,并作为JObject字典来回操作/传递)但这里有一些代码可以让你了解我们正在做什么和也许提供思考的食物:

 [POST("{primaryEntity}", RouteName = "PostPrimary")] public async Task CreatePrimary(string primaryEntity, JObject entity) { // first find out which params are necessary to accept the request based on the entity's 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 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); } 

如果您有预定义的设计时类,例如从EF模型或Code First生成的类,那么这对您的系统来说太复杂了。 如果您没有预定义的类(例如在我的项目中,在运行时生成数据实体类),这是很好的。

我的解决方案(尚未正确实现)是创建自定义IHttpControllerSelector,为所有请求选择我的通用控制器,在那里我可以根据请求路径将控制器的描述符类型设置为具体的通用reflection设置通用参数。

另一个很好的起点是http://entityrepository.codeplex.com/ (我在stackoverflow上找到了这个地方)

正如其他人所说,你做的事情肯定是可能的。 但是对于存储库依赖性,您应该使用dependency injection。 我的典型控制器(Api或MVC)如下。

 public class PatientCategoryApiController : ApiController { private IEntityRepository m_Repository; public PatientCategoryApiController(IEntityRepository 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

如前面的答案中所说的那样绝对可能。 这是一种很好的方法,也是一种很好的架构。 但我不明白为什么你的控制器不公开。 可能是你的问题因为它你的代码不起作用?