ASP.NET Core 使用protobuf

ASP.NET Core 使用protobuf在一些性能要求很高的应用中,使用protocol buffer序列化,优于Json。而且protocol buffer向后兼容的能力比较好。

由于Asp.net core 采用了全新的MiddleWare方式,因此使用protobuf序列化,只需要使用Protobuf-net修饰需要序列化的对象,并在MVC初始化的时候增加相应的Formatter就可以了。

MVC Controller 的Action返回对象时,MVC回根据用户的Request Header里面的MIME选择对应的Formater来序列化返回对象( Serialize returned object)。

MVC具有默认的Json Formater,这个可以不用管。

这里有一个直接可以运行的例子,具有Server和Client代码

https://github.com/damienbod/AspNetMvc6ProtobufFormatters

但是,这里面有一个很严重的问题。 看下面的例子。例如我们需要序列化的对象时ApparatusType,服务端的定义(使用了EntityFramework)是这样的:

using System;

using System.Collections.Generic;

using System.ComponentModel.DataAnnotations;

using ProtoBuf;

namespace Hammergo.Data

{

[ProtoContract]

public partial class ApparatusType

{

public ApparatusType()

{

this.Apps = new List<App>();

}

[ProtoMember(1)]

public System.Guid Id { get; set; }

[ProtoMember(2)]

[MaxLength(20)]

public string TypeName { get; set; }

[ProtoIgnore]

public virtual ICollection<App> Apps { get; set; }

}

}

属于ProtoBuf 的三个修饰为

[ProtoContract]

[ProtoMember(1)]

[ProtoMember(2)]

其他的不用管,在客户端定义是这样的

using System;

using System.Collections.Generic;

using ProtoBuf;

namespace Hammergo.Poco

{

[ProtoContract]

public class ApparatusType

{

[ProtoMember(1)]

public virtual System.Guid Id { get; set; }

[ProtoMember(2)]

public virtual string TypeName { get; set; }

}

}

这里使用了Virtual关键字,是为了生成POCO的代理类,以跟踪状态,没有这个要求可以不使用。

如果使用https://github.com/damienbod/AspNetMvc6ProtobufFormatters 的方案就会发现

如果ASP.NET 的action返回List<AppratusType>,在客户端使用

var result =

response.Content.ReadAsAsync<List<Hammergo.Poco.ApparatusType>>(new[] { new ProtoBufFormatter() }).Result;

就会抛出异常,ReadAsAsync ProtoBuf Formatter No MediaTypeFormatter is available to read

大意是没有 相应的MediaTypeFormatter来供ReadAsAsync来使用,

检查https://github.com/damienbod/AspNetMvc6ProtobufFormatters 的方案,发现它调用了https://github.com/WebApiContrib/WebApiContrib.Formatting.ProtoBuf里面的ProtoBufFormatter.cs ,这个里面有一个错误。

using System;

using System.IO;

using System.Linq;

using System.Net;

using System.Net.Http;

using System.Net.Http.Formatting;

using System.Net.Http.Headers;

using System.Reflection;

using System.Threading.Tasks;

using ProtoBuf;

using ProtoBuf.Meta;

namespace WebApiContrib.Formatting

{

public class ProtoBufFormatter : MediaTypeFormatter

{

private static readonly MediaTypeHeaderValue mediaType = new MediaTypeHeaderValue("application/x-protobuf");

private static Lazy<RuntimeTypeModel> model = new Lazy<RuntimeTypeModel>(CreateTypeModel);

public static RuntimeTypeModel Model

{

get { return model.Value; }

}

public ProtoBufFormatter()

{

SupportedMediaTypes.Add(mediaType);

}

public static MediaTypeHeaderValue DefaultMediaType

{

get { return mediaType; }

}

public override bool CanReadType(Type type)

{

return CanReadTypeCore(type);

}

public override bool CanWriteType(Type type)

{

return CanReadTypeCore(type);

}

public override Task<object> ReadFromStreamAsync(Type type, Stream stream, HttpContent content, IFormatterLogger formatterLogger)

{

var tcs = new TaskCompletionSource<object>();

try

{

object result = Model.Deserialize(stream, null, type);

tcs.SetResult(result);

}

catch (Exception ex)

{

tcs.SetException(ex);

}

return tcs.Task;

}

public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContent content, TransportContext transportContext)

{

var tcs = new TaskCompletionSource<object>();

try

{

Model.Serialize(stream, value);

tcs.SetResult(null);

}

catch (Exception ex)

{

tcs.SetException(ex);

}

return tcs.Task;

}

private static RuntimeTypeModel CreateTypeModel()

{

var typeModel = TypeModel.Create();

typeModel.UseImplicitZeroDefaults = false;

return typeModel;

}

private static bool CanReadTypeCore(Type type)

{

return type.GetCustomAttributes(typeof(ProtoContractAttribute)).Any();

}

}

}

