ServiceStack v4.0.11

OrmLite

This release saw a lot of effort towards adding new features to OrmLite:

Pluggable Complex Type Serializers

One of the most requested features to enable pluggable serialization for complex types in OrmLite is now supported. This can be used to specify different serialization strategies for each available RDBMS provider, e.g:

//ServiceStack's JSON and JSV Format
SqliteDialect.Provider.StringSerializer = new JsvStringSerializer();       
PostgreSqlDialect.Provider.StringSerializer = new JsonStringSerializer();
//.NET's XML and JSON DataContract serializers
SqlServerDialect.Provider.StringSerializer = new DataContractSerializer();
MySqlDialect.Provider.StringSerializer = new JsonDataContractSerializer();
//.NET XmlSerializer
OracleDialect.Provider.StringSerializer = new XmlSerializableSerializer();

You can also provide a custom serialization strategy by implementing IStringSerializer.

By default all dialects use the existing JsvStringSerializer, except for PostgreSQL which due to its built-in support for JSON, now uses the JSON format by default.

Breaking Change

Using JSON as a default for PostgreSQL may cause issues if you already have complex types blobbed with the previous JSV Format. You can revert back to the old behavior by resetting it back to the JSV format with:

PostgreSqlDialect.Provider.StringSerializer = new JsvStringSerializer();

New Global Insert / Update Filters

Similar to interceptors in some heavy ORM's, new Insert and Update filters were added which get fired just before any insert or update operation using OrmLite's typed API's (i.e. not dynamic SQL or partial updates using anon types). This functionality can be used for easily auto-maintaining Audit information for your POCO data models, e.g:

public interface IAudit 
{
    DateTime CreatedDate { get; set; }
    DateTime ModifiedDate { get; set; }
    string ModifiedBy { get; set; }
}

OrmLiteConfig.InsertFilter = (dbCmd, row) => {
    var auditRow = row as IAudit;
    if (auditRow != null)
        auditRow.CreatedDate = auditRow.ModifiedDate = DateTime.UtcNow;
};

OrmLiteConfig.UpdateFilter = (dbCmd, row) => {
    var auditRow = row as IAudit;
    if (auditRow != null)
        auditRow.ModifiedDate = DateTime.UtcNow;
};

Which will ensure that the CreatedDate and ModifiedDate fields are populated on every insert and update.

Validation

The filters can also be used for validation where throwing an exception will prevent the operation and bubble the exception, e.g:

OrmLiteConfig.InsertFilter = OrmLiteConfig.UpdateFilter = (dbCmd, row) => {
    var auditRow = row as IAudit;
    if (auditRow != null && auditRow.ModifiedBy == null)
        throw new ArgumentNullException("ModifiedBy");
};

try
{
    db.Insert(new AuditTable());
}
catch (ArgumentNullException) {
   //throws ArgumentNullException
}

db.Insert(new AuditTable { ModifiedBy = "Me!" }); //succeeds

Custom SQL Customizations

A number of new hooks were added to provide more flexibility when creating and dropping your RDBMS tables.

Custom Field Declarations

The new [CustomField] can be used for specifying custom field declarations in the generated Create table DDL statements, e.g:

public class PocoTable
{
    public int Id { get; set; }

    [CustomField("CHAR(20)")]
    public string CharColumn { get; set; }

    [CustomField("DECIMAL(18,4)")]
    public decimal? DecimalColumn { get; set; }
}

db.CreateTable<PocoTable>(); 

Generates and executes the following SQL:

CREATE TABLE "PocoTable" 
(
  "Id" INTEGER PRIMARY KEY, 
  "CharColumn" CHAR(20) NULL, 
  "DecimalColumn" DECIMAL(18,4) NULL 
);  

Pre / Post Custom SQL Hooks when Creating and Dropping tables

A number of custom SQL hooks were added that allow you to inject custom SQL before and after tables are created or dropped, e.g:

[PostCreateTable("INSERT INTO TableWithSeedData (Name) VALUES ('Foo');" +
                 "INSERT INTO TableWithSeedData (Name) VALUES ('Bar');")]
public class TableWithSeedData
{
    [AutoIncrement]
    public int Id { get; set; }
    public string Name { get; set; }
}

And just like other ServiceStack attributes, they can also be added dynamically, e.g:

