Unit of Work Policy
- 6 minutes to read
Overview
The Unit of Work Policy allows you to manage whether view models use their own individual instances of UnitOfWork or share the one provided by its parent.
The following policies are supported.
- Individual - use an individual UnitOfWork and pass UnitOfWorkPolicy.Individual to children.
- Shared - use a shared UnitOfWork and pass UnitOfWorkPolicy.Shared to children.
- PassSharedToChildren - use an individual UnitOfWork, but pass UnitOfWorkPolicy.Shared to children.
By default, all view models use UnitOfWorkPolicy.Individual. Entities that are queried in one view model cannot be used in another view model. So a collection view model does not pass the selected Entity to its child SingleObjectViewModel. Instead, the collection view model passes the primary key of an Entity and the SingleObjectViewModel then fetches the Entity from its own UnitOfWork.
Sharing UnitOfWork instances allows a parent view model to commit or discard changes made by its children. A change made by a child view model does not need to be committed to the database immediately. Sharing makes it possible to create/modify several child Entities and commit changes in a single transaction.
The CollectionViewModel takes a unitOfWorkPolicy parameter in its constructor. This policy cannot be changed during the view model life time.
The SingleObjectViewModel also functions according to the specified UnitOfWorkPolicy, but it cannot be set explicitly and is always inherited from a parent CollectionViewModel.
Setting the Unit of Work Policy
To change the default Unit of Work Policy, set the additional unitOfWorkPolicy argument within either the CollectionViewModel constructor
/// <summary>
/// Represents the Departments collection view model.
/// </summary>
public partial class DepartmentCollectionViewModel : CollectionViewModel<Department, int, IDepartmentContextUnitOfWork> {
// ...
/// <summary>
/// Initializes a new instance of the DepartmentCollectionViewModel class.
/// This constructor is declared protected to avoid undesired instantiation of the DepartmentCollectionViewModel type without the POCO proxy factory.
/// </summary>
/// <param name="unitOfWorkFactory">A factory used to create a unit of work instance.</param>
protected DepartmentCollectionViewModel(IUnitOfWorkFactory<IDepartmentContextUnitOfWork> unitOfWorkFactory = null)
: base(unitOfWorkFactory ?? UnitOfWorkSource.GetUnitOfWorkFactory(), x => x.Departments,
unitOfWorkPolicy: UnitOfWorkPolicy.Shared) {
}
}
or within the GetDetailsCollectionViewModel method.
/// <summary>
/// Represents the single Department object view model.
/// </summary>
public partial class DepartmentViewModel : SingleObjectViewModel<Department, int, IDepartmentContextUnitOfWork> {
// ...
/// <summary>
/// The view model for the DepartmentEmployees detail collection.
/// </summary>
public CollectionViewModelBase<Employee, Employee, int, IDepartmentContextUnitOfWork> DepartmentEmployeesDetails {
get
{
return GetDetailsCollectionViewModel(
propertyExpression: (DepartmentViewModel x) => x.DepartmentEmployeesDetails,
getRepositoryFunc: x => x.Employees,
foreignKeyExpression: x => x.DepartmentID,
navigationExpression: x => x.Department,
unitOfWorkPolicy: UnitOfWorkPolicy.PassSharedToChildren);
}
}
}
Important
Changing the default Unit of Work Policy requires resetting the saved layout stored in the %LOCALAPPDATA%/[YourApplicationName] folder if it exists.
Scenarios
To illustrate the scenarios enabled by Unit of Work Policies, let’s use an application based on the following data model.
It has the following hierarchical structure:
- Root CollectionViewModel: the Departments collection
- SingleObjectViewModel: individual Department instances
- Detail CollectionViewModel: the Courses and Employees collections specific to each department
- Detail SingleObjectViewModel: individual Course and Employee instances
Default Scenario
The following gallery illustrates the default scenario: all view models use UnitOfWorkPolicy.Individual. As the end-user navigates down the hierarchy, views are opened in separate tabs.
Shared Collection View Model Root
The UnitOfWorkPolicy.Shared policy is specified in the collection view model constructor.
This mode allows you to add several entities in the root collection and save them at once in a single transaction.
The following gallery illustrates this scenario: the DepartmentCollectionViewModel uses UnitOfWorkPolicy.Shared. The Departments view is shown in a tab. As the end-user navigates down the hierarchy, views are opened in modal windows.
Shared Single Object View Model
The UnitOfWorkPolicy.PassSharedToChildren policy is specified in the collection view model constructor.
In this mode, each single object view model gets an individual UnitOfWork. Each detail collection view model shares the UnitOfWork of its parent single object view model.
This mode allows you to edit several entities independently. A new entity can be created, several detail entities appended to it and then all of them saved in one step.
The following gallery illustrates this scenario: the DepartmentCollectionViewModel uses UnitOfWorkPolicy.PassSharedToChildren. The Employee view is opened in a modal window.
Shared Detail Collection View Model
The UnitOfWorkPolicy.Shared policy is specified in a GetDetailsCollectionViewModel invocation.
Every SingleObjectViewModel is independent of its detail collections and other single object view models. It is possible to add/modify several detail entities in a single transaction, but only if the parent entity is already saved to the database.
The following gallery illustrates this scenario: the view model for the DepartmentEmployees detail collection uses UnitOfWorkPolicy.PassSharedToChildren. The Employees view model in the Department-Journalism tab displays Save and Reset Changes buttons that allow the end-user to apply or revert changes made to several detail entities.
Limitations
The following features are only available in view models that use UnitOfWorkPolicy.Individual.
- Projections
- Instant Feedback server mode
- Many-to-many associations with an implicit junction table, or a junction table that does not contain additional payload properties