Using proxies as Slice types

Learn how to a create custom type to represent your proxy in Slice.

A proxy is a construct that allows you to call operations on a remote service using IceRPC. While proxies can be used to call operations defined in Slice, they are not a construct or type in the Slice language itself.

In each programming language, a proxy encapsulates an invocation pipeline and a service address.

If you want to transmit a proxy through a Slice operation, you can easily transmit its service address using the built-in custom type IceRpc::ServiceAddress:

slice
mode = Slice1
[cs::namespace("IceRpc.Slice")]
module IceRpc
/// Represents the address of an RPC service that can be called using ice or icerpc. This custom type is
/// compatible with both Slice1 and Slice2.
[cs::type("IceRpc.ServiceAddress")]
custom ServiceAddress

The recipient can then decode this service address and use it to create a proxy with the correct type. It can also set a suitable invoker and other options. For example:

slice
interface Widget {
spin(speed: int32)
}
interface WidgetFactory {
/// Creates a new @Widget and returns its service address.
createWidget() -> IceRpc::ServiceAddress
}
C#
// Recipient
ServiceAddress serviceAddress = await widgetFactoryProxy.CreateWidgetAsync();
// Create new Widget proxy. We keep the invoker and encode options of the factory proxy.
var widgetProxy = new WidgetProxy(
widgetFactoryProxy.Invoker,
serviceAddress,
widgetFactoryProxy.EncodeOptions);

Transmitting a service address in Slice works well but results in a fairly untyped API both in Slice and in the mapped programming languages.

A solution is to wrap the service address in a custom Slice type. This allows us to rewrite the example above as follows:

slice
interface Widget {
spin(speed: int32)
}
interface WidgetFactory {
/// Creates a new @Widget and returns a proxy to this new Widget.
createWidget() -> WidgetProxy
}
// A proxy to a service that implements Widget; it's encoded as a service address.
[cs::type("Example.WidgetProxy")]
custom WidgetProxy
C#
// Recipient
WidgetProxy widgetProxy = await widgetFactoryProxy.CreateWidgetAsync();

The advantage with this custom proxy approach is we get a typed API and the invoker and other options can be set automatically by the decoding code (see below).

When you define a custom type, you need to provide methods that encode and decode this type in each programming language you want to support. See Custom types for details. As a convenience, the Slice compiler always generates these encode and decode methods for NameProxy. This way, if you decide to create a custom type NameProxy (where Name is the name of a Slice interface), you don't need to implement these methods. And that's exactly what we did in the example above.

The decode method generated for NameProxy decodes a service address and then creates:

  • on the client side, a proxy with the invoker and SliceEncodeOptions of the proxy that sent the request
  • on the server side, a proxy with a null invoker and null SliceEncodeOptions

You can override this default behavior by configuring a base proxy in the ISliceFeature of your outgoing request features or incoming request features.

Was this page helpful?

CookiesYour privacy
This website uses cookies to analyze traffic and improve your experience.
By clicking "Accept," you consent to the use of these cookies. You can learn more about our cookies policy in our Privacy Policy.