typeof(TableWithSeedData)
    .AddAttributes(new PostCreateTableAttribute(
        "INSERT INTO TableWithSeedData (Name) VALUES ('Foo');" +
        "INSERT INTO TableWithSeedData (Name) VALUES ('Bar');"));

Custom SQL Hooks are now available to execute custom SQL before and after a table has been created or dropped, i.e:

[PreCreateTable(runSqlBeforeTableCreated)]
[PostCreateTable(runSqlAfterTableCreated)]
[PreDropTable(runSqlBeforeTableDropped)]
[PostDropTable(runSqlAfterTableDropped)]
public class Table {}

Re-factoring OrmLite's SQLite NuGet Packages

In their latest release, the SQLite dev team maintaining the core SQLite NuGet packages have added a dependency to Entity Framework on their existing Sqlite NuGet packages forcing the installation of Entity Framework for users of OrmLite Sqlite. This change also caused some users to see invalid web.config sections after applying the new web.config.transforms. After speaking to the maintainers they've created a new System.Data.SQLite.Core NuGet package without the entity framework dependency and the problematic web.config.transforms.

Unfortunately this was only added for their bundled x86/x64 NuGet package and not their other System.Data.SQLite.x86 and System.Data.SQLite.x64 which the team have indicated should be deprecated in favor of the x86/x64 bundled System.Data.SQLite.Core package.

As a result of this we're removing the dependency to the Sqlite NuGet packages in both architecture specific ServiceStack.OrmLite.Sqlite32 and ServiceStack.OrmLite.Sqlite64 packages and have instead embedded the Sqlite binaries directly, which will solve the current issues and shield them from any future changes/updates from the upstream Sqlite packages.

New ServiceStack.OrmLite.Sqlite.Windows NuGet package

Both these arch-specific packages should now be deprecated in favour of a new Sqlite NuGet package supporting both x86/x64 architectures on Windows:

PM> Install-Package ServiceStack.OrmLite.Sqlite.Windows

Which should now be used for future (or existing) projects previously using the old OrmLite.Sqlite32 and OrmLite.Sqlite64 packages.

The Windows-specific package was added in addition to our existing Mono and Windows compatible release:

PM> Install-Package ServiceStack.OrmLite.Sqlite.Mono

Which works cross-platform on Windows and Linux/OSX with Mono should you need cross-platform support.

.NET Service Clients

New async API's were added for requests marked with returning IReturnVoid. This provides a typed API for executing services with no response that was previously missing, e.g:

public class Request : IReturnVoid {}

await client.PostAsync(new Request());

The API's for all sync and async REST operations have been changed to return HttpWebResponse which now lets you query the returned HTTP Response, e.g:

HttpWebResponse response = await client.PostAsync(new Request());
var api = response.Headers["X-Api"];

Authentication

New IManageRoles API

A new IManageRoles API was added that IAuthRepository's can implement in order to provide an alternative strategy for querying and managing Users' Roles and permissions.

This new API is being used in the OrmLiteAuthRepository to provide an alternative way to store Roles and Permission in their own distinct table rather than being blobbed with the rest of the User Auth data. You can enable this new behavior by specifying UseDistinctRoleTables=true when registering the OrmLiteAuthRepository, e.g:

container.Register<IAuthRepository>(c =>
new OrmLiteAuthRepository(c.Resolve<IDbConnectionFactory>()) {
    UseDistinctRoleTables = true,
});

When enabled, roles and permissions are persisted in the distinct UserAuthRole table. This behavior is integrated with the rest of ServiceStack including the Users Session, RequiredRole/RequiredPermission attributes and the AssignRoles/UnAssignRoles authentication services. Examples of this can be seen in ManageRolesTests.cs.

Messaging

Flexible Queue Name strategies

There are now more flexible options for specifying the Queue Names used in ServiceStack's MQ Servers. You can categorize queue names or avoid conflicts with other MQ services by specifying a global prefix to be used for all Queue Names, e.g:

QueueNames.SetQueuePrefix("site1.");

QueueNames<Hello>.In //= site1.mq:Hello.inq

Or to gain complete control of each queue name used, provide a custom QueueName strategy, e.g:

QueueNames.ResolveQueueNameFn = (typeName, suffix) =>
    $"SITE.{typeName.ToLower()}{suffix.ToUpper()}";

QueueNames<Hello>.In  //= SITE.hello.INQ

Note: Custom QueueNames need to be declared on both MQ Client in addition to ServiceStack Hosts.