private static bool CanReadTypeCore(Type type)这个有问题,它只能识别有ProtoContract的类,没法识别其对应的IEnumerable<T>,修改这个方法就可以了。如下:

private static bool CanReadTypeCore(Type type)

{

bool isCan = type.GetCustomAttributes(typeof(ProtoContractAttribute)).Any();

if (!isCan && typeof(IEnumerable).IsAssignableFrom(type))

{

var temp = type.GetGenericArguments().FirstOrDefault();

isCan = temp.GetCustomAttributes(typeof(ProtoContractAttribute)).Any();

}

return isCan;

}

下面我给出,关键的代码片段:

使用了一个辅助Library,结构如下图:

DateTimeOffsetSurrogate.cs的代码如下:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Threading.Tasks;

using ProtoBuf;

namespace ProtoBufHelper

{

[ProtoContract]

public class DateTimeOffsetSurrogate

{

[ProtoMember(1)]

public long DateTimeTicks { get; set; }

[ProtoMember(2)]

public short OffsetMinutes { get; set; }

public static implicit operator DateTimeOffsetSurrogate(DateTimeOffset value)

{

return new DateTimeOffsetSurrogate

{

DateTimeTicks = value.Ticks,

OffsetMinutes = (short)value.Offset.TotalMinutes

};

}

public static implicit operator DateTimeOffset(DateTimeOffsetSurrogate value)

{

return new DateTimeOffset(value.DateTimeTicks, TimeSpan.FromMinutes(value.OffsetMinutes));

}

}

}

ProtoBufFormatter.cs 的代码如下:

using System;

using System.Collections;

using System.IO;

using System.Linq;

using System.Net;

using System.Net.Http;

using System.Net.Http.Formatting;

using System.Net.Http.Headers;

using System.Reflection;

using System.Threading.Tasks;

using ProtoBuf;

using ProtoBuf.Meta;

namespace ProtoBufHelper

{

public class ProtoBufFormatter : MediaTypeFormatter

{

private static readonly MediaTypeHeaderValue mediaType = new MediaTypeHeaderValue("application/x-protobuf");

private static Lazy<RuntimeTypeModel> model = new Lazy<RuntimeTypeModel>(CreateTypeModel);

public static RuntimeTypeModel Model

{

get { return model.Value; }

}

public ProtoBufFormatter()

{

SupportedMediaTypes.Add(mediaType);

}

public static MediaTypeHeaderValue DefaultMediaType

{

get { return mediaType; }

}

public override bool CanReadType(Type type)

{

var temp = CanReadTypeCore(type);

return temp;

}

public override bool CanWriteType(Type type)

{

return CanReadTypeCore(type);

}

public override Task<object> ReadFromStreamAsync(Type type, Stream stream, HttpContent content, IFormatterLogger formatterLogger)

{

var tcs = new TaskCompletionSource<object>();

try

{

object result = Model.Deserialize(stream, null, type);

tcs.SetResult(result);

}

catch (Exception ex)

{

tcs.SetException(ex);

}

return tcs.Task;

}

public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContent content, TransportContext transportContext)

{

var tcs = new TaskCompletionSource<object>();

try

{

Model.Serialize(stream, value);

tcs.SetResult(null);

}

catch (Exception ex)

{

tcs.SetException(ex);

}

return tcs.Task;

}

private static RuntimeTypeModel CreateTypeModel()

{

var typeModel = TypeModel.Create();

typeModel.UseImplicitZeroDefaults = false;

typeModel.Add(typeof(DateTimeOffset), false).SetSurrogate(typeof(DateTimeOffsetSurrogate));

return typeModel;

}

private static bool CanReadTypeCore(Type type)

{

bool isCan = type.GetCustomAttributes(typeof(ProtoContractAttribute)).Any();

if (!isCan && typeof(IEnumerable).IsAssignableFrom(type))

{

var temp = type.GetGenericArguments().FirstOrDefault();

isCan = temp.GetCustomAttributes(typeof(ProtoContractAttribute)).Any();

}

return isCan;

}

}

}

