I have a web site based on MVC with a page that should serve OData formatted results, while also logging how many bytes were sent representing each record in the result. I get the results from the DB using this code:
[EnableQuery]
public IHttpActionResult GetRecords(ODataQueryOptions<Record> queryOptions)
{
DataProvider<Record> provider = GetRecordProvider();
...
return OK<IQueryable<Record>>(provider.Queryable);
}
I tried to hook to the OData formatter by
config.Formatters.InsertRange(0, ODataMediaTypeFormatters.Create(new CustomODataSerializerProvider(), new DefaultODataDeserializerProvider()));
where
public class CustomODataSerializerProvider : DefaultODataSerializerProvider
{
public override ODataEdmTypeSerializer GetEdmTypeSerializer(IEdmTypeReference edmType)
{
if (edmType.Definition.TypeKind == EdmTypeKind.Entity)
return new CustomODataEntityTypeSerializer(this);
else
return base.GetEdmTypeSerializer(edmType);
}
}
public class CustomODataEntityTypeSerializer : ODataEntityTypeSerializer
{
public CustomODataEntityTypeSerializer(ODataSerializerProvider provider)
: base(provider)
{
}
public override ODataProperty CreateStructuralProperty(IEdmStructuralProperty structuralProperty, EntityInstanceContext entityInstanceContext)
{
var property = base.CreateStructuralProperty(structuralProperty, entityInstanceContext);
if(property.Value == null) return null;
else return property;
}
public override void WriteObject(object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext)
{
base.WriteObject(graph, type, messageWriter, writeContext);
}
public override void WriteDeltaObjectInline(object graph, IEdmTypeReference expectedType, ODataDeltaWriter writer, ODataSerializerContext writeContext)
{
base.WriteDeltaObjectInline(graph, expectedType, writer, writeContext);
}
public override void WriteObjectInline(object graph, IEdmTypeReference expectedType, ODataWriter writer, ODataSerializerContext writeContext)
{
int outputSize = 0;
base.WriteObjectInline(graph, expectedType, writer, writeContext);
writer.Flush();
Log(outputSize);
}
}
I thought I would be able to find out the length of the output generated by the WriteObjectInline
call, but can't figure out how to do it.
I also tried a different solution, using
public class MeasuringJsonFormatter : ODataMediaTypeFormatter
{
public MeasuringJsonFormatter(IEnumerable<Microsoft.Data.OData.ODataPayloadKind> payloadKinds)
: base(payloadKinds)
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
}
public override bool CanReadType(Type type)
{
return false;
}
private bool IsSupportedType(Type type)
{
return type==typeof(Record);
}
private bool IsSupportedCollection(Type type)
{
return
type.IsGenericType &&
IsSupportedType(type.GetGenericArguments()[0]) &&
typeof(IEnumerable<>).MakeGenericType(type.GetGenericArguments()[0]).IsAssignableFrom(type)
;
}
public override bool CanWriteType(Type type)
{
return IsSupportedType(type) || IsSupportedCollection(type);
}
public override System.Threading.Tasks.Task WriteToStreamAsync(Type type, object value, Stream writeStream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext)
{
return base.WriteToStreamAsync(typeof(string), Format(type, value, content), writeStream, content, transportContext);
}
public override System.Threading.Tasks.Task WriteToStreamAsync(Type type, object value, Stream writeStream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext, System.Threading.CancellationToken cancellationToken)
{
return base.WriteToStreamAsync(typeof(string), Format(type, value, content), writeStream, content, transportContext, cancellationToken);
}
private string Format(Type type, object value, System.Net.Http.HttpContent content)
{
if (IsSupportedType(type))
{
string result =JsonConvert.SerializeObject(value, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
Log(result.Length);
return result;
}
else if (IsSupportedCollection(type))
{
StringBuilder sb = new StringBuilder();
foreach (object item in (value as IEnumerable)) sb.Append(Format(type.GetGenericArguments()[0], item, content));
return sb.ToString();
}
else return "Unable to process type " + type.ToString();
}
}
hooked by
config.Formatters.Insert(0, new MeasuringJsonFormatter(new ODataPayloadKind[] { ODataPayloadKind.Entry, ODataPayloadKind.Feed}));
but here the hook does not seem to work: I set breakpoints to all methods defined in the MeasuringJsonFormatter
, and none were hit.
Can anyone give me a direction where to look?
I'm using C# with Visual Studio 2010, MS ASP.NET MVC 5.2.3, MS ASP.NET Web API 2.2 for OData v4
I'm not sure but if you implement your own version of ODataWriter then you can check for the length of the entry. Look at this example: https://blogs.msdn.microsoft.com/odatateam/2015/01/05/tutorial-sample-odatalib-custom-payload-format/ of custom ODataWriter. In the CsvOutputContext you got
public void Flush()
{
this.stream.Flush();
}
I think you can try to check this.stream.Length
before Flush
, but I didn't test it.
If you want to add data or change the way serializer saves your data you need to override WriteStart inside your custom ODataWriter like this:
public override void WriteStart(ODataFeed feed)
{
}
private void WriteEntry(ODataEntry entry)
{
foreach (var header in this.headers)
{
var property = entry.Properties.SingleOrDefault(p => p.Name == header);
if (property != null)
{
this.context.Writer.Write(property.Value);
}
this.context.Writer.Write(",");
}
this.context.Writer.WriteLine();
}
this.context.Writer.Write
lets you add whatver data you like. Just look at example link that I provided.
In order to save binary data like jpg file you will need to serialize it. Look at this:
private void WriteEntry(ODataEntry entry)
{
string file = @"C:\test.jpg";
byte[] data = File.ReadAllBytes(file);
using (MemoryStream fileStream = new MemoryStream(data))
using (Image i = Image.FromStream(originalms))
{
i.Save(this.context.Writer, System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With