Concurrency control

Adapter contains an advanced concurrency mechanism, in such a way that you can decide how to implement concurrency control in your application. It is often better to schedule concurrency aspects at a high level in your application, however if you are required to check whether a save can take place or not, you can.

Similar to SelfServicing, Adapter allows you to specify a predicate expression object with the SaveEntity() method. This predicate expression is included in the UPDATE query (it's ignored in an INSERT query) so you can specify exactly if the update should take place.

Adapter also allows you to implement the interface IConcurrencyPredicateFactory, and instances of that interface can be inserted into entity class instances. If such a factory is present inside an entity class instance, SaveEntity() will automatically request a predicate object from that factory to include in the UPDATE query. This way you can provide concurrency predicates during a recursive save action.

To filter on the original database values fetched into the entity to be saved, you can create for example FieldCompareValuePredicate instances which use the EntityField2.DbValue property. Even though a field is changed in memory through code, the DbValue property of a field will have the original value read from the database. You can use this for optimistic concurrency schemes. See for an example below. If the field is NULL in the database, DbValue is null (C#) or Nothing (VB.NET). To efficiently read the DbValue of a field, use the GetDbValue method on an entity's Fields object as shown below in the example.

See for more information on predicates and filtering Getting started with filtering.

Below an example implementation of IConcurrencyPredicateFactory, which returns predicates which test for equality on EmployeeID for the particular Order. This will make sure the Save or Delete action will only succeed if the entity in the database has still the same value for EmployeeID as the in-memory entity had when it was loaded from the database.

private class OrderConcurrencyFilterFactory : 
    IConcurrencyPredicateFactory
{
    public IPredicateExpression CreatePredicate(
        ConcurrencyPredicateType predicateTypeToCreate, object containingEntity)
    {
        IPredicateExpression toReturn = new PredicateExpression();
        OrderEntity order = (OrderEntity)containingEntity;

        switch(predicateTypeToCreate)
        {
            case ConcurrencyPredicateType.Delete:
                toReturn.Add(OrderFields.EmployeeID == order.Fields.GetDbValue((int)OrderFieldIndex.EmployeeID));
                break;
            case ConcurrencyPredicateType.Save:
                // only for updates
                toReturn.Add(OrderFields.EmployeeID == order.Fields.GetDbValue((int)OrderFieldIndex.EmployeeID));
                break;
        }
        return toReturn;
    }
}
Private Class OrderConcurrencyFilterFactory 
    Implements IConcurrencyPredicateFactory

    Public Function CreatePredicate( _
        predicateTypeToCreate As ConcurrencyPredicateType, containingEntity As object) _
        As IPredicateExpression Implements IConcurrencyPredicateFactory.CreatePredicate

        Dim toReturn As New PredicateExpression()
        Dim order As OrderEntity = CType(containingEntity, OrderEntity)

        Select Case predicateTypeToCreate
            Case ConcurrencyPredicateType.Delete
                toReturn.Add(OrderFields.EmployeeID = _
                        order.Fields.GetDbValue(CInt(OrderFieldIndex.EmployeeID)))
            Case ConcurrencyPredicateType.Save
                ' only for updates
                toReturn.Add(OrderFields.EmployeeID = _
                        order.Fields.GetDbValue(CInt(OrderFieldIndex.EmployeeID)))
        End Select
        Return toReturn
    End Function
End Class

During recursive saves, if a save action fails, which can be caused by a ConcurrencyPredicateFactory produced predicate, thus if no rows are affected by the save action, an ORMConcurrencyException is thrown by the save logic, which will terminate any transaction started by the recursive save.

To set an IConcurrencyPredicateFactory object when an entity is created or initialized, please see the section Adding your own code to the generated classes which discusses various ways to adjust the generated code to add your own initialization code which for example sets the IConcurrencyPredicateFactory instance for a particular object.

You can also set an IConcurrencyPredicateFactory instance of an entity using the ConcurrencyPredicateFactoryToUse property of an entity collection to automatically set the ConcurrencyPredicateFactoryToUse property of an entity when it's added to the particular entity collection.

A third way to set an entity class instance's IConcurrencyPredicateFactory is by using Dependency Injection.