2014-09-22 102 views
26

我試圖創建MediaTypeFormatter來處理text/csv,但在OData查詢中使用$expand時會遇到一些問題。

查詢:

http://localhost/RestBlog/api/Blogs/121?$expand=Comments 

控制器:

[EnableQuery] 
public IQueryable<Blog> GetBlog(int id) 
{ 
    return DbCtx.Blog.Where(x => x.blogID == id); 
} 

在我的媒體類型格式:

private static MethodInfo _createStreamWriter = 
     typeof(CsvFormatter) 
     .GetMethods(BindingFlags.Static | BindingFlags.NonPublic) 
     .Single(m => m.Name == "StreamWriter"); 

internal static void StreamWriter<T, X>(T results) 
{ 
    var queryableResult = results as IQueryable<X>; 
    if (queryableResult != null) 
    { 
     var actualResults = queryableResult.ToList<X>(); 
    } 
} 

public override void WriteToStream(Type type, object value, 
    Stream writeStream, HttpContent content) 
{ 
    Type genericType = type.GetGenericArguments()[0]; 
    _createStreamWriter.MakeGenericMethod(
       new Type[] { value.GetType(), genericType }) 
       .Invoke(null, new object[] { value } 
     ); 
} 

注意的value類型是System.Data.Entity.Infrastructure.DbQuery<System.Web.Http.OData.Query.Expressions.SelectExpandBinder.SelectAllAndExpand<Rest.Blog>>這意味着它不工作。

value的類型應該是IQueryable但是在鑄造時返回null

在沒有$expand的情況下進行查詢時,事情會更加明智。我究竟做錯了什麼?

我只是想在甚至輸出爲CSV之前獲取數據,因此將不勝感激。

回答

5

SelectExpandBinder.SelectAllAndExpand是SelectExpandWrapper的子類,它實現了IEdmEntityObject和ISelectExpandWrapper。使用ISelectExpandWrapper.ToDictionary方法,您可以獲取底層實體的屬性。這是如何從SelectExpandWrapperConverter中看到對象被序列化爲JSON的方式。

+0

一千個祝福你的先生。 – gorillapower 2017-02-21 10:33:39

2

當我面對我的任務,問題,我GOOGLE了..我有乾淨實現從這個thread

首先你需要驗證EDM模型構建器以適當的方式爲擴大 對象

你必須註冊edm模型博客和外鍵的releas.then只有它會成功

ODataModelBuilder builder = new ODataConventionModelBuilder(); 
     builder.EntitySet<Blog>("blog"); 
     builder.EntitySet<Profile>("profile");//ForeignKey releations of blog 
     builder.EntitySet<user>("user");//ForeignKey releations of profile 
     config.MapODataServiceRoute(
      routeName: "ODataRoute", 
      routePrefix: null, 
      model: builder.GetEdmModel()); 

然後,你需要開發這個格式..example源代碼,我們here

applogies,我的英語 ..

首先,我們需要創建一個將從MediaTypeFormatter抽象類派生的類。下面是它的構造函數的類:

public class CSVMediaTypeFormatter : MediaTypeFormatter { 

    public CSVMediaTypeFormatter() { 

     SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/csv")); 
    } 

    public CSVMediaTypeFormatter(
     MediaTypeMapping mediaTypeMapping) : this() { 

     MediaTypeMappings.Add(mediaTypeMapping); 
    } 

    public CSVMediaTypeFormatter(
     IEnumerable<MediaTypeMapping> mediaTypeMappings) : this() { 

     foreach (var mediaTypeMapping in mediaTypeMappings) { 
      MediaTypeMappings.Add(mediaTypeMapping); 
     } 
    } 
} 

以上,無論你用哪個構造,我們隨時添加這種格式可以支持文本/ CSV媒體類型。我們還允許注入自定義MediaTypeMappings。

現在,我們需要重寫兩個方法:MediaTypeFormatter.CanWriteType和MediaTypeFormatter.OnWriteToStreamAsync。

首先,這裏是CanWriteType方法的實現。該方法需要做的是確定該格式化程序是否支持該對象的類型,以便編寫它。

protected override bool CanWriteType(Type type) { 

    if (type == null) 
     throw new ArgumentNullException("type"); 

    return isTypeOfIEnumerable(type); 
} 

