SisoDb, v10.0-prerelease – Transactions, updates and concurrencies

Been using SisoDb for the read model in an distributed system using CQRS for a while and while doing this we have identified some features that we needed when handling concurrent updates. Hence that has been the “theme” for this release of SisoDb. Also, the API has been simplified and the sessions are now using transaction scopes instead of plain ADO.Net transactions.

As always the documentation has been updated along with the release notes.

Install Prerelease via NuGet

Note that it’s a prerelease. Hence when installing via NuGet you need to append the “-Pre” suffix.

PM> Install-Package SisoDb.Sql2008 -Pre

PM> Install-Package SisoDb.Sql2012 -Pre

PM> Install-Package SisoDb.SqlCe4 -Pre

Concurrency tokens

From v10.0 you can now chose to include a concurrency token in your model. You do this by adding a member: public Guid|int|long ConcurrencyToken { get; set; }. This will tell SisoDb to re-check against the database before storing the item and ensure that you aren’t storing an older/other version of the document. If you violate this, a SisoDbConcurrencyException will be thrown.

var item = new SimpleItem { Value = "A" };
Database.WriteOnce().Insert(item);
...
...
using (var session = Database.BeginSession())
{
    var refetched = session.GetById<SimpleItem>(item.Id);
    refetched.Value = "B";

    session.Update(refetched);
}

Inline updates

rom v10.0 you can now do “inline updates” and also (optional) provide a proceed condition. This is useful e.g. when using SisoDb in a concurrent and multithreaded environment like in a asynchronous CQRS solution, since it lets you keep down the update-scope and only update if it’s a later message.

using(var session = db.BeginSession())
{
    session.Update<Customer>(
        id, 
        c => c.BonusPoints = bonusPointIncreasedEvent.NewValue);
}

//or

db.UseOnceTo().Update<Customer>(
    id, 
    c => c.BonusPoints = bonusPointIncreasedEvent.NewValue);

and with the proceed clause:

using(var session = db.BeginSession())
{
    session.Update<Customer>(
        id, 
        c => c.BonusPoints = bonusPointIncreasedEvent.NewValue, 
        c => bonusPointIncreasedEvent.Sequence > c.LastSequence);
}

//or

db.UseOnceTo().Update<Customer>(
    id, 
    c => c.BonusPoints = bonusPointIncreasedEvent.NewValue,
    c => bonusPointIncreasedEvent.Sequence > c.LastSequence);

InsertAs – e.g. anonymous types

From v10.0 you can now call InsertAs(object item) where the item doesn’t have to implement any interface or base-class defined by T. The item being passed just need to match the contract in signature, either partially or fully. You could even insert an anonymous type.

db.UseOnceTo().InsertAs<Customer>(new { CustomerNo = "124", Name = "Daniel Wertheim" });

Transaction Scopes

Before v10.0 the transactions explicitly was plain ADO.Net transactions. As of v10.0, under the hood, the Session makes use of transaction-scopes TransactionScope-class but only as long as the targeted storage does support transaction scopes. So, when creating a session:

using(var session = db.BeginWriteSession())
{
    session.InsertMany(customers);
}

It will make a call to Db.ProviderFactory.GetRequiredTransaction() (Db must be of ISisoDbDatabase to have the member ProviderFactory). Underlying, it will create a TransactionScope with the options ReadCommitted and Required. By using Required you can wrap multiple Sessions in an outer TransactionScope and there by getting a larger transaction scope. Note! It’s not recommended to explicitly make use of the TransactionScope class. Instead, use the ProviderFactory.GetRequiredTransaction() or ProviderFactory.GetSuppressedTransaction() methods.

using (var t = TestContext.ProviderFactory.GetRequiredTransaction())
{
    using (var session = TestContext.Database.BeginSession())
    {
        session.InsertMany(new[]
        {
            new IdentityItem {Value = 1},
            new IdentityItem {Value = 2},
            new IdentityItem {Value = 3}
        });
    }

    using (var session = TestContext.Database.BeginSession())
    {
        session.InsertMany(new[]
        {
            new IdentityItem {Value = 4},
            new IdentityItem {Value = 5},
            new IdentityItem {Value = 6}
        });
    }

    //If you want to rollback changes, you can call t.MarkAsFailed()
}

But again. In the simplest case, db.BeginSession() will make use of transactions under the hood, and it’s nothing you should have to take care of.

That’s it for this release. I’m planning on putting together a provider for MySQL and if there’s anyone that want to contrib, don’t hesitate.

//Daniel

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s