OrmLite now supports Async!​
Another major feature request is ticked off in this release with the new Async support available in OrmLite!
A quick overview of the new Async API's added can be seen in the class diagram below:
Basically most of OrmLite public API's now have async equivalents of the same name and an additional conventional *Async
suffix.
The Async API's also take an optional CancellationToken
making converting sync code trivial, where you just need to
add the Async
suffix and await keyword, as can be seen in the
Customer Orders UseCase upgrade to Async diff
, e.g:
Sync:
db.Insert(new Employee { Id = 1, Name = "Employee 1" });
db.Save(product1, product2);
var customer = db.Single<Customer>(new { customer.Email });
Async:
await db.InsertAsync(new Employee { Id = 1, Name = "Employee 1" });
await db.SaveAsync(product1, product2);
var customer = await db.SingleAsync<Customer>(new { customer.Email });
Effectively the only Data Access API's that doesn't have async equivalents are
*Lazy
APIs yielding a lazy sequence (incompatible with async) as well as Schema DDL API's which are typically not used at runtime.
For a quick preview of many of the new Async API's in action, checkout ApiSqlServerTestsAsync.cs.
Async RDBMS Providers​
Currently only a limited number of RDBMS providers offer async API's which are only available in their .NET 4.5 builds, which at this time are only:
We've also added a
.NET 4.5 build for Sqlite
as it's a common use-case to swapout to use Sqlite's in-memory provider for faster tests.
But as Sqlite doesn't provide async API's under-the-hood we fallback to pseudo async support where we just wrap its synchronous responses in Task
results.
Regardless of whether the RDBMS provider offers Async API's, you still can use the same OrmLite async API's with all providers, where the same Async OrmLite API's can also be used in DB Providers that doesn't natively support Async (i.e. Sqlite):
Only when these Async API's are run on an RDBMS provider with native async support (i.e. .NET 4.5 SqlServer or MySql) will you benefit from true
non-blocking Async I/O, otherwise it fallsback to pseudo async support, i.e. synchronous I/O datasets wrapped in Task
Results.
Multiple Self References​
OrmLite's POCO Reference conventions has been expanded to include support for multiple Self References.
The example below shows a customer with multiple CustomerAddress
references which are able to be matched with
the {PropertyReference}Id
naming convention, e.g:
public class Customer
{
[AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
[References(typeof(CustomerAddress))]
public int? HomeAddressId { get; set; }
[References(typeof(CustomerAddress))]
public int? WorkAddressId { get; set; }
[Reference]
public CustomerAddress HomeAddress { get; set; }
[Reference]
public CustomerAddress WorkAddress { get; set; }
}
Once defined, it can be saved and loaded via OrmLite's normal Reference and Select API's, e.g:
var customer = new Customer
{
Name = "Z Customer",
HomeAddress = new CustomerAddress {
Address = "1 Home Street",
Country = "US"
},
WorkAddress = new CustomerAddress {
Address = "2 Work Road",
Country = "UK"
},
};
db.Save(customer, references:true);
var c = db.LoadSelect<Customer>(q => q.Name == "Z Customer");
c.WorkAddress.Address.Print(); // 2 Work Road
var ukAddress = db.Single<CustomerAddress>(q => q.Country == "UK");
ukAddress.Address.Print(); // 2 Work Road
ServiceStack.Redis SSL Support​
The most requested feature for ServiceStack.Redis has also been realized in this release with ServiceStack.Redis now supporting SSL connections making it suitable for accessing remote Redis server instances over a secure SSL connection.
Redis Use Cases​
Redis is normally used as a back-end datastore whose access is typically limited to Internal networks or authorized networks protected via firewalls. The new SSL Support in the Redis Client also enables secure access to a redis-server instance over the Internet and public networks as well, a scenario that's been recently popularized by Cloud hosting environments like Azure Redis Cache.
Connecting to Azure Redis​
As connecting to Azure Redis Cache via SSL was the primary use-case for this feature, we've added a new Getting connected to Azure Redis via SSL to help you get started.
Redis Connection Strings​
Redis Connection strings have been expanded to support the more versatile URI format which is now able to capture most of Redis Client settings in a single connection string (akin to DB Connection strings).
Redis Connection Strings supports multiple URI-like formats, from a simple hostname or IP Address and port pair to a fully-qualified URL with multiple options specified on the QueryString.
Some examples of supported formats:
localhost
127.0.0.1:6379
redis://localhost:6379
password@localhost:6379
clientid:password@localhost:6379
redis://clientid:password@localhost:6380?ssl=true&db=1
More examples can be seen in ConfigTests.cs
Any additional configuration can be specified as QueryString parameters. The full list of options that can be specified include:
Ssl | bool | If this is an SSL connection |
Db | int | The Redis DB this connection should be set to |
Client | string | A text alias to specify for this connection for analytic purposes |
Password | string | UrlEncoded version of the Password for this connection |
ConnectTimeout | int | Timeout in ms for making a TCP Socket connection |
SendTimeout | int | Timeout in ms for making a synchronous TCP Socket Send |
ReceiveTimeout | int | Timeout in ms for waiting for a synchronous TCP Socket Receive |
IdleTimeOutSecs | int | Timeout in Seconds for an Idle connection to be considered active |
NamespacePrefix | string | Use a custom prefix for ServiceStack.Redis internal index colletions |
New RedisManagerPool
Client Manager​
With the introduction of Redis URI Connection Strings we've been able to simplify and streamline the existing PooledRedisClientManager
implementation that's been extracted out into clients manager called RedisManagerPool
.
In addition to removing all above options on the Client Manager itself, we've also removed readonly connection strings so the configuration is
much simpler and more aligned with the common use-case.
In most cases, PooledRedisClientManager
is substitutable with RedisManagerPool
e.g:
container.Register<IRedisClientsManager>(c =>
new RedisManagerPool(redisConnectionString));
New Generic API's for calling Custom Redis commands​
Most of the time when waiting to use a new Redis Command you'll need to wait for an updated version of ServiceStack.Redis to add support for the new commands likewise there are times when the Redis Client doesn't offer every permutation that redis-server supports.
With the new Custom
and RawCommand
API's on IRedisClient
and IRedisNativeClient
you can now use the RedisClient to send your own
custom commands that can call adhoc Redis commands:
public interface IRedisClient
{
...
RedisText Custom(params object[] cmdWithArgs);
}
public interface IRedisNativeClient
{
...
RedisData RawCommand(params object[] cmdWithArgs);
RedisData RawCommand(params byte[][] cmdWithBinaryArgs);
}
These API's return Custom Results in the generic data structures below:
public class RedisText
{
public string Text { get; set; }
public List<RedisText> Children { get; set; }
}
public class RedisData
{
public byte[] Data { get; set; }
public List<RedisData> Children { get; set; }
}
These Custom API's take a flexible object[]
arguments which accepts any serializable value e.g.
byte[]
, string
, int
as well as any user-defined Complex Types which are transparently serialized
as JSON and send across the wire as UTF-8 bytes.
var ret = Redis.Custom("SET", "foo", 1); // ret.Text = "OK"
byte[] cmdSet = Commands.Set;
ret = Redis.Custom(cmdSet, "bar", "b"); // ret.Text = "OK"
ret = Redis.Custom("GET", "foo"); // ret.Text = "1"
There are also
convenient extension methods
on RedisData
and RedisText
that make it easy to access structured data, e.g:
var ret = Redis.Custom(Commands.Keys, "*");
var keys = ret.GetResults(); // keys = ["foo", "bar"]
ret = Redis.Custom(Commands.MGet, "foo", "bar");
var values = ret.GetResults(); // values = ["1", "b"]
Enum.GetNames(typeof(DayOfWeek)).ToList()
.ForEach(x => Redis.Custom(Commands.RPush, "DaysOfWeek", x));
ret = Redis.Custom(Commands.LRange, "DaysOfWeek", 1, -2);
var weekDays = ret.GetResults();
weekDays.PrintDump(); // ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]
and some more examples using Complex Types with the Custom API's:
var ret = Redis.Custom(Commands.Set, "foo", new Poco { Name = "Bar" }); // ret.Text = "OK"
ret = Redis.Custom(Commands.Get, "foo"); // ret.Text = {"Name":"Bar"}
Poco dto = ret.GetResult<Poco>();
dto.Name.Print(); // Bar
New Config, Role and Client commands​
A number of New API's added in this can be seen below:
public interface IRedisClient
{
...
RedisText GetServerRoleInfo();
string GetConfig(string item);
void SetConfig(string item, string value);
void SaveConfig();
void ResetInfoStats();
string GetClient();
void SetClient(string name);
void KillClient(string address);
long KillClients(string fromAddress = null,
string withId = null, RedisClientType? ofType = null, bool? skipMe = null);
List<Dictionary<string, string>> GetClientsInfo();
void PauseAllClients(TimeSpan duration);
}
public interface IRedisNativeClient
{
...
void ConfigRewrite();
RedisText Role();
string ClientGetName();
void ClientSetName(string client);
void ClientKill(string host);
long ClientKill(string addr = null, string id = null, string type = null, string skipMe = null);
byte[] ClientList();
void ClientPause(int timeOutMs);
}
New VB.NET Add ServiceStack Reference!​
This release also adds Add ServiceStack Reference support for the last remaining major .NET language with the new first-class support for VB.NET Add ServiceStack Reference!
This now allows any C#, F# or VB.NET client project to be able generate and end-to-end typed API for your services just by providing the url of your remote ServiceStack instance, directly from within VS.NET!
After clicking OK, the servers DTO's and ServiceStack.Client NuGet package are added to the project, providing an instant typed API:
Thanks to the close semantics between the C# and VB.NET languages, we're able to add support for all C# customization options in VB.NET as well.
Much of the new VB.NET NativeTypes provider is thanks to the efforts of @KevinHoward.
Upgrade ServiceStackVS​
To take advantage of VB.NET Add ServiceStack Reference feature,
Upgrade or Install ServiceStackVS VS.NET Extension.
If you already have ServiceStackVS installed, uninstall it first from Tools -> Extensions and Updates... -> ServiceStackVS -> Uninstall
.
Simplified UX for all languages​
Our first iteration of Add ServiceStack Reference for C# used a T4 Template to make it easy for clients to view and modify all Customization options available and to be able to auto-generate the Server DTO's by modifying and saving (or re-running) the T4 template.
As F# projects didn't support T4 Templates, when we added support for F# Add ServiceStack Reference we had to skip the T4 template and add the server-generated DTO's source file directly to the project.
By skipping the T4 Template we pleasantly discovered we ended up with a nicer, simplified and more user-friendly UX, with less moving parts for the default use case of generating client DTO's based on the Default Server Configuration.
Improving Single Generated Source File Story​
We've since decided to embrace and provide a better development experience around a single source file approach and use it consistently in all C#, F# and VB.NET projects - now resulting simpler Add ServiceStack Reference UX for all client projects.
Update ServiceStack Reference Context Menu Item​
With the latest ServiceStackVS you can now update the Server DTO's in all projects by clicking on Update ServiceStack Reference
on the context-menu, e.g:
Updating and Customizing Generated Types​
To customize the generated DTO's
on the client you can just uncomment the option you want to change directly in the header comments and hit save.
ServiceStackVS automatically watches for any changes to the generated dto source files (i.e. ending with .dtos.cs
) and will automatically
send the uncommented options to the remote server referenced by the BaseUrl
and replace the existing file with the updated DTOs instantly!
Taking the example below, once we uncomment the MakePartial
option and save the file, ServiceStackVS automatically sends a new request
to the remote ServiceStack instance, passing in the ?MakePartial=False
option when requesting updated DTO's:
/* Options:
Date: 2014-10-21 00:44:24
Version: 1
BaseUrl: https://stackapis.netcore.io
MakePartial: False
//MakeVirtual: True
//MakeDataContractsExtensible: False
//AddReturnMarker: True
//AddDescriptionAsComments: True
//AddDataContractAttributes: False
//AddIndexesToDataMembers: False
//AddResponseStatus: False
//AddImplicitVersion:
//InitializeCollections: True
//AddDefaultXmlNamespace: http://schemas.servicestack.net/types
*/
After saving you'll be able to notice the DTO's are updated instantly with the Date: *
changing to reflect the current time and the new
generated DTO's no longer containing partial
classes.
ServiceStack.Text​
New JsConfig<T>.OnDeserializing
and dynamic ShouldSerialize(string field)
customization options were added to ServiceStack's JSON and JSV
Text serializers by @pavelsavara. An example of these new customization options in action is visible below:
[DataContract]
public class CustomSerializedPoco
{
[IgnoreDataMember]
public HashSet<string> hasAttribute;
[DataMember(EmitDefaultValue = false, IsRequired = false)]
public int A { get; set; }
[DataMember(EmitDefaultValue = false, IsRequired = false)]
public int? B { get; set; }
public bool? ShouldSerialize(string fieldName)
{
return hasAttribute == null
? null
: hasAttribute.Contains(fieldName);
}
public object OnDeserializing(string fieldName, object value)
{
if (hasAttribute == null)
hasAttribute = new HashSet<string>();
hasAttribute.Add(fieldName);
return value;
}
}
This change makes it possible to create dynamic POCO's that behave in a similar way that dynamic languages can,
e.g. After deserialization you can detect which fields were deserialized by inspecting the hasAttribute
collection.
The ShouldSerialize
API, closely follows the existing ShouldSerialize{X}
convention but instead allows for a single API
to handle all serializable properties.
The API returns a bool?
which has the following meaning:
true
- Should be emittedfalse
- Should not be emittednull
- Use default behavior
This allows us to implement a custom type that can support full round-trip when the field on the original JSON payload allowing use to
implement a custom type with similar functionality to IExtensibleDataObject
which allows survival and forwarding of unknown properties, but for JSON.
RabbitMQ​
RabbitMQ Server and Client now have optional PublishMessageFilter
and GetMessageFilter
callbacks which can be used to intercept
outgoing and incoming messages, the IBasicProperties.Type
is also pre-populated with the Type name of the message body that was published, e.g:
var mqServer = new RabbitMqServer("localhost")
{
PublishMessageFilter = (queueName, properties, msg) => {
properties.AppId = "app:{0}".Fmt(queueName);
},
GetMessageFilter = (queueName, basicMsg) => {
var props = basicMsg.BasicProperties;
receivedMsgType = props.Type; //automatically added by RabbitMqProducer
receivedMsgApp = props.AppId;
}
};
using (var mqClient = mqServer.CreateMessageQueueClient())
{
mqClient.Publish(new Hello { Name = "Bugs Bunny" });
}
receivedMsgApp.Print(); // app:mq:Hello.In
receivedMsgType.Print(); // Hello
Other Minor Changes​
- ServerEvents Server now echoes heartbeat messages back through to the listening connection,
ServerEventsClient
only fires theOnHeartbeat
callback when it's receives the echoedcmd.Heartbeat
command message - Request binding for
Path
andQueryString
variables are added to DTO's with Request DTO's providing their own custom body deserialization by implementingIRequiresRequestStream
- New
IAppHost.OnDisposeCallbacks
available allowing Plugins to register callbacks whenAppHost
is disposed Config.UseHttpsLinks
now modifies generated BaseUrl of all links to usehttps
- The
ResponseStatus
on Custom DTO's are now preserved when thrown inside a customHttpError
response - Equality members added to
[Route]
,[Authenticate]
,[RequiredRole]
and[RequiredPermission]
attributes ToOptimizedResultUsingCache
no longer double-encodes rawstring
responsesMvcHtmlString
was moved toServiceStack.Html
namespace- New
StaticFileHandler.ResponseFilter
added to be able to modify custom headers returned on static files - Many of OrmLite's static Extension method classes were renamed into a more logical grouping. These changes are source compatible for typical usage of OrmLite API's, i.e. referenced as extension methods
Breaking changes​
Added new .NET 4.5 Builds​
In preparation for introducing Async API's we've added new .NET 4.5 builds for the following packages:
- ServiceStack.OrmLite
- ServiceStack.OrmLite.Sqlite.Mono
- ServiceStack.OrmLite.SqlServer
- ServiceStack.OrmLite.MySql
- ServiceStack.Server
When adding ServiceStack NuGet Packages to a .NET 4.5 project you will now get these newer .NET 4.5 builds instead. The additional builds means you could potentially run into issues if mixing .NET v4.0 and v4.5 builds as all dependencies need to reference the same build version.
Should you need to, the easiest way to fix any versioning issues is to make sure all projects use the same .NET Framework version (e.g. .NET 4.5) and then just uninstall and re-install the ServiceStack NuGet packages.
Removed ThreadStatic OrmLite Configuration​
We've also removed our existing ThreadStatic config variables (used to temporarily override global configuration).
Most per-connection state is now stored on the connection e.g. CommandTimeout
was previously overridden with:
var hold = OrmLiteConfig.TSCommandTimeout;
try {
OrmLiteConfig.TSCommandTimeout = 60;
db.Select(...);
} finally {
OrmLiteConfig.TSCommandTimeout = hold;
}
Is now set directly on the connection (and only applies to that connection), e.g:
using (var db = DbFactory.Open())
{
db.SetCommandTimeout(60);
db.Select(...);
}
Likewise if you ever need to access the current OrmLiteConfig.DialectProvider
, it should now be retrieved from the IDbConnection
, i.e:
db.GetDialectProvider();
and if you ever need to access the underlying ADO.NET IDbConnection
or IDbCommand
you can access them via the following APIs:
IDbConnection adoDb = db.ToDbConnection();
IDbCommand adoDbCmd = dmCmd.ToDbCommand();
IReturnVoid now returns void​
All IReturnVoid
API's on Service Clients have been changed to return void
instead of
HttpWebResponse
which needed to be explicitly disposed by the callee.
To access the HttpWebResponse
, Request DTO's can be changed to IReturn<HttpWebResponse>
public class EmptyResponse : IReturn<HttpWebResponse> { ... }
Alternatively the Response can be specified on the call-site with:
HttpWebResponse response = client.Get<HttpWebResponse>(new EmptyResponse());