2014-11-13 10 views
1

예를 들어, ParentsController라는 단일 컨트롤러가있는 간단한 웹 API가 있는데, 표준 WebAPi2 ApiController입니다. 보일러 판입니다.HttpRequest 프레임웍에 Linq-To-Repository를 만드는 방법

클라이언트 측에서 나는 Repository Base 클래스를 생성했으며이를 기반으로 ParentRepository 클래스를 가지고 있습니다.

ApiRequester :

public class ApiRequester 
{ 
    readonly Uri _apiServiceBaseAddress; 
    readonly String _apiControllerPath; 
    readonly Guid _apiKey; 

    public ApiRequester(string apiServiceBaseAddress, string apiControllerPath, Guid apiKey) 
    { 
     _apiServiceBaseAddress = new Uri(apiServiceBaseAddress); 
     _apiControllerPath = apiControllerPath; 
     _apiKey = apiKey; 
    } 
    public async Task<T> Get<T>(string queryParameters) 
    { 
     using (var client = new HttpClient { BaseAddress = _apiServiceBaseAddress }) 
     { 
      //client.Timeout = TimeSpan.FromSeconds(60); 
      client.DefaultRequestHeaders.Add("X-APIKey", _apiKey.ToString()); 

      var response = await client.GetAsync(_apiControllerPath + queryParameters); 

      var result = await response.Content.ReadAsAsync<T>(); 

      return result; 
     } 
    } 

    public async Task<T> Post<T>(string queryParameters, T entity) 
    { 
     using (var client = new HttpClient { BaseAddress = _apiServiceBaseAddress }) 
     { 
      client.DefaultRequestHeaders.Add("X-APIKey", _apiKey.ToString()); 
      HttpResponseMessage response = null; 

      response = await client.PostAsJsonAsync<T>(_apiControllerPath + queryParameters, entity); 

      response.EnsureSuccessStatusCode(); 
      var result = await response.Content.ReadAsAsync<T>(); 
      return result; 
     } 
    } 

    public async Task<T> Put<T>(string queryParameters, T entity) 
    { 
     using (var client = new HttpClient { BaseAddress = _apiServiceBaseAddress }) 
     { 
      client.DefaultRequestHeaders.Add("X-APIKey", _apiKey.ToString()); 
      var response = await client.PutAsJsonAsync<T>(_apiControllerPath + queryParameters, entity); 
      response.EnsureSuccessStatusCode(); 

      var result = await response.Content.ReadAsAsync<T>(); 

      return result; 
     } 
    } 

    public async Task Delete(string queryParameters) 
    { 
     using (var client = new HttpClient { BaseAddress = _apiServiceBaseAddress }) 
     { 
      client.DefaultRequestHeaders.Add("X-APIKey", _apiKey.ToString()); 

      var response = await client.DeleteAsync(_apiControllerPath + queryParameters); 
      response.EnsureSuccessStatusCode(); 
     } 
    } 
} 

RepositoryBase :

public abstract class RepositoryBase<T> where T : class, IEntity 
{ 
    readonly ApiRequester _requester; 

    protected RepositoryBase(string apiBaseUri, string controllerPath, Guid apiKey) 
    { 
     if (String.IsNullOrWhiteSpace(apiBaseUri)) 
      throw new ArgumentNullException("apiBaseUri"); 

     if (apiKey.Equals(default(Guid))) 
      throw new ArgumentNullException("apiKey"); 

     _requester = new ApiRequester(apiBaseUri, controllerPath, apiKey); 
    } 
    public virtual Task<ICollection<T>> GetAsync(string queryParameters) 
    { 
     return _requester.Get<ICollection<T>>(queryParameters); 
    } 

    public virtual Task<T> PostAsync(string queryParameters, T entity) 
    { 
     return _requester.Post<T>(queryParameters, entity); 
    } 

    public virtual Task<T> PutAsync(string queryParameters, T entity) 
    { 
     return _requester.Put<T>(queryParameters, entity); 
    } 
    public virtual Task DeleteAsync(string queryParameters) 
    { 
     return _requester.Delete(queryParameters); 
    } 
} 

ParentRepository :

public class ParentRepository : RepositoryBase<Parent> 
{ 
    public ChannelRepository(string apiBaseUri, string controllerPath, Guid apiKey) 
     : base(apiBaseUri, controllerPath, apiKey) 
    { } 
} 

