DataObjects.Net is built to simplify writing business code – a code written on high-level language (e.g. C#) and operating on application server.
This brings some new effects in comparison to the case developers faced earlier, when similar code was written in stored procedures:
DataObjects.Net addresses both of these issues:
Transparent persistence and transactional transparency ensure you’re working with entities as if they’re “directly connected” to the underlying database objects:
So DataObjects.Net provides “full WYSIWYG” here.
Generalized batching, future queries and prefetch API allow you to dramatically reduce the chattiness with almost zero efforts.
Note
Transparent persistence and transactional transparency allows developer to be sure that he always deal with actual state: the state he sees is absolutely the same as direct SQL queries performed on the same connection & transaction would show.
For example, if you’re fetching some entity in transaction TA and trying to get its property value in transaction TB, by default its value would be re-fetched there. “By default” indicates there are ways to make DataObjects.Net behave differently, but you must do this explicitly.
Note
As a consequence, DataObjects.Net requires any data access operations to be executed inside logical transactions. “Logical” here means that these transactions define isolation boundaries just for DataObjects.Net.
To open a transaction, you should call session.OpenTransaction(...) method (it has several overloads):
using (var session = domain.OpenSession()) {
using (var transactionScope = session.OpenTransaction()) {
var person = new Person();
person.Name = "Barack Obama";
transactionScope.Complete();
}
}
Asynchronous opening is also possible:
await using (var session = await domain.OpenSessionAsync()) {
await using (var transactionScope = await session.OpenTransactionAsync()) {
var person = new Person();
person.Name = "Barack Obama";
transactionScope.Complete();
}
}
The simplest form of this method opens a transaction in active session and returns its transaction scope – an IDisposable object used to committing or rolling back the transaction.
To commit the transaction, call transactionScope.Complete() method and dispose the transaction scope.
To rollback the transaction, just dispose the transaction scope without calling Complete() method.
Note
In example above we open a transaction inside using block, so transaction scope returned by Open() method will be disposed on leaving this block. If code inside using block throws an exception, Complete() method won’t be invoked. So our transaction will be committed only if code inside it is executed successfully.
If session.OpenTransaction(...) method is invoked, but active session already has associated transaction, it does nothing and return so-called void scope. Such a scope does nothing on Complete() method call and its disposal as well.
You can also open a transaction with specified isolation level: session.OpenTransaction(IsolationLevel isolationLevel).
Use session.OpenTransaction(TransactionOpenMode.New) to open a new transaction. If there is no outermost transaction in the active Session, the outermost transaction will be opened; otherwise a nested one will be opened.
Nesting level is limited only by underlying RDBMS.
var domain = BuildDomain();
using (var session = domain.OpenSession()) {
using (var transactionScope = session.OpenTransaction()) {
var john = new User {
Name = "john doe"
};
// Opening a nested transaction
using (var nestedScope = session.OpenTransaction(TransactionOpenMode.New)) {
john.Name = "john smith";
// Omitting nestedScope.Complete(), so nested transaction will be rolled back
}
Assert.AreEqual("john doe", john.Name);
// Marking the transaction scope as completed to commit the outermost transaction
transactionScope.Complete();
}
}