Pomona can generate a .NET compatible client for our service if desired.
A client NuGet package can be generated and downloaded by accessing the
/client.nupkg url on the service.
Alternatively the DLL can be downloaded directly from /{ClientAssembly}.dll,
where {ClientAssembly} must be replaced with the configured name of the
client dll (see below for this).
Controlling client generation
Metadata
By overriding the ClientMetadata property of a ITypeMappingFilter,
we can change the assembly name, interface name, type name and namespace
of the client.
public override ClientMetadata ClientMetadata =>
base.ClientMetadata.With(
assemblyName : "Critters.Client",
name : "CritterClient",
interfaceName : "ICritterClient",
@namespace : "Critters.Client");
Merged assembly
By returning true from the GenerateIndependentClient() method of
ITypeMappingFilter we can generate an assembly with Pomona.Common.dll
merged.
Note that the resulting assembly will still have a dependency on
Newtonsoft.Json.
LINQ support
As the IQueryable interface by nature is a leaky abstraction, supporting
all queries is not practical.
Common use cases should however be supported, although sometimes with a
bit of Expression acrobatics.
Simple queries
Simple non-aggregate queries must be structured in a way described in the example below:
client.Weapons
.Query()
.Where(x => x.Price > 100.0m /* any number of where expressions */)
.Select(x => x.Model /* any number of select expressions */)
.Where(x => x.Name.StartsWith("NERF") /* where and select expressions can be mixed */)
.OrderBy(x => x.Name /* ordering must come after where and select */)
.Skip(50 /* skip after order by */)
.Take(50 /* take after skip */)
.ToList();
Group by queries
Aggregate queries must be structured in a way described in the example below:
client.Weapons
.Query()
.Where(x => x.Price > 100.0m /* any number of where expressions */)
.GroupBy(x => x.Model.Name /* group by after where */)
.Select(x => new { modelName = x.Key, totalPrice = x.Sum(y => y.Price) })
.OrderBy(x => x.totalPrice)
.ToList();
Supported expressions
A few of the supported methods are listed here
- Operators: + - / * && ||
- String
- .Length
- .StartsWith(string value)
- .Contains(string value)
- .ToLower()
- .ToUpper()
- IEnumerable<T>
- .Where(Func<,> predicate)
- .Select(Func<,> selector)
- .Sum()
- .Count()
A more complete list can be found in QueryFunctionMapping.cs of the Pomona
source code.
Extended resources and attributes
In some cases it is useful for resources to contain a Dictionary<string,object>
to store dynamic data, which keys might only be known by the client side.
Unfortunately a .NET dictionary is not very convenient to work with in queries, and also leads to a lot of clumsy mapping code.
To simplify this Pomona makes it possible to automatically map statically typed properties to a resource dictionary by declaring the dictionary property as an attribute container.
map.Include(x => x.Map, o => o.AsAttributes());
Now we can inherit the client generated interface, and any added properties
with primitive values will automatically project to Map.
public interface IExtendedResource : IDictionaryContainer
{
string CustomString { get; set; }
string OtherCustom { get; set; }
}
After this we can query the resource using the IExtendedResource interface,
which will automatically redirect CustomString to Map["CustomString"],
and OtherCustom to Map["OtherCustom"].
For more details about this feature looking at the ExtendedResourceClientTests.cs
file is recommended.