2014-09-22 10 views
26

मैं text/csv संभालने के लिए एक MediaTypeFormatter बनाने के लिए कोशिश कर रहा हूँ, लेकिन जब 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 के बिना कोई क्वेरी बनाते समय चीजें अधिक समझदारी से काम करती हैं। मैं क्या गलत कर रहा हूं?

मैं बस सीएसवी के रूप में आउटपुट करने से पहले डेटा प्राप्त करने की कोशिश कर रहा हूं, इसलिए मार्गदर्शन की सराहना की जाएगी।

उत्तर

5

SelectExpandBinder.SelectAllAndExpand SelectExpandWrapper का उप-वर्ग है जो IEdmEntityObject और ISelectExpandWrapper लागू करता है। ISelectExpandWrapper.ToDictionary विधि का उपयोग करके, आप अंतर्निहित इकाई के गुणों पर प्राप्त कर सकते हैं। इस प्रकार ऑब्जेक्ट JSON को क्रमबद्ध किया गया है जैसा कि SelectExpandWrapperConverter से देखा जा सकता है।

+0

आप पर एक हजार आशीर्वाद महोदय। – gorillapower

2

मैं googled किया गया था जब मैं अपने काम में इस मुद्दे का सामना करना .. मैं इस thread

पहले आप के लिए उचित तरीके से EDM modelbuilder सत्यापित करने की जरूरत से साफ कार्यान्वयन का विस्तार वस्तुओं

आप

है ब्लॉग और विदेशी कुंजी रिलीज के लिए एडीएम मॉडल पंजीकृत करना होगा। फिर केवल यह

उदाहरण

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); 
     } 
    } 
} 

ऊपर,: यहाँ अपने कंस्ट्रक्टर्स साथ वर्ग है। हम कस्टम मीडिया टाइप मैपिंग इंजेक्शन देने की भी अनुमति देते हैं।

अब, हमें दो विधियों को ओवरराइड करने की आवश्यकता है: 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; 
} 

यह यहां जांचने के लिए है कि ऑब्जेक्ट ने आईनेमरेबल इंटरफ़ेस को कार्यान्वित किया है या नहीं। यदि ऐसा है, तो यह उस के साथ अच्छा है और वस्तु को प्रारूपित कर सकते हैं। यदि नहीं, तो यह झूठी वापसी करेगा और ढांचा इस विशेष अनुरोध के लिए इस फ़ॉर्मेटर को अनदेखा कर देगा।

और अंत में, यहां वास्तविक कार्यान्वयन है। हम आदेश मूल्य पैरामीटर जो वस्तु का एक प्रकार है से बाहर संपत्ति के नाम और मान प्राप्त करने के लिए प्रतिबिंब यहाँ कुछ काम करने की जरूरत है:

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()); 
} 

हम आंशिक रूप से किया जाता है। अब, हमें इसका उपयोग करने की आवश्यकता है।

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

मेरी नमूना आवेदन पर, जब आप करने के लिए/API/कारों के प्रारूप = सीएसवी नेविगेट, तो यह आपको एक CSV फ़ाइल मिल जाएगा: मैं Global.asax Application_Start विधि के अंदर निम्न कोड के साथ पाइपलाइन में इस फ़ॉर्मेटर पंजीकृत लेकिन एक विस्तार के बिना। आगे बढ़ें और सीएसवी एक्सटेंशन जोड़ें। फिर, इसे Excel के साथ खोलें और आपको नीचे जैसा कुछ दिखना चाहिए:

+0

यह करीब है, लेकिन अभी भी मुझे जो चाहिए वह बिल्कुल नहीं है। यदि ऑब्जेक्ट को परिवर्तित किया जा रहा है तो किसी अन्य इकाई के साथ एक विदेशी कुंजी संबंध है, तो कॉल val.Value.ToString() इसे सभी को एक स्ट्रिंग में "Property1 = Value1 Property2 = Value2" के रूप में डंप करता है जो वास्तव में एक csv में उपयोगी नहीं है। – Geoff

+0

और यदि आप क्वेरी स्ट्रिंग में $ विस्तार निर्दिष्ट करते हैं, तो तब से भी बदतर है obj.GetType() System.Web.OData.Query.Expressions.SelectExpandBinder.SelectAllAndExpand <> देता है, इसलिए इसे वास्तविक के लिए सही गुण नहीं मिलते हैं इकाई वस्तु। – Geoff

-1

आपकी कार्रवाई GetBlog को पैरामीटर नाम गलत मिला। पैरामीटर नाम आईडी के बजाय "कुंजी" होना चाहिए। बजाय अपनी कार्रवाई के रूप में इस प्रयास करें,

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

यह article जो स्पष्ट रूप से कहा गया है कि पैरामीटर कुंजी बुलाया जाना चाहिए!

संबंधित मुद्दे