2017-02-23 77 views
1

我正在構建WebApi2項目以公開一些RESTful服務。假設我有以下模型對象。未使用EF的WebApi字段過濾

public class Person 
{ 
    public string Name { get; set; } 

    public DateTime? Birthdate { get; set; } 

    public int Status { get; set; } 

    public List<Account> Accounts { get; set; } 
} 

public class Account 
{ 
    public decimal Amount { get; set; } 

    public string Code { get; set; } 

    public DateTime Expiry { get; set; } 
} 

在我的服務中,我必須去2個不同的系統檢索個人的數據和人的帳戶信息。顯然,服務的實現看起來像

[HttpGet] 
    [Route("Person/{id:int}")] 
    public IHttpActionResult Get(string id) 
    { 
     var person = new Person(); 
     person = GetPersonFromSystemA(id); 

     if (person.Status == 2) 
     { 
      person.Accounts = GetPersonAccountsFromSystemB(id); 
     } 

     return this.Ok(person); 
    } 

我不能在這種情況下使用EF可言,所以的OData是非常棘手的。

我有一些要求,我需要提供過濾功能的服務客戶端。客戶可以決定返回哪些對象字段,這也意味着如果客戶端不想包含該人員的賬戶信息,我應該跳過第二次調用系統B以避免整個子對象。

我做了一些快速搜索,但我找不到一些類似的解決方案。我知道我可以實現我自己的過濾語法,並讓所有自定義代碼使用過濾(通過有很多if/else)。

我在尋找更優雅的解決方案的一些想法。

回答

0

構建OData服務不需要實體框架。如果您不使用OData,您可能必須實現您自己的IQueryable,這是OData開箱即用的功能。

一些示例代碼。

模型類增加了一些特性

public class Person 
{ 
    [Key] 
    public String Id { get; set; } 
    [Required] 
    public string Name { get; set; } 
    public DateTime? Birthdate { get; set; } 
    public int Status { get; set; } 
    public List<Account> Accounts { get; set; } 
} 

public class Account 
{ 
    [Key] 
    public String Id { get; set; } 
    [Required] 
    public decimal Amount { get; set; } 
    public string Code { get; set; } 
    public DateTime Expiry { get; set; } 
} 

WebApiConfig.cs

public static class WebApiConfig 
{ 
    public static void Register(HttpConfiguration config) 
    { 
     config.MapODataServiceRoute("odata", null, GetEdmModel(), new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer)); 
     config.EnsureInitialized(); 
    } 

    private static IEdmModel GetEdmModel() 
    { 
     ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); 
     builder.Namespace = "YourNamespace"; 
     builder.ContainerName = "DefaultContainer"; 
     builder.EntitySet<Person>("People"); 
     builder.EntitySet<Account>("Accounts"); 
     var edmModel = builder.GetEdmModel(); 
     return edmModel; 
    } 
} 

控制方法

[EnableQuery] 
public class PeopleController : ODataController 
{ 
    public IHttpActionResult Get() 
    { 
     return Ok(SomeDataSource.Instance.People.AsQueryable()); 
    } 
} 

您將需要包括Microsoft.AspNet.OData Nuget包。

有關更多指導,請參閱以下內容。它使用內存數據源,但不管怎樣,概念都是一樣的。

http://www.odata.org/blog/how-to-use-web-api-odata-to-build-an-odata-v4-service-without-entity-framework/

+0

感謝您的評論,您的實現假設我必須首先獲取所有數據,無論它們是否有用,然後應用過濾。如果EF/OData實現使用延遲加載,則根本不會加載過濾出的日期。我也嘗試實現類似的功能來加載我需要的數據,因爲調用外部系統很昂貴。 – hardywang

0

當構建你會經常要篩選您迴應並獲得唯一的某些字段的web API。你可以用很多方式做到這一點,其中之一如上所述。另一種方法,你可以通過你的web api使用數據整形。

如果你有一個控制器動作這樣:

public IHttpActionResult Get(string fields="all") 
{ 
    try 
    { 
     var results = _tripRepository.Get(); 
     if (results == null) 
      return NotFound(); 
     // Getting the fields is an expensive operation, so the default is all, 
     // in which case we will just return the results 
     if (!string.Equals(fields, "all", StringComparison.OrdinalIgnoreCase)) 
     { 
      var shapedResults = results.Select(x => GetShapedObject(x, fields)); 
      return Ok(shapedResults); 
     } 
     return Ok(results); 
    } 
    catch (Exception) 
    { 
     return InternalServerError(); 
    } 
} 

然後你GetShapedData方法可以做過濾這樣:

public object GetShapedObject<TParameter>(TParameter entity, string fields) 
{ 
    if (string.IsNullOrEmpty(fields)) 
     return entity; 
    Regex regex = new Regex(@"[^,()]+(\([^()]*\))?"); 
    var requestedFields = regex.Matches(fields).Cast<Match>().Select(m => m.Value).Distinct(); 
    ExpandoObject expando = new ExpandoObject(); 

    foreach (var field in requestedFields) 
    { 
     if (field.Contains("(")) 
     { 
      var navField = field.Substring(0, field.IndexOf('(')); 

      IList navFieldValue = entity.GetType() 
            ?.GetProperty(navField, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public) 
            ?.GetValue(entity, null) as IList; 
      var regexMatch = Regex.Matches(field, @"\((.+?)\)"); 
      if (regexMatch?.Count > 0) 
      { 
       var propertiesString = regexMatch[0].Value?.Replace("(", string.Empty).Replace(")", string.Empty); 
       if (!string.IsNullOrEmpty(propertiesString)) 
       { 
        string[] navigationObjectProperties = propertiesString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); 

        List<object> list = new List<object>(); 
        foreach (var item in navFieldValue) 
        { 
         list.Add(GetShapedObject(item, navigationObjectProperties)); 
        } 

        ((IDictionary<string, object>)expando).Add(navField, list); 
       } 
      } 
     } 
     else 
     { 
      var value = entity.GetType() 
          ?.GetProperty(field, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public) 
          ?.GetValue(entity, null); 
      ((IDictionary<string, object>)expando).Add(field, value); 
     } 
    } 

    return expando; 
} 

檢查我的博客進行了詳細的職位:https://jinishbhardwaj.wordpress.com/2016/12/03/web-api-supporting-data-shaping/

+0

這是一個很好的解決方案,我會研究它的結果。謝謝。 – hardywang