23

プロジェクト内の任意のモデルをサポートする汎用WebAPIを使用することは可能ですか?

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メソッドを繰り返すだけですか?この基本クラスを使用して作業を行うことはできますか?

これでいい?あなたはこれをしますか?より良いアイデアはありますか?

4

6 に答える 6

27

私は、クライアントにデモを行うために何かを立ち上げて実行するための小さなプロジェクトのためにこれを行いました。ビジネスルール、検証、その他の考慮事項の詳細に入ると、基本クラスのCRUDメソッドをオーバーライドする必要があり、長期的な実装として機能しなくなりました。

すべてが同じタイプのIDを使用しているわけではないため、ルーティングで問題が発生しました(既存のシステムで作業していました)。一部のテーブルにはint主キーがあり、一部にはstringsあり、その他にはありましたguids

私もそれで問題を抱えることになりました。結局、私が最初にそれをしたときは滑らかに見えましたが、実際にそれを実際の実装で使用することは別の問題であることがわかり、私はそれ以上先に進みませんでした。

于 2012-08-22T17:44:27.003 に答える
5

それは間違いなく可能です。私はこれまでそうする理由がありませんでしたが、それがあなたの状況でうまくいくなら、それは良いはずです。

すべてのモデルをまったく同じ方法で保存および取得できる場合は、代わりにすべてを同じコントローラーに配置する必要がありますか?

于 2012-08-22T16:21:46.853 に答える
4
 public class GenericApiController<TEntity> : BaseApiController
    where TEntity : class, new()
{
    [HttpGet]
    [Route("api/{Controller}/{id}")]       
    public IHttpActionResult Get(int id)
    {
        try
        {
            var entity = db.Set<TEntity>().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<TEntity>().Find(primaryKeyValue);
            ReflectionHelper.Copy(entity, existing, primaryKeyName);
            db.Entry<TEntity>(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<TEntity>().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<TEntity>().Find(id);
            if(entity==null)
            {
                return NotFound();
            }
            db.Set<TEntity>().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<TEntity>().Find(id) != null;
    }
}
于 2018-09-05T09:14:49.827 に答える
1

リポジトリ内のすべての重労働を処理する限り、これに問題はありません。ベースコントローラーでmodelstate例外をラップ/処理することをお勧めします。

私は実際に、ユーザーが独自のエンティティとAPIを定義できる大規模なプロジェクトで同様のことを行っています。つまり、あるユーザーがユーザーとアカウントを持ち、別のユーザーが車などを追跡したい場合があります。それらはすべて同じ内部コントローラーを使用しますが、それぞれに独自のエンドポイントがあります。

ジェネリックスを使用していないため(各オブジェクトはメタデータとして維持され、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'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<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);

}
于 2012-08-23T01:43:42.710 に答える
1

EFモデルやCodeFirstから生成されたものなど、事前定義された設計時クラスがある場合、これはシステムにとって複雑すぎます。これは、事前定義されたクラスがない場合に最適です(データエンティティクラスが実行時に生成される私のプロジェクトのように)。

私の解決策(まだ正しく実装されていません)は、すべてのリクエストに対してジェネリックコントローラーを選択するカスタムIHttpControllerSelectorを作成することでした。そこで、リクエストパスに応じて、リフレクション設定ジェネリックパラメーターを介してコントローラーの記述子タイプをジェネリックから具象に設定できます。

また、良い出発点はhttp://entityrepository.codeplex.com/です(これはstackoverflowのどこかで見つけました)

于 2013-12-17T09:03:48.590 に答える
1

他の人が言ったように、あなたがしていることは間違いなく可能です。ただし、リポジトリの依存関係については、依存性注入を使用する必要があります。私の典型的なコントローラー(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

于 2015-12-22T13:05:15.087 に答える