C# 有没有办法在启动时通过ConfigureServices向ASP.Net Core 2.0添加开放通用格式化程序
根据标题,我希望能够在Startup.cs中添加open generic InputFormatter和OutputFormatter实例作为ConfigureServices的一部分,类似于添加open generic services的方式 我想要的是这样的:C# 有没有办法在启动时通过ConfigureServices向ASP.Net Core 2.0添加开放通用格式化程序,c#,protocol-buffers,asp.net-core-2.0,inputformatter,C#,Protocol Buffers,Asp.net Core 2.0,Inputformatter,根据标题,我希望能够在Startup.cs中添加open generic InputFormatter和OutputFormatter实例作为ConfigureServices的一部分,类似于添加open generic services的方式 我想要的是这样的: services.AddMvc(options => { options.InputFormatters.Add(new ProtobufInputFormatter<>())
services.AddMvc(options =>
{
options.InputFormatters.Add(new ProtobufInputFormatter<>());
options.OutputFormatters.Add(new ProtobufOutputFormatter<>());
options.FormatterMappings.SetMediaTypeMappingForFormat("protobuf", MediaTypeHeaderValue.Parse("application/x-protobuf"));
});
public class ProtobufInputFormatter<T> : InputFormatter where T : IMessage<T>, new()
{
static MediaTypeHeaderValue protoMediaType = MediaTypeHeaderValue.Parse("application/x-protobuf");
public override bool CanRead(InputFormatterContext context)
{
var request = context.HttpContext.Request;
MediaTypeHeaderValue requestContentType = null;
MediaTypeHeaderValue.TryParse(request.ContentType, out requestContentType);
if (requestContentType == null)
{
return false;
}
return requestContentType.IsSubsetOf(protoMediaType);
}
public override Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
{
try
{
var request = context.HttpContext.Request;
var serialiser = new ProtobufSerialiser<T>();
var obj = serialiser.Deserialise(request.Body);
return InputFormatterResult.SuccessAsync(obj);
}
catch (Exception ex)
{
Console.WriteLine("Exception: " + ex);
return InputFormatterResult.FailureAsync();
}
}
class ProtobufSerializer<T> where T: new() {
public T Deserialize(Stream body) {
...
}
}
public static class SerializersCache {
private static readonly ConcurrentDictionary<Type, Func<Stream, object>> _cache = new ConcurrentDictionary<Type, Func<Stream, object>>();
public static object Deserialize(Type type, Stream body) {
var handler = _cache.GetOrAdd(type, (key) => {
var arg = Expression.Parameter(typeof(Stream), "body");
var genericSerializer = typeof(ProtobufSerializer<>).MakeGenericType(key);
// new ProtobufSerializer<T>();
var instance = Expression.New(genericSerializer.GetConstructor(new Type[0]));
// new ProtobufSerializer<T>().Deserialize(body);
var call = Expression.Call(instance, "Deserialize", new Type[0], arg);
// body => new ProtobufSerializer<T>().Deserialize(body);
return Expression.Lambda<Func<Stream, object>>(call, arg).Compile();
});
return handler(body);
}
}
services.AddMvc(选项=>
{
options.InputFormatters.Add(新ProtobufInputFormatter());
options.OutputFormatters.Add(新ProtobufOutputFormatter());
options.FormatterMappings.SetMediaTypeMappingForFormat(“protobuf”,MediaTypeHeaderValue.Parse(“application/x-protobuf”);
});
这有可能吗
编辑:
当前实现的ProtoBufOutFormatter示例
public class ProtobufInputFormatter : InputFormatter
{
static MediaTypeHeaderValue protoMediaType = MediaTypeHeaderValue.Parse("application/x-protobuf");
public override bool CanRead(InputFormatterContext context)
{
var request = context.HttpContext.Request;
MediaTypeHeaderValue requestContentType = null;
MediaTypeHeaderValue.TryParse(request.ContentType, out requestContentType);
if (requestContentType == null)
{
return false;
}
return requestContentType.IsSubsetOf(protoMediaType);
}
public override Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
{
try
{
var request = context.HttpContext.Request;
var obj = (IMessage)Activator.CreateInstance(context.ModelType);
obj.MergeFrom(request.Body);
return InputFormatterResult.SuccessAsync(obj);
}
catch (Exception ex)
{
Console.WriteLine("Exception: " + ex);
return InputFormatterResult.FailureAsync();
}
}
}
公共类ProtobufInputFormatter:InputFormatter
{
静态MediaTypeHeaderValue protoMediaType=MediaTypeHeaderValue.Parse(“应用程序/x-protobuf”);
公共覆盖bool CanRead(InputFormatterContext上下文)
{
var request=context.HttpContext.request;
MediaTypeHeaderValue requestContentType=null;
MediaTypeHeaderValue.TryParse(request.ContentType,out requestContentType);
if(requestContentType==null)
{
返回false;
}
return requestContentType.IsSubsetOf(protoMediaType);
}
公共重写任务ReadRequestBodyAsync(InputFormatterContext上下文)
{
尝试
{
var request=context.HttpContext.request;
var obj=(IMessage)Activator.CreateInstance(context.ModelType);
对象合并自(请求主体);
返回InputFormatterResult.SuccessAsync(obj);
}
捕获(例外情况除外)
{
Console.WriteLine(“异常:+ex”);
返回InputFormatterResult.FailureAsync();
}
}
}
问题是,使用反射的性能不如我能够使用通用Protobuf反序列化程序,例如:
services.AddMvc(options =>
{
options.InputFormatters.Add(new ProtobufInputFormatter<>());
options.OutputFormatters.Add(new ProtobufOutputFormatter<>());
options.FormatterMappings.SetMediaTypeMappingForFormat("protobuf", MediaTypeHeaderValue.Parse("application/x-protobuf"));
});
public class ProtobufInputFormatter<T> : InputFormatter where T : IMessage<T>, new()
{
static MediaTypeHeaderValue protoMediaType = MediaTypeHeaderValue.Parse("application/x-protobuf");
public override bool CanRead(InputFormatterContext context)
{
var request = context.HttpContext.Request;
MediaTypeHeaderValue requestContentType = null;
MediaTypeHeaderValue.TryParse(request.ContentType, out requestContentType);
if (requestContentType == null)
{
return false;
}
return requestContentType.IsSubsetOf(protoMediaType);
}
public override Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
{
try
{
var request = context.HttpContext.Request;
var serialiser = new ProtobufSerialiser<T>();
var obj = serialiser.Deserialise(request.Body);
return InputFormatterResult.SuccessAsync(obj);
}
catch (Exception ex)
{
Console.WriteLine("Exception: " + ex);
return InputFormatterResult.FailureAsync();
}
}
class ProtobufSerializer<T> where T: new() {
public T Deserialize(Stream body) {
...
}
}
public static class SerializersCache {
private static readonly ConcurrentDictionary<Type, Func<Stream, object>> _cache = new ConcurrentDictionary<Type, Func<Stream, object>>();
public static object Deserialize(Type type, Stream body) {
var handler = _cache.GetOrAdd(type, (key) => {
var arg = Expression.Parameter(typeof(Stream), "body");
var genericSerializer = typeof(ProtobufSerializer<>).MakeGenericType(key);
// new ProtobufSerializer<T>();
var instance = Expression.New(genericSerializer.GetConstructor(new Type[0]));
// new ProtobufSerializer<T>().Deserialize(body);
var call = Expression.Call(instance, "Deserialize", new Type[0], arg);
// body => new ProtobufSerializer<T>().Deserialize(body);
return Expression.Lambda<Func<Stream, object>>(call, arg).Compile();
});
return handler(body);
}
}
public类ProtobufInputFormatter:InputFormatter其中T:IMessage,new()
{
静态MediaTypeHeaderValue protoMediaType=MediaTypeHeaderValue.Parse(“应用程序/x-protobuf”);
公共覆盖bool CanRead(InputFormatterContext上下文)
{
var request=context.HttpContext.request;
MediaTypeHeaderValue requestContentType=null;
MediaTypeHeaderValue.TryParse(request.ContentType,out requestContentType);
if(requestContentType==null)
{
返回false;
}
return requestContentType.IsSubsetOf(protoMediaType);
}
公共重写任务ReadRequestBodyAsync(InputFormatterContext上下文)
{
尝试
{
var request=context.HttpContext.request;
var serialiser=new ProtobufSerialiser();
var obj=serialiser.Deserialise(request.Body);
返回InputFormatterResult.SuccessAsync(obj);
}
捕获(例外情况除外)
{
Console.WriteLine(“异常:+ex”);
返回InputFormatterResult.FailureAsync();
}
}
ProtobufSerialiser是Google.Protobuf的包装器(由于性能原因,它被Activator.CreateInstance的需要所颠覆,如上面实际实现和工作的非通用示例所示)
T将来自端点。我认为这是不可能的,它只是没有设计为将当前模型类型作为泛型类型参数提供给格式化程序(如果格式化程序是泛型的) 如果您测量到反射(通过使用非泛型版本)正在减慢速度,我建议改用编译的表达式树,并缓存它们。例如,在这种情况下,您需要:
var serialiser = new ProtobufSerialiser<T>();
var obj = serialiser.Deserialise(request.Body);
你能说明你的格式化程序是如何实际实现的吗?我的意思是,我想知道你到底是如何使用泛型类型参数的。在我当前的知识示例实现中添加了一个非泛型和可编译但不可注册的示例,以说明我们正在做什么-这非常简单(改编自)-效果很好,但并不理想,正如编辑中所解释的。这看起来非常感谢,并证实了我的猜测。我将尝试一下,并在实施后接受这一答案!@tker很想知道这是否带来了明显的性能优势。