private bool isTypeOfIEnumerable(Type type) { 

    foreach (Type interfaceType in type.GetInterfaces()) { 

     if (interfaceType == typeof(IEnumerable)) 
      return true; 
    } 

    return false; 
} 

這裏做的是檢查對象是否實現了IEnumerable接口。如果是這樣,那麼它很酷,可以格式化對象。如果不是,它將返回false,並且框架將忽略該特定請求的格式化程序。

最後,這裏是實際的實現。我們需要做的反射這裏的一些工作,以獲得屬性名稱和值出這是一類對象的值參數:

protected override Task OnWriteToStreamAsync(
    Type type, 
    object value, 
    Stream stream, 
    HttpContentHeaders contentHeaders, 
    FormatterContext formatterContext, 
    TransportContext transportContext) { 

    writeStream(type, value, stream, contentHeaders); 
    var tcs = new TaskCompletionSource<int>(); 
    tcs.SetResult(0); 
    return tcs.Task; 
} 

private void writeStream(Type type, object value, Stream stream, HttpContentHeaders contentHeaders) { 

    //NOTE: We have check the type inside CanWriteType method 
    //If request comes this far, the type is IEnumerable. We are safe. 

    Type itemType = type.GetGenericArguments()[0]; 

    StringWriter _stringWriter = new StringWriter(); 

    _stringWriter.WriteLine(
     string.Join<string>(
      ",", itemType.GetProperties().Select(x => x.Name) 
     ) 
    ); 

    foreach (var obj in (IEnumerable<object>)value) { 

     var vals = obj.GetType().GetProperties().Select(
      pi => new { 
       Value = pi.GetValue(obj, null) 
      } 
     ); 

     string _valueLine = string.Empty; 

     foreach (var val in vals) { 

      if (val.Value != null) { 

       var _val = val.Value.ToString(); 

       //Check if the value contans a comma and place it in quotes if so 
       if (_val.Contains(",")) 
        _val = string.Concat("\"", _val, "\""); 

       //Replace any \r or \n special characters from a new line with a space 
       if (_val.Contains("\r")) 
        _val = _val.Replace("\r", " "); 
       if (_val.Contains("\n")) 
        _val = _val.Replace("\n", " "); 

       _valueLine = string.Concat(_valueLine, _val, ","); 

      } else { 

       _valueLine = string.Concat(string.Empty, ","); 
      } 
     } 

     _stringWriter.WriteLine(_valueLine.TrimEnd(',')); 
    } 

    var streamWriter = new StreamWriter(stream); 
     streamWriter.Write(_stringWriter.ToString()); 
} 

我們完成了一部分。現在,我們需要利用這一點。我註冊了這個格式與Global.asax中的Application_Start方法中下面的代碼管道:

GlobalConfiguration.Configuration.Formatters.Add(
    new CSVMediaTypeFormatter(
     new QueryStringMapping("format", "csv", "text/csv") 
    ) 
); 

在我的示例應用程序,當你瀏覽到/ API /汽車格式= CSV,它可以把你一個CSV文件?但沒有擴展。繼續並添加csv擴展。然後,用Excel打開它,你應該看到類似如下的東西:

+0

這是更接近,但仍然不是我所需要的。如果被轉換的對象與另一個實體有外鍵關係,那麼調用val.Value.ToString()將其全部轉儲爲一個字符串,作爲「Property1 = Value1 Property2 = Value2」,這在csv中並不實用。 – Geoff 2015-05-27 13:13:09

+0

如果您在查詢字符串中指定$ expand,則自此更糟的是obj.GetType()返回System.Web.OData.Query.Expressions.SelectExpandBinder.SelectAllAndExpand <>,因此它沒有獲得實際的正確屬性實體對象。 – Geoff 2015-05-27 13:18:15

-1

你的行動GetBlog得到的參數名稱不正確。參數名稱應該是「key」而不是id。試試這個作爲你的行動,而不是,

[Queryable] 
public SingleResult<Blog> GetBlog([FromODataUri]int key) 
{ 
    return SingleResult.Create(DbCtx.Blog.Where(x => x.blogID == key)); 
} 

article其中明確指出,必須將參數稱爲關鍵!