따라서 클라이언트의 소비자 따라서 API에서 부모의 목록을 얻기 위해 전화를 걸 수 있습니다 :

내가 내 저장소 방법에있게하고 싶은 것은 다음과 유사한입니다

public class Parent : IEntity 
{ 
    [Key] 
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public Guid ParentId { get; set; } 

    public String Name { get; set; } 

// Navigation Properties 
    public virtual ICollection<Child> Children { get; set; } 
} 
:

pRepo = new ParentRepository(); 
var parents = await pRepo.GetAsync().Where(p => p.Name == "aName").Include(p => p.Children); 

가 그리고 여기에 내가 끼 었어 곳

pRepo = new ParentRepository(); 
var parents = await pRepo.GetAsync(); 

// Do something with parent collection. 

는 부모 엔티티는 다음과 같습니다 .

내 http 요청을 빌드 할 수 있도록 GetAsync() 메소드에서 어떻게 차단합니까?

'Where'절을 가져 오려면 확장 메서드를 만드시겠습니까? 은 다음과 같습니다.

public static ICollection<TEntity> Where(this ICollection<TEntity> source, Func<TEntity> , bool> predicate) 
{ 
// Do something or not? 
} 

하지만 그 다음은 무엇입니까?

'Include'부분에는 System.Reflection에서 Child collection 속성 이름을 가져 오기 위해 무엇이 필요합니까?

다시 내 GetAsync() 메서드에서 유추를 가로채는 방법은 무엇입니까?

내가 오버로드 된 메소드를 사용하여 원하는 것을 쉽게 수행 할 수있는 방법이 있다는 것을 알고 있지만 linq와 유사한 접근법을 만드는 아이디어를 좋아합니다.

편집 :

내가 원하는 명확성을 위해

와 내가 웹 URI에 내 LINQ 식을 변환 (방법을 아직 확실하지) 식 트리를 사용할 필요가 있다고 생각 :

그렇게 내 고객에 내가 쓰기 : 다음 GET 요청을 수행 할 수 있습니다

GET /api/parents/?name=aName&IncludeChildren=true 

클라이언트 : var parents = await pRepo.GetAsync().Where(p => p.Name == "aName").Include(p => p.Children);

및 그 표현이 같은 변환됩니다. 이것은 모든 클라이언트에서 사용할 수 있어야합니다. 웹 기반 API의 모든 유형을 타겟팅하는 것은 WebAPI 일 필요는 없습니다.

답변

0

필자가 경험 한 바에 따르면 Web API 컨트롤러는 필터링 및 정렬과 같은 선택적 매개 변수를 가지고 있으며 이러한 매개 변수를 linq와 유사한 요청으로 변환하여 데이터 액세스 계층의 리포지토리 클래스에 전달합니다. 쿼리

GET /api/parent?minimumId=5&maximumId=20&orderBy=Name 

옵션 쿼리 문자열 매개 변수

이 Linq에 또는 Dynamic Linq 중 하나로 변환 할 수 있습니다 : 아무도 같은 쿼리 문자열에 표준 HTTP 규칙을 사용하여 웹 API 컨트롤러에 요청을하는 클라이언트에 상주하지 기본 DAL에서 제공하는 일종의 IQueryable입니다.

웹 API 컨트롤러에 대한 요청에서 linq와 비슷한 동작이 자주 발생하지 않을 것입니다. 컨트롤러가 HTTP 지향적이며 HTTP와 비슷한 동작을하는 경향이 있기 때문입니다.

Linq와 유사한 구문의 호출을 실제로 랩핑하려는 경우, Dynamic Linq을 사용하여 Linq 쿼리로 데이터 (Funcs/Expressions/Strings/등)를 변환하는 방법을 살펴볼 것입니다. 너 자신에 대한 성찰을 피한다.

+0

내 질문이 구체적이지 않을 수 있습니다. 내가 원하는 것과 연구를 통해 표현식 트리로 작업해야한다고 생각합니다. 그래도 어떻게 확신 할 수 있습니까? 변환하고 싶습니다 : pRepo = new ParentRepository(); var parents = pRepo.GetAsync()를 기다립니다. (p => p.Name == "aName"). 포함 (p => p.Children); to/api/parent? name = aName & IncludeChildren = true – John

+0

이것은 좀 더 명확합니다. 나는 의도 된 질문에 대한 적어도 부분적인 해결책을 가지고 오늘 나의 대답을 편집하려고 노력할 것이다. – jmsb