这样就可以设置ASP.NET Core端的代码:

添加ProtobufInputFormatter.cs 和 ProtobufOutputFormatter.cs 代码分别如下:

using System;

using System.Threading.Tasks;

using Microsoft.AspNetCore.Mvc;

using Microsoft.AspNetCore.Mvc.Formatters;

using Microsoft.Net.Http.Headers;

using ProtoBuf.Meta;

using ProtoBufHelper;

namespace DamService

{

public class ProtobufInputFormatter : InputFormatter

{

private static Lazy<RuntimeTypeModel> model = new Lazy<RuntimeTypeModel>(CreateTypeModel);

public static RuntimeTypeModel Model

{

get { return model.Value; }

}

public override Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)

{

var type = context.ModelType;

var request = context.HttpContext.Request;

MediaTypeHeaderValue requestContentType = null;

MediaTypeHeaderValue.TryParse(request.ContentType, out requestContentType);

object result = Model.Deserialize(context.HttpContext.Request.Body, null, type);

return InputFormatterResult.SuccessAsync(result);

}

public override bool CanRead(InputFormatterContext context)

{

return true;

}

private static RuntimeTypeModel CreateTypeModel()

{

var typeModel = TypeModel.Create();

typeModel.UseImplicitZeroDefaults = false;

typeModel.Add(typeof(DateTimeOffset), false).SetSurrogate(typeof(DateTimeOffsetSurrogate));

return typeModel;

}

}

}

using System;

using System.Text;

using System.Threading.Tasks;

using Microsoft.AspNetCore.Mvc.Formatters;

using Microsoft.Net.Http.Headers;

using ProtoBuf.Meta;

using ProtoBufHelper;

namespace DamService

{

public class ProtobufOutputFormatter : OutputFormatter

{

private static Lazy<RuntimeTypeModel> model = new Lazy<RuntimeTypeModel>(CreateTypeModel);

public string ContentType { get; private set; }

public static RuntimeTypeModel Model

{

get { return model.Value; }

}

public ProtobufOutputFormatter()

{

ContentType = "application/x-protobuf";

SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/x-protobuf"));

//SupportedEncodings.Add(Encoding.GetEncoding("utf-8"));

}

private static RuntimeTypeModel CreateTypeModel()

{

var typeModel = TypeModel.Create();

typeModel.UseImplicitZeroDefaults = false;

typeModel.Add(typeof(DateTimeOffset), false).SetSurrogate(typeof(DateTimeOffsetSurrogate));

return typeModel;

}

public override Task WriteResponseBodyAsync(OutputFormatterWriteContext context)

{

var response = context.HttpContext.Response;

Model.Serialize(response.Body, context.Object);

return Task.FromResult(response);

}

}

}

在Startup.cs中

稿源:勤快学QKXue.NET
扩展阅读:
ASP.NET Core 使用protobuf(上)
http://qkxue.net/info/22913/ASP-NET-Core-protobuf
ASP.NET Core 使用protobuf(下)
http://qkxue.net/info/22914/ASP-NET-Core-protobuf

时间: 2024-12-19 08:30:24

ASP.NET Core 使用protobuf的相关文章

插上腾飞的翅膀:为asp.net core添加protobuf支持

没时间解释了,快上车. 通过NuGet获取Zaabee.AspNetCoreProtobuf Install-Package Zaabee.AspNetCoreProtobuf 在Startup.cs文件中修改ConfigureServices方法 public void ConfigureServices(IServiceCollection services) { services.AddMvc(options => { options.AddProtobufSupport(); }); }

asp.net core 使用 Redis 和 Protobuf

asp.net core 使用 Redis 和 Protobuf 前言 上篇博文介绍了怎么样在 asp.net core 中使用中间件,以及如何自定义中间件.项目中刚好也用到了Redis,所以本篇就介绍下怎么样在 asp.net core 中使用 Redis 进行资源缓存和Session缓存. 如果你觉得对你有帮助的话,不妨点个[推荐]. 目录 Redis 介绍 asp.net core Session 介绍 Redis & Session 实例讲解 Session的使用 使用 Protobuf

