A Young Person’s Guide to C# Bond
About
Bond is an extensible framework for working with schematized data. It is suitable for scenarios ranging from service communications to Big Data storage and processing.
Bond defines a rich type system and schema versioning rules which allow forward and backward compatibility. The core Bond features include high performance serialization/deserialization and a very powerful, generic data transform mechanism. The framework is highly extensible via pluggable serialization protocols, data streams, user defined type aliases and more.
By design Bond is language and platform independent and is currently supported for C++, C#, and Python on Linux, OS X and Windows.
We are also introducing the Bond Communications framework–known as Bond Comm. More information about Bond Comm for C# can be found below.
Bond is published on GitHub at https://github.com/Microsoft/bond/.
Basic example
In Bond data schemas are defined using idl-like syntax:
1 2 3 4 5 6 7 | |
In order to use the schema in a C# program, it needs to be compiled using the Bond compiler. This step is sometimes also referred to as code generation (or codegen) because the compilation generates C# code corresponding to the schema definition.
1 | |
Using the generated C# code, we can write a simple program that will serialize and deserialize an instance of the Record schema using Compact Binary protocol:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | |
Code generation
In order to use a Bond schema in a C# program, it needs to be compiled using the Bond compiler gbc. The compiler generates C# classes that represent the schema. By default schema fields are represented by public auto-properties initialized in the default constructor.
The mapping between Bond and C# type systems is mostly obvious but it is worth noting that unlike C# reference types, Bond types are not nullable. This means, for a example, that while string in Bond IDL will be mapped to C# string, which is a reference type, the value null will not be valid. In order to allow null value a types must be declared as nullable, e.g.:
1 2 3 4 | |
The value null is also legal for fields declared in Bond IDL to have a default of nothing, e.g.:
1 2 3 4 | |
Caveat: blob, nullable<blob> and blob = nothing are all represented as an ArraySegment<byte> in the generated C# code. The default value for all three is default(ArraySegment<byte>) (in which the Array field is null). An empty ArraySegment<byte> (in which the Array field is not null but the Count is 0) is treated as a non-default value, so it will not be omitted for optional fields. This behavior will be changing in a future release, to align it with how other types are handled and how nullable/nothing fields are handled in other languages.
Code generation can be customized by passing one or more of the following command line options to gbc:
--fields
Schema fields are represented by public fields with initializers and no constructor is generated.
--readonly-properties
Schema fields are represented by properties with public getter and private setter and initialized to the default values in the default constructor. Classes with read-only properties are fully supported by all Bond APIs.
--collection-interfaces
Collection types vector<T>, map<K, V>, list<T> and set<T> are represented by respective generic collection interfaces: IList<T>, IDictionary<K, V>, ICollection<T> and ISet<T>.
Serializer
Bond serialization API is provided by the Serializer class. It is a generic class parameterized with type of protocol writer used for serialization:
1 | |
The constructor of the Serializer class takes the type of a class or struct representing Bond schema:
1 | |
The constructor is non-trivial so application usually should create an instance of Serializer outside of the inner loop and reuse it.
The Serializer class exposes one public method Serialize which takes two arguments, an object to be serialized and an instance of the protocol writer to be used for serialization.
1 | |
The object’s type must be the same as the type passed to the Serializer constructor, otherwise the behaviour is undefined.
Bond provides a helper static API for applications that use schema types known at compile-time and don’t need to manage lifetime of the Serializer:
1 | |
When the API is called for the first time, a static instance of appropriate Serializer is created. Because of this the first call to the API for given type and protocol may take relatively long time. Subsequent calls for the same writer/object types reuse the static instance.
Deserializer
Bond serialization API is provided by the Deserializer class. It is a generic class parameterized with type of the protocol reader to be used for deserialization:
1 | |
The constructor of the Deserializer class takes the type of a class or struct representing Bond schema:
1 | |
The constructor is non-trivial so application usually should create an instance of Deserializer outside of the inner loop and reuse it.
The Deserializer class exposes one generic public method Deserialize which takes as an argument an instance of the protocol reader and returns a deserialized object:
1 | |
The object created by Deserialize is always of the type specified during Deserializer construction. The type parameter of the method is only used to cast the result.
Deserializing from payload encoded in an untagged protocol like Simple usually requires specifying schema of the payload. To address this scenario the Deserializer class has an additional constructor taking a RuntimeSchema as an argument:
1 2 3 4 | |
An instance of Deserializer created this way is tied to the triplet of protocol, object type and payload schema. In order to deserialize from payload in another schema a new instance of Deserializer needs to be created.
Bond provides a helper static API for applications that use schema types known at compile-time, don’t need to specify payload schema and don’t need to manage lifetime of the Deserializer:
1 | |
When an application calls this API for the first time, a static instance of appropriate Deserializer is created. Because of this the first call to the API for given type and protocol may take relatively long time. Subsequent calls for the same reader/object types reuse the static instance.
See also the following example:
examples/cs/core/untagged_protocols
Marshaling
Since Bond supports multiple serialization protocols, application endpoints either have to agree on a particular protocol, or include protocol metadata in the payload. Marshaling APIs provide the standard way to do the latter, by automatically adding a payload header with the protocol identifier and version.
Marshal and Unmarshal APIs are very similar to Serialize and Deserialize, except that when calling Unmarshal the application simply provides an input stream with payload data, rather than an instance of a particular protocol reader:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
See also the following example:
examples/cs/core/marshaling
Transcoder
The Transcoder class provides API for converting payloads from one protocol into another. Transcoder operates directly on serialized data and doesn’t need to know the C# type representing payload schema.
The Transcoder is a generic class parameterized with types of source protocol reader and destination protocol writer, e.g.:
1 | |
The constructor of the Transcoder class takes as an optional argument the runtime schema of the payload. The argument is optional when transcoding between tagged protocols but must be specified when transcoding from an untagged protocol or to a text protocol.
1 2 3 | |
The Transcoder constructor is non-trivial so application usually should create an instance of Transcoder outside of the inner loop and reuse it.
The Transcoder class exposes one public method Transcode which takes two arguments, protocol reader representing payload to transcode from, and protocol writer to be used to write the result.
1 | |
Bond provides a static helper API for application that don’t need to explicitly manage Transcoder lifetime and don’t specify payload schema or use a schema known at compile-time:
1 2 3 4 5 | |
When an application calls this API for the first time, a static instance of appropriate Transcoder is created. Because of this the first call to the API for given type/schema and protocol may take relatively long time. Subsequent calls for the same reader/writer types reuse the static instance. Note that the static API can’t be used if source protocol is untagged and will result in runtime exception.
See also the following example:
examples/cs/core/protocol_transcoding
Input and output streams
The input and output for binary protocols is provided by IInputStream and IOutputStream interfaces. Bond comes with standard implementations of these interfaces for memory buffers and System.IO.Stream, and applications can provide their own custom implementations.
The OutputBuffer class implements IOutputStream interface on top of a memory buffer. It comes in two variants. Bond.IO.Safe.OutputBuffer uses only safe managed code and is included in Bond.dll assembly which is compatible with Portable Class Library. Bond.IO.Unsafe.OutputBuffer uses unsafe code to optimize for performance. It is included in Bond.IO.dll assembly which requires full .NET runtime. Both implementations have identical class names and APIs, the only difference is the namespace in which they are defined.
1 2 3 4 5 6 7 8 | |
The InputBuffer class implements IInputStream interface on top of a memory buffer. Like OutputBuffer it comes in two flavors, the safe and portable Bond.IO.Safe.OutputBuffer, and the performance optimized via use of unsafe code Bond.IO.Unsafe.OutputBuffer.
1 2 3 | |
The InputStream and OutputStream classes provide implementations of IInputStream and IOutputStream on top of System.IO.Stream. These classes are included in Bond.IO.dll and thus are only available to applications using full .NET runtime and allowing unsafe code. In/OutputStream can be used with any Stream, including MemoryStream, however aforementioned the In/OutputBuffer provide significantly better performance and are recommended when working with in-memory payloads.
1 2 3 4 5 6 | |
Cloner
The Cloner class provides API for deep cloning of objects representing Bond schemas. Unlike typical cloning, Bond Cloner is not limited to making clones that are of the same type as the source object. The type of the source and the clone merely need to represent compatible Bond schemas.
The Cloner is a generic class parameterized with the source type and its constructor takes one argument representing the type of clones to be created e.g.:
1 | |
The constructor is non-trivial so application usually should create an instance of Cloner outside of the inner loop and reuse it.
The Cloner exposes one public, generic method Clone which takes as the argument the source object and returns a clone:
1 | |
The object created by Clone is always of the type specified during Cloner construction. The type parameter of the method is only used to cast the result.
Bond provides a helper static API which creates and caches appropriate instance of Cloner the first time it is used, e.g.:
1 2 | |
See also the following example:
examples/cs/core/cloning
Comparer
The Comparer class provides API for deep comparison for equality of objects representing Bond schemas. The class exposes one public, static, generic method Equal which takes two parameters representing objects to be compared.
1 2 3 4 | |
Note that the Comparer doesn’t compare arbitrary C# objects, it compares instances of Bond schemas. Only fields/properties decorated with Bond attributes and base classes/interface representing Bond schemas are considered during comparison.
Performance
Bond offers very fast serialization and deserialization. Here are some tips on how to achieve the best performance.
Explicitly create instances of
Serializer/Deserializer/TranscoderInstead of using simplified APIs like
Serialize.ToandDeserialize<T>.Fromit is usually better to explicitly instantiate and cache appropriateSerializer/Deserializer/Transcoderobjects. Creation of these objects involves generation and JIT’ing of specific code to handle the particular operation for a given schema type and protocol(s). This may take a relatively long time, especially for large schemas, and usually it is best to do it during program initialization. Once the object is created it can be reused repeatedly and calling the Serialize/Deserialize/Transcode methods will be very fast.1 2 3 4 5 6 7 8 9 10 11 12
var exampleSerializer = new Serializer<CompactBinaryWriter<OutputBuffer>>(typeof(Example)); var exampleDeserializer = new Deserializer<CompactBinaryReader<InputBuffer>>(typeof(Example)); var output = new OutputBuffer(); var writer = new CompactBinaryWriter<OutputBuffer>(output); exampleSerializer.Serialize(src, writer); var input = new InputBuffer(output.Data); var reader = new CompactBinaryReader<InputBuffer>(input); var dst = exampleDeserializer.Deserialize<Example>(reader);Note that the type of
Serializer/Deserializerdoesn’t depend on the schema type so it is easy to cache these objects for multiple schemas used in an application:1 2 3 4 5 6 7 8 9 10 11
var serializerCache = new Dictionary<Type, Serializer<CompactBinaryWriter<OutputBuffer>>> { { typeof(Foo), new Serializer<CompactBinaryWriter<OutputBuffer>>(typeof(Foo)) }, { typeof(Bar), new Serializer<CompactBinaryWriter<OutputBuffer>>(typeof(Bar)) } };Prefer
InputBufferandOutputBufferoverMemoryStreamWhen working with Bond payloads in a memory buffer (
byte[]orArraySegment<byte>) Bond-definedInputBufferandOutputBufferclasses will provide significantly better performance thanInputStreamandOutputStreamused together withSystem.MemoryStream.OutputBufferby default preallocates 64 KB of memory. When serializing small objects the cost of allocating and zeroing the memory may dominate the actual cost of serialization. Conversely, when serializing very large objects the initial buffer of 64KB may be too small, leading to unnecessary reallocations and memory copying.The
OutputBufferconstructor accepts an argument specifying the size of initial buffer in bytes. For optimal performance the size should be set to be a little bigger than expect size of serialized data.Prefer
using Bond.IO.Unsafe;overusing Bond.IO.Safe;Bond defines two variants of
InputBufferandOutputBufferin two namespacesBond.IO.SafeandBond.IO.Unsafe. The classes have identical interface and can be used interchangeably. The only difference is that the latter uses some low level memory access constructs and is implemented inBond.IO.dllassembly which is compiled with/unsafeflag. The unsafe version is faster.Pool memory buffers
Creating a new
InputBuffer/OutputBufferevery time may be more costly than the actual serialization or deserialization and it increases GC pressure. Whenever possible pool and reuse buffers, simply resetting their position after or before use:buffer.Position = 0;Choose the right protocol
The Fast Binary protocol is a little faster than Compact Binary, although the difference is not big. Untagged protocols like Simple Binary can provide much better performance (up to 4 times faster for some schemas). They are most applicable in scenarios where runtime schema of the data is available during deserialization and the same schema applies to many instances of the data, so that the cost of creating the
Deserializercan be amortized. The canonical use case for an untagged protocol is record-based data storage.Using .NET 4.5 will give better performance than 4.0.
Runtime schema
Some generic applications may need to work with Bond schemas unknown at compile-time. In order to address such scenarios Bond defines a type SchemaDef to represent schemas at runtime. Applications can obtain an instance of SchemaDef for a particular type using the Schema class:
1 2 3 4 5 | |
The APIs return an object of type RuntimeSchema, which is a thin wrapper over SchemaDef. Access to underlying schema is provided via public properties:
1 | |
The SchemaDef object is always self contained, including the runtime schema definitions for all nested types (if any). The RuntimeSchema class instance can be constructed from a SchemaDef, and then it represents the whole schema, or from any embedded TypeDef:
1 2 3 4 5 | |
SchemaDef is a Bond type, defined in bond.bond, and as such can be de/serialized like any other Bond type:
1 | |
A serialized representation of SchemaDef can be also obtained directly from a schema definition IDL file using bond compiler.
See also the following example:
examples/cs/core/runtime_schema
Understanding bonded<T>
The generic type bonded<T> is a simple yet powerful abstraction which is a fundamental part of Bond APIs and enables such usage scenarios as lazy deserialization, pass-through and polymorphism.
In C# bonded<T> maps to IBonded<T> interface which supports three operations: Serialize, Deserialize and Convert. Bond provides two standard implementation of the IBonded<T> interface, Bonded<T> which can hold and instance of type T, and Bonded<T, R> which can hold a serialized payload represented by a protocol reader R. The former is usually used by producers to initialize bonded<T> values, the latter is implicitly used during deserialization.
The standard implementations always use default implementations of Serializer/Deserializer/Cloner/Transcoder. In order to customize this behavior, the user can pass custom ObjectBondedFactory or PayloadBondedFactory delegates as parameters to ObjectParser or ParserFactory<R>.Create():
1 2 3 4 5 6 7 | |
Lazy deserialization
Because bonded<T> can store (or more accurately, refer to) data representing a serialized data, it can be used to de facto delay deserialization of some parts of payload:
1 2 3 4 5 | |
The schema defined above contains two nested fields. When an object of type Example is deserialized, the field always will be fully instantiated and deserialized, but field sometimes, which is declared as bonded<Sometimes>, will be merely initialized with a reference to its serialized representation. Application can then deserialize the object only when needed:
1 2 3 4 5 6 7 | |
Pass-through
When bonded<T> containing a payload is serialized all the field from the original payload are preserved. This property very useful when building multi-stage service pipelines. Intermediary nodes often need to pass data through with full fidelity. At the same time, it is desirable that every schema change doesn’t necessitate redeployment of all the nodes in a pipeline. Using bonded<T> for pass-through is often the right solution.
As an example let’s imagine a simple aggregator which receives responses from upstream services and aggregates top results.
1 2 3 4 5 6 7 8 9 10 | |
Using bonded<Response> allows the intermediary to aggregate responses, preserving their full content, even if the aggregator doesn’t use the same version of the Response schema as the upstream.
1 2 3 4 5 6 7 | |
Polymorphism
The type parameter T in IBonded<T> interface is covariant which enables polymorphism. A IBonded<Base> can be initialized with an instance of Bonded<Derived>. For example, given the following schema:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | |
The type of the shapes field in C# class Example will be List<IBonded<Shape>> and an instance of the Example can be initialized as following:
1 2 3 4 5 6 7 8 | |
See also the following example:
examples/cs/core/polymorphic_container
Custom type mappings
Bond codegen provides a simple extensibility mechanism allowing use of custom C# types to represent types in a Bond schema. One common scenario is replacing the default collections with a different implementation that is semantically identical, e.g. SortedSet<T> instead of HashSet<T>. Custom type mappings can be also used to introduce completely new types which can be serialized as one of the built-in Bond schema types. For example time could be represented using the DateTime class and serialized as int64.
Defining a custom type mapping involves three steps:
- Define a type alias in the schema.
- Specify during codegen a C# type to represent the alias.
- Implement an appropriate converter for the custom C# type.
Codegen parameters
When generating code for a schema that uses type aliases, the user can specify a custom type to represent each alias in the generated code:
1 | |
The value of the /using parameter consists of one or more alias substitutions separated by semicolons, each in the following format:
1 | |
Custom containers
Type aliases of container types can be mapped to user defined collection classes as long as they implement the same interfaces as the default collections: IEnumerable<T> as well as ICollection<T> for aliases of list<T> and vector<T>, ISet<T> for aliases of set<T> and IDictionary<K, V> for aliases of map<K ,V>. Custom mappings of container type aliases don’t require a user defined converter.
examples/cs/core/container_alias
Converter
Applications using custom mappings for aliases of scalar types, string or blob must provide converter between the custom type and the default type. The converter is a public class named BondTypeAliasConverter defining a pair of public static Convert methods for each type alias:
1 2 | |
For example if System.DateTime was mapped to an alias of int64 the following class could be defined to provide conversions between DateTime and long (the default type for int64):
1 2 3 4 5 6 7 8 9 10 11 12 | |
The converter class must be defined in the same assembly and namespace as the class representing the Bond schema(s) in which the type alias is used or assembly/namespace of one of the types being converted.
examples/cs/core/date_timeexamples/cs/core/decimalexamples/cs/core/guid
Xml
Bond supports Xml serialization via Simple Xml protocol implemented by the SimpleXmlReader and SimpleXmlWriter classes:
1 2 3 4 5 6 7 8 9 10 11 | |
In the example above the Xml reader and writer are constructed directly from an instance of System.Stream. Underneath however they use System.Xml.XmlReader and System.Xml.XmlWriter which provide fast, non-cached, forward-only Xml parsing and generation on top of many different data readers and writers. For example to deserialize Xml payload from a string:
1 2 3 4 | |
The Simple Xml protocol flattens the inheritance hierarchy, putting fields from base and derived classes together under a single element. In order to prevent name conflicts, Simple Xml protocol provides support for optional use of fully qualified schema names as field element namespaces, e.g.:
1 2 3 4 | |
Namespaces can be enabled when serializing to Xml via the UseNamespaces flag in SimpleXmlWriter.Settings:
1 2 3 4 | |
There is no need to enable namespace support for the SimpleXmlReader. The elements representing fields are always matched against field names and their namespaces, if specified in the Xml document, against the qualified names of the containing structs.
Using Xml namespace inherently limits some of flexibility of Bond deserialization. In particular a document with namespaces can’t be deserialized into a schema that is compatible but has a different name, for example a view of the payload schema.
See also the following example:
examples/cs/core/simple_xml
JSON
Bond supports JSON serialization via the Simple JSON protocol implemented by the SimpleJsonReader and SimpleJsonWriter classes. The JSON protocol depends on the Newtonsoft JSON parser and the classes are in a separate assembly Bond.JSON.dll.
1 2 3 4 5 6 7 8 9 10 11 | |
In the example above the JSON reader and writer are constructed directly from an instance of System.Stream. Alternatively they can be also constructed from System.IO.TextReader and System.IO.TextWriter. For example to deserialize JSON payload from a string:
1 2 3 4 | |
The Simple JSON protocol flattens the inheritance hierarchy, putting fields from base and derived schemas together in the same JSON object. Name conflicts in the JSON representation between fields of base and derived schema can be resolved using JsonName schema field attribute:
1 2 3 4 5 6 7 8 9 10 | |
See also the following example:
examples/cs/core/simple_json
Attributes
Bond defines several attributes which are used to decorate user defined types with extra information required by Bond.
Schema decoration
User defined types that represent Bond schemas and their members are decorated with following attributes.
SchemaAttribute
The Schema attribute is used to mark types that represent Bond schemas and thus can be used with Bond APIs. The attribute can be applied to classes, structs and interfaces.
1 2 3 4 5 6 7 8 | |
The Schema attribute applies to the specific type only and is not inherited. When a class decorated with the Schema attribute derives from another class also decorated with the attribute then it represents Bond schema hierarchy. When a class representing schema derives from a class that is not marked with the Schema attribute then it represents a simple schema without a base, the C# base class is ignored by Bond.
When a C# class/interfaces representing a schema derives from multiple interfaces, at most one can be an interface representing a schema (i.e. decorated with [Schema] attribute).
NamespaceAttribute
The Namespace attribute can be optionally used to annotate C# classes, interfaces and enums if their C# namespace is different than the schema namespace (i.e. namespace in .bond idl file), for example when C# code is generated with --namespace flag.
Bond will use the Namespace attribute, when present, to create qualified name of the type. If the attribute is absent the qualified name will use the C# namespace in which the type is defined.
IdAttribute
All public fields and properties that represents fields of a Bond schema must be decorated with the Id attribute to specify the field’s identifier (also called field ordinal). The ordinal value must an unsigned 16-bit integer, unique for each field within a type.
1 2 3 4 5 6 | |
A type representing a schema may have additional fields/properties that don’t represent schema fields and thus are not decorated with Bond attributes.
RequiredAttribute
By default fields of Bond schemas are optional. Required fields must be marked with the Required attribute.
1 2 3 4 5 6 7 8 9 10 11 12 | |
TypeAttribute
The Type attribute is used to provide additional type information about schema fields. The attribute is optional because in most cases Bond can infer type from the C# type of field/property. For example C# type short always maps to Bond type int16. However not all C# types have an unambiguous mapping to Bond type system. For example C# string can represent either Bond type string or wstring. Similarly C# reference types which are always nullable can represent both nullable and non-nullable type in Bond schema. One of the uses for the Type is resolving such ambiguities.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
Bond defines the following tag types that can be used in a Type attribute:
nullable: specifies that a reference or nullable C# type represents a nullable type in the Bond type system.wstring: specifies that a string is UTF16 (i.e.wstringin the Bond type system).blob: specifies that the type represents the schema typeblob.
The Type attribute can also be used to specify type of object to be created during deserialization when a field/property type is an interface.
1 2 3 4 5 6 7 8 9 | |
DefaultAttribute
Bond infers default field values for classes and structs representing schemas from the field initializers or the class/struct constructor. For interfaces the default field values must be specified explicitly by decorating the interface properties with the Default attribute. The value specified in the attribute must be compatible with the field type, otherwise the behaviour is undefined.
1 2 3 4 5 6 7 8 9 | |
The Default attribute is optional for properties that are decorated with nullable tag (the default is implicitly null). For non-nullable collections the Default attribute can either specify null, which means default of nothing in the schema, or it can be omitted, which means the default is an empty collection. Non-nullable properties of a type representing a schema have no concept of a default value thus the Default attribute is not applicable.
AttributeAttribute
The Attribute attribute can be used to specify user defined attribute(s) for schemas, fields and enums.
1 2 3 4 5 6 7 8 9 | |
Schema attributes are usually used by transforms to customize code generation but they can also be accessed by applications via reflection.
Protocol decoration
Bond defines several attributes that are used to decorate implementation of custom protocols with extra information.
ReaderAttribute
The Reader attribute is used on a protocol writer implementation and specifies the type that implements the reader for that protocol.
1 2 3 4 5 | |
ParserAttribute
The Parser attribute can be used on a protocol reader implementation and specifies the type of parser to be used for the protocol. It is optional for protocols that implement IUntaggedReader or ITaggedReader because they implicitly default to use UntaggedParser and TaggedParser respectively. When specified, the Parser attribute value must be a generic type definition with one type parameter, it must implement IParser interface and have two public constructors, one accepting RuntimeSchema argument and one accepting Type argument (compile-time schema).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
SerializerAttribute
The Serializer attribute can be used on a protocol writer implementation to specify custom serializer for the writer. If the attribute is not specified then the default serializer implementation is used. The value of Serializer attribute must be a generic type definition with two type parameters R and W, it must implement ISerializerGenerator<R, W> interface and define two public constructors:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
NuGet packages
Pre-compiled versions of Bond are distributed via NuGet packages from NuGet.org.
Bond.CSharp - An omnibus package that pulls in everything required to use Bond in a C# project. If you’re not sure which packages to use, use this one. (It will pull in all the other packages you need.)
Bond.Core.CSharp - The assemblies required to use Bond at runtime. Useful if some other assembly already contains the compiled types. If your project contains .bond files, you will need to use either Bond.CSharp or Bond.Compiler.CSharp to perform code generation at build time.
Bond.Runtime.CSharp - Additional assemblies that may be needed at runtime depending on which Bond protocols are being used. Needed for Simple JSON.
Bond.Compiler.CSharp - A package with the Bond compiler (gbc) and MSBuild targets for C# code generation. Bond.CSharp includes similar functionality, but pulls in lots of dependencies. Bond.Complier.CSharp has no dependencies.
Bond.Compiler - A tools-only package that contains the Bond compiler (gbc). This is useful if you want to integrate gbc into a build process that isn’t using C# or MSBuild.
For example, if you want to use Bond’s Compact Binary protocol but want to avoid a dependency on Newtonsoft’s JSON.NET, you can use the Bond.Compiler.CSharp and Bond.Core.CSharp packages together.
Platform limitations
The pre-compiled gbc that is included in these packages is Windows-only. See the README for instructions to build gbc for other platforms.
Bond.IO.dll (which provides the types in the Bond.IO.Unsafe namespace) is currently Windows-only, as it relies on some Win32 APIs. To stay cross-platform, only use the types from Bond.dll (in the Bond.IO.Safe namespace).
Frameworks targeted
This table lists which frameworks are targeted by the Bond assemblies.
This table is accurate for Bond NuGet packages 5.1.0 and later and Bond Comm 0.9.0 and later.
| Assembly | .NET 4.0 | .NET 4.5 | Profile78 | .NET Standard 1.0 | .NET Standard 1.3 | .NET Standard 1.6 |
|---|---|---|---|---|---|---|
| Bond.Attributes.dll | Yes | Yes | Yes | Yes | ← | Yes |
| Bond.Reflection.dll | Yes | Yes | Yes | Yes | ← | Yes |
| Bond.dll | Yes | Yes | Yes | Yes | ← | Yes |
| Bond.JSON.dll | Yes | Yes | No | Yes | ← | Yes |
| Bond.IO.dll | Win only | Win only | No | No | Win only | Win only |
| Bond.Comm.Interfaces.dll | No | Yes | Yes | ← | ← | ← |
| Bond.Comm.Layers.dll | No | Yes | Yes | ← | ← | ← |
| Bond.Comm.Services.dll | No | Yes | Yes | ← | ← | ← |
| Bond.Comm.Epoxy.dll | No | Yes | No | No | No | No |
A left arrow (←) indicates that support for that framework is provided by the version of the assembly that targets a lower version of the framework.
Bond Comm
The Bond Communications framework in C# makes it easy and efficent to construct services and hook clients up to those services. Built on top of the Bond serialization framework, Bond Comm aims for the same principles of high-performance and extensibility.
Today the framework supports two messaging patterns:
- request-response: roundtrip messages supporting either payload or error responses
- event: one-way, fire-and-forget messages with no responses
Bond Comm is naturally asynchronous, and the C# implementation takes advantage of the idiomatic Task and async/await facilities of .NET.
Bond Comm is designed to run on NET 4.5 or .NET Core. Mono support is forthcoming, as are Linux and Mac OS X – only Windows 10 and Windows Server 2012 R2 have been tested to date.
See the following examples:
- the C# calculator sample project
examples/cs/comm/pingpongexamples/cs/comm/notifyevent
Defining services
Cross-language services are defined in the Bond IDL.
The generated C# service stub contains a service base class that the developer should subclass to provide the business logic of their service.
The generated proxy stub provides a class with methods that the developer can call to exhange messages with the service.
Epoxy Transport
Bond Comm provides a binary transport called Epoxy. This is the recommended default Transport. The Epoxy transport is designed to be .NET Core compliant, which will allow for cross-platform support.
Epoxy supports TLS as of version 0.6.0.
SimpleInMem Transport
Bond Comm provides an in-memory, single process, binary transport called SimpleInMem. This is not a shared memory transport.
This is an example Transport recommended only for test automation.
Logging and Metrics Facades
Bond Comm includes facades for logging and metrics. By writing simple handlers for logging and metrics, developers can supply their own logging and metrics facilities to gather log and metric data from Bond Comm-based services.
See the following examples:
examples/cs/comm/loggingexamples/cs/comm/metrics
Roadmap
We have a brief roadmap for Bond Comm.
Comm NuGet Packages
Bond Comm is split into a number of NuGet packages to allow for granular consumption. Some of these packages will have dependencies on core Bond packages as well.
Bond.Comm.CSharp - An omnibus package that contains everything needed to use Bond Comm. If you’re not sure which packages to use, use this one. (It will pull in the rest of them.)
Bond.Comm.Runtime.CSharp The assemblies required to use Bond at runtime. Useful if some other assembly already contains the compiled types. If your project contains .bond files, you will need to use either Bond.Comm.CSharp or Bond.Compiler.CSharp to perform code generation at build time.
Bond.Comm.Epoxy.CSharp The Epoxy transport. You’ll need at least one transport, and Epoxy is a good default choice.
Bond.Comm.SimpleInMem.CSharp The SimpleInMem transport. SimpleInMem is a good transport for unit tests, as it is lightweight and doesn’t require or support cross-process communication.