Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Poznámka:
Toto není nejnovější verze tohoto článku. Aktuální verzi najdete ve verzi .NET 10 tohoto článku.
Upozorňující
Tato verze ASP.NET Core se už nepodporuje. Další informace najdete v zásadách podpory .NET a .NET Core. Aktuální verzi najdete v tomto článku ve verzi .NET 9.
James Newton-King a Mark Rendle
gRPC používá Protobuf jako svůj jazyk IDL (Interface Definition Language). Protobuf IDL je jazykově neutrální formát pro určení zpráv odeslaných a přijatých službami gRPC. Zprávy protobuf jsou definovány v .proto souborech. Tento dokument vysvětluje, jak se koncepty Protobuf mapují na .NET.
Zprávy ve formátu protobuf
Zprávy jsou hlavním objektem přenosu dat v Protobuf. Jsou koncepčně podobné třídám .NET.
syntax = "proto3";
option csharp_namespace = "Contoso.Messages";
message Person {
int32 id = 1;
string first_name = 2;
string last_name = 3;
}
Předchozí definice zprávy určuje tři pole jako páry název-hodnota. Podobně jako vlastnosti u typů .NET má každé pole název a typ. Typ pole může být skalárním typemzprávou.
Průvodce stylem Protobuf doporučuje použít underscore_separated_names názvy polí. Nové zprávy Protobuf vytvořené pro aplikace .NET by měly postupovat podle pokynů pro styl Protobuf. Nástroje .NET automaticky generují typy .NET, které používají standardy pojmenování .NET. Například first_name pole Protobuf generuje FirstName vlastnost .NET.
Kromě názvu má každé pole v definici zprávy jedinečné číslo. Čísla polí se používají k identifikaci polí při serializaci zprávy do Protobuf. Serializace malého čísla je rychlejší než serializace celého názvu pole. Vzhledem k tomu, že čísla polí identifikují pole, je důležité se při jejich změně postarat. Další informace o změně zpráv Protobuf naleznete v tématu Služby správy verzí gRPC.
Když je aplikace sestavena nástroj Protobuf generuje typy .NET ze .proto souborů. Zpráva Person vygeneruje třídu .NET:
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Další informace o zprávách Protobuf naleznete v průvodci jazykem Protobuf.
Skalární typy hodnot
Protobuf podporuje rozsah nativních skalárních hodnot. V následující tabulce jsou uvedeny všechny s odpovídajícím typem jazyka C#:
| Protobuf – typ | Typ jazyka C# |
|---|---|
double |
double |
float |
float |
int32 |
int |
int64 |
long |
uint32 |
uint |
uint64 |
ulong |
sint32 |
int |
sint64 |
long |
fixed32 |
uint |
fixed64 |
ulong |
sfixed32 |
int |
sfixed64 |
long |
bool |
bool |
string |
string |
bytes |
ByteString |
Skalární hodnoty mají vždy výchozí hodnotu a nelze je nastavit na null. Toto omezení zahrnuje string a ByteString které jsou třídy jazyka C#.
string výchozí hodnota prázdné řetězcové hodnoty a ByteString výchozí hodnota je prázdná bajtová hodnota. Pokus o nastavení null vyvolá chybu.
K podpoře hodnot null lze použít typy obálky s možnou hodnotou Null.
Datové typy pro datum a čas
Nativní skalární typy neposkytují hodnoty data a času, které jsou ekvivalentní hodnotě . NET , DateTimeOffsetDateTimea TimeSpan. Tyto typy lze určit pomocí některých rozšíření Protobuf Well-Known Types . Tato rozšíření poskytují podporu generování kódu a modulu runtime pro komplexní typy polí napříč podporovanými platformami.
Následující tabulka uvádí typy data a času:
| Typ .NET | Známý typ Protobuf |
|---|---|
DateTimeOffset |
google.protobuf.Timestamp |
DateTime |
google.protobuf.Timestamp |
TimeSpan |
google.protobuf.Duration |
syntax = "proto3";
import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";
message Meeting {
string subject = 1;
google.protobuf.Timestamp start = 2;
google.protobuf.Duration duration = 3;
}
Vygenerované vlastnosti ve třídě C# nejsou typy data a času rozhraní .NET. Vlastnosti používají třídy TimestampDuration v Google.Protobuf.WellKnownTypes oboru názvů. Tyto třídy poskytují metody pro převod na a z DateTimeOffset, DateTimea TimeSpan.
// Create Timestamp and Duration from .NET DateTimeOffset and TimeSpan.
var meeting = new Meeting
{
Time = Timestamp.FromDateTimeOffset(meetingTime), // also FromDateTime()
Duration = Duration.FromTimeSpan(meetingLength)
};
// Convert Timestamp and Duration to .NET DateTimeOffset and TimeSpan.
var time = meeting.Time.ToDateTimeOffset();
var duration = meeting.Duration?.ToTimeSpan();
Poznámka:
Typ Timestamp funguje s časy UTC.
DateTimeOffset hodnoty vždy mají posun nuly a DateTime.Kind vlastnost je vždy DateTimeKind.Utc.
Typy s povolenou hodnotou Null
Generování kódu Protobuf pro jazyk C# používá nativní typy, například int pro int32. Proto jsou hodnoty vždy zahrnuté a nemohou být null.
Pro hodnoty, které vyžadují explicitní null, například použití int? v kódu jazyka C#, Protobuf dobře známé typy obsahují obálky, které jsou kompilovány do typů C# s možnou hodnotou null. Pokud je chcete použít, naimportujte wrappers.proto je do souboru .proto , například do následujícího kódu:
syntax = "proto3";
import "google/protobuf/wrappers.proto";
message Person {
// ...
google.protobuf.Int32Value age = 5;
}
wrappers.proto typy nejsou vystaveny ve generovaných vlastnostech. Protobuf je automaticky mapuje na odpovídající typy s možnou hodnotou null .NET ve zprávách jazyka C#. Například google.protobuf.Int32Value pole generuje int? vlastnost. Vlastnosti typu odkazu jako string a ByteString beze změny se dají přiřadit s výjimkou null případů bez chyby.
Následující tabulka uvádí úplný seznam typů obálky s odpovídajícím typem jazyka C#:
| Typ jazyka C# | Dobře známá obálka typu |
|---|---|
bool? |
google.protobuf.BoolValue |
double? |
google.protobuf.DoubleValue |
float? |
google.protobuf.FloatValue |
int? |
google.protobuf.Int32Value |
long? |
google.protobuf.Int64Value |
uint? |
google.protobuf.UInt32Value |
ulong? |
google.protobuf.UInt64Value |
string |
google.protobuf.StringValue |
ByteString |
google.protobuf.BytesValue |
Přijaté
Binární datové části jsou podporovány v Protobuf s typem bytes skalární hodnoty. Vygenerovaná vlastnost v jazyce C# se používá ByteString jako typ vlastnosti.
Slouží ByteString.CopyFrom(byte[] data) k vytvoření nové instance z pole bajtů:
var data = await File.ReadAllBytesAsync(path);
var payload = new PayloadResponse();
payload.Data = ByteString.CopyFrom(data);
ByteString k datům se přistupuje přímo pomocí ByteString.Span nebo ByteString.Memory. Nebo volání ByteString.ToByteArray() převodu instance zpět na bajtové pole:
var payload = await client.GetPayload(new PayloadRequest());
await File.WriteAllBytesAsync(path, payload.Data.ToByteArray());
Desetinná místa
Protobuf nativně nepodporuje typ .NET decimal , pouze double a float. V projektu Protobuf probíhá diskuze o možnosti přidání standardního desítkového typu do známých typů s podporou platformy pro jazyky a architektury, které ho podporují. Zatím se nic neimplementovalo.
Je možné vytvořit definici zprávy, která představuje decimal typ, který funguje pro bezpečné serializace mezi klienty .NET a servery. Vývojáři na jiných platformách by ale museli porozumět použitému formátu a implementovat pro něj vlastní zpracování.
Vytvoření vlastního desítkového typu pro Protobuf
package CustomTypes;
// Example: 12345.6789 -> { units = 12345, nanos = 678900000 }
message DecimalValue {
// Whole units part of the amount
int64 units = 1;
// Nano units of the amount (10^-9)
// Must be same sign as units
sfixed32 nanos = 2;
}
Pole nanos představuje hodnoty od 0.999_999_999 do -0.999_999_999. Například decimal hodnota 1.5m by byla reprezentována jako { units = 1, nanos = 500_000_000 }. Toto je důvod, proč nanos pole v tomto příkladu sfixed32 používá typ, který kóduje efektivněji než int32 pro větší hodnoty.
units Pokud je pole záporné, nanos mělo by být také záporné.
Poznámka:
Další algoritmy jsou k dispozici pro kódování decimal hodnot jako řetězce bajtů. Algoritmus používaný:DecimalValue
- Je snadno pochopitelný.
- Není ovlivněný big-endianem nebo malým endianem na různých platformách.
- Podporuje desetinná čísla od kladných
9,223,372,036,854,775,807.999999999po záporná9,223,372,036,854,775,808.999999999s maximální přesností na devět desetinných míst, což není úplný rozsahdecimal.
Převod mezi tímto typem a typem seznamu BCL decimal může být implementován v jazyce C#takto:
namespace CustomTypes
{
public partial class DecimalValue
{
private const decimal NanoFactor = 1_000_000_000;
public DecimalValue(long units, int nanos)
{
Units = units;
Nanos = nanos;
}
public static implicit operator decimal(CustomTypes.DecimalValue grpcDecimal)
{
return grpcDecimal.Units + grpcDecimal.Nanos / NanoFactor;
}
public static implicit operator CustomTypes.DecimalValue(decimal value)
{
var units = decimal.ToInt64(value);
var nanos = decimal.ToInt32((value - units) * NanoFactor);
return new CustomTypes.DecimalValue(units, nanos);
}
}
}
Předchozí kód:
- Přidá částečnou třídu pro
DecimalValue. Částečná třída se zkombinuje sDecimalValuevygenerovaným ze.protosouboru. Vygenerovaná třída deklarujeUnitsaNanosvlastnosti. - Obsahuje implicitní operátory pro převod mezi
DecimalValuea typem seznamu BCLdecimal.
Kolekce
Seznamy
Seznamy v Protobuf jsou určeny pomocí klíčového repeated slova předpony v poli. Následující příklad ukazuje, jak vytvořit seznam:
message Person {
// ...
repeated string roles = 8;
}
Ve vygenerovaném kódu repeated jsou pole reprezentována obecným typem Google.Protobuf.Collections.RepeatedField<T> .
public class Person
{
// ...
public RepeatedField<string> Roles { get; }
}
RepeatedField<T> implementuje IList<T>. Proto můžete použít dotazy LINQ nebo je převést na pole nebo seznam.
RepeatedField<T> vlastnosti nemají veřejnou setter. Položky by se měly přidat do existující kolekce.
var person = new Person();
// Add one item.
person.Roles.Add("user");
// Add all items from another collection.
var roles = new [] { "admin", "manager" };
person.Roles.Add(roles);
Slovníky
Typ .NET IDictionary<TKey,TValue> je reprezentován v Protobuf pomocí map<key_type, value_type>.
message Person {
// ...
map<string, string> attributes = 9;
}
Ve vygenerovaném kódu map .NET jsou pole reprezentována obecným typem Google.Protobuf.Collections.MapField<TKey, TValue> .
MapField<TKey, TValue> implementuje IDictionary<TKey,TValue>. Vlastnosti, jako repeated jsou vlastnosti, map nemají veřejnou setter. Položky by se měly přidat do existující kolekce.
var person = new Person();
// Add one item.
person.Attributes["created_by"] = "James";
// Add all items from another collection.
var attributes = new Dictionary<string, string>
{
["last_modified"] = DateTime.UtcNow.ToString()
};
person.Attributes.Add(attributes);
Nestrukturované a podmíněné zprávy
Protobuf je formát zasílání zpráv na základě kontraktu. Zprávy aplikace, včetně polí a typů, musí být při vytváření aplikace zadány v .proto souborech. Návrh prvního kontraktu protobuf je skvělý při vynucování obsahu zpráv, ale může omezit scénáře, kdy není vyžadován striktní kontrakt:
- Zprávy s neznámými datovými částmi Například zpráva s polem, které může obsahovat jakoukoli zprávu.
- Podmíněné zprávy. Například zpráva vrácená ze služby gRPC může být výsledkem úspěchu nebo chybovým výsledkem.
- Dynamické hodnoty. Například zpráva s polem, které obsahuje nestrukturovanou kolekci hodnot, podobně jako JSON.
Protobuf nabízí jazykové funkce a typy pro podporu těchto scénářů.
Všechny
Tento Any typ umožňuje používat zprávy jako vložené typy bez jejich .proto definice. Chcete-li použít Any typ, importujte any.proto.
import "google/protobuf/any.proto";
message Status {
string message = 1;
google.protobuf.Any detail = 2;
}
// Create a status with a Person message set to detail.
var status = new ErrorStatus();
status.Detail = Any.Pack(new Person { FirstName = "James" });
// Read Person message from detail.
if (status.Detail.Is(Person.Descriptor))
{
var person = status.Detail.Unpack<Person>();
// ...
}
Oneof
oneof pole jsou funkce jazyka. Kompilátor zpracovává oneof klíčové slovo při generování třídy zprávy. Použití oneof k zadání zprávy odpovědi, která by mohla vrátit Person odpověď nebo Error může vypadat takto:
message Person {
// ...
}
message Error {
// ...
}
message ResponseMessage {
oneof result {
Error error = 1;
Person person = 2;
}
}
Pole v oneof sadě musí mít jedinečná čísla polí v celkové deklaraci zprávy.
Při použití oneofobsahuje vygenerovaný kód jazyka C# výčet, který určuje, která pole byla nastavena. Výčet můžete otestovat a zjistit, které pole je nastavené. Pole, která nejsou nastavena jako návratová null nebo výchozí hodnota, místo vyvolání výjimky.
var response = await client.GetPersonAsync(new RequestMessage());
switch (response.ResultCase)
{
case ResponseMessage.ResultOneofCase.Person:
HandlePerson(response.Person);
break;
case ResponseMessage.ResultOneofCase.Error:
HandleError(response.Error);
break;
default:
throw new ArgumentException("Unexpected result.");
}
Hodnota
Typ Value představuje dynamicky zadaná hodnota. Může to být buď null, číslo, řetězec, logická hodnota, slovník hodnot (Struct) nebo seznam hodnot (ValueList).
Value je známý typ Protobuf, který používá dříve diskutovanou oneof funkci. Chcete-li použít Value typ, importujte struct.proto.
import "google/protobuf/struct.proto";
message Status {
// ...
google.protobuf.Value data = 3;
}
// Create dynamic values.
var status = new Status();
status.Data = Value.ForStruct(new Struct
{
Fields =
{
["enabled"] = Value.ForBool(true),
["metadata"] = Value.ForList(
Value.ForString("value1"),
Value.ForString("value2"))
}
});
// Read dynamic values.
switch (status.Data.KindCase)
{
case Value.KindOneofCase.StructValue:
foreach (var field in status.Data.StructValue.Fields)
{
// Read struct fields...
}
break;
// ...
}
Použití Value přímo může být podrobné. Alternativní způsob použití Value je s integrovanou podporou Protobuf pro mapování zpráv na JSON. Protobuf JsonFormatter a JsonWriter typy lze použít s libovolnou zprávou Protobuf.
Value je zvláště vhodný pro převod do a z JSON.
Toto je ekvivalent JSON předchozího kódu:
// Create dynamic values from JSON.
var status = new Status();
status.Data = Value.Parser.ParseJson(@"{
""enabled"": true,
""metadata"": [ ""value1"", ""value2"" ]
}");
// Convert dynamic values to JSON.
// JSON can be read with a library like System.Text.Json or Newtonsoft.Json
var json = JsonFormatter.Default.Format(status.Data);
var document = JsonDocument.Parse(json);