ASP.NET Core 缓存技术 及 Nginx 缓存配置

前言 在Asp.Net Core Nginx部署一文中,主要是讲述的如何利用Nginx来实现应用程序的部署,使用Nginx来部署主要有两大好处,第一是利用Nginx的负载均衡功能,第二是使用Nginx的反向代理来降低我们后端应用程序的压力.那除了以上两点之外,其实我们还可以利用代理服务器的缓存功能来进一步的降低后端应用程序的压力,提升系统的吞吐量(tps).这一篇就来看一下具体应该如何去做吧. 目录 WEB 缓存 ASP.NET Core 缓存 内存缓存 分布式缓存 Response 缓存 Ng

ASP.NET Core 文件上传

小分享:我有几张阿里云优惠券,用券购买或者升级阿里云相应产品最多可以优惠五折!领券地址:https://promotion.aliyun.com/ntms/act/ambassador/sharetouser.html?userCode=ohmepe03 前言 上篇博文介绍了怎么样在 asp.net core 使用 Redis 和 Protobuf 进行 Session缓存.本篇的是开发过程中使用的一个小功能,怎么做单文件和多文件上传. 如果你觉得对你有帮助的话,不妨点个[推荐]. 目录 单文件

微服务介绍及Asp.net Core实战项目系列之微服务介绍

0.目录 整体架构目录:ASP.NET Core分布式项目实战-目录 一.微服务选型 在做微服务架构的技术选型的时候,以"无侵入"和"社区活跃"为主要的考量点,将来升级为原子服务架构.量子服务架构的时候.甚至恢复成单体架构的时候,代价最小. 软件开发只需要组装,不再需要从头开发. 选型可以参考一下张队长的文章:https://mp.weixin.qq.com/s/UIFjm7W6bDfdmjpeMVJtqA 二.微服务架构是什么? 每一个微服务都是一个零件,并使用这

【分享】Asp.net Core相关教程及开源项目

入门 全新的ASP.NET:  https://www.cnblogs.com/Leo_wl/p/5654828.html 在IIS上部署你的ASP.NET Core项目: https://www.cnblogs.com/wangjieguang/p/core-iis.html 举个例子来聊聊它的依赖注入: http://www.bkjia.com/Asp_Netjc/1309630.html, http://www.cnblogs.com/jesse2013/p/di-in-aspnetcor

asp.net core 3.0 gRPC框架小试

什么是gRPC gRPC是google开源的一个高性能.跨语言的RPC框架,基于HTTP2协议,采用ProtoBuf 定义的IDL. gRPC 的主要优点是: 现代高性能轻量级 RPC 框架. 协定优先 API 开发,默认使用协议缓冲区,允许与语言无关的实现. 可用于多种语言的工具,以生成强类型服务器和客户端. 支持客户端.服务器和双向流式处理调用. 使用 Protobuf 二进制序列化减少对网络的使用. 这些优点使 gRPC 适用于: 效率至关重要的轻量级微服务. 需要多种语言用于开发的 Po

ASP.NET Core Web API

1.简单介绍 ASP.NET Core Web API 是 ASP.NET Core MVC 的一个功能.ASP.NET Core MVC 包含了对 Web API 的支持.可以构建多种客户端的 HTTP 服务.ASP.NET Core Web API可用于在 .NET Core 上构建 RESTful 应用程序. 框架包含对 HTTP 内容协商的支持,内置支持以 JSON 或 XML 格式化的数据.编写自定义格式化程序已添加对自有格式的支持. 使用链接生成对超媒体的支持.启用对跨资源共享(CO

.NET Core 3.0及ASP.NET Core 3.0 前瞻

前几天微软发布了 .NET Core 3.0 Preview 9 ,这是.NET Core 3.0 最后一个预览版. .NET Core 3.0 正式发布将在.NET Conf 上发布,.NET Conf 时间是9月23日至25日. Visual Studio 2019 16.3预览版3和Visual Studio for Mac 8.3支持.NET Core 3.0 ,这些版本也同时发布. 从.NET Core 3.0 Preview 7就可用于生产,目前dotnet官网就是使用 https: