You are on page 1of 28

Karvonite 4.

0
Release Candidate
Fernando Píccolo, Chief Executive Officer, MP Game Studio and Diego Cánepa, Director of
Game Design/Software Architect, MP Game Studio.

Follow us:

Agile persistence framework for the .NET platform, including the .NET Compact Framework and
the XNA Framework.

Copyright © MP Game Studio 2010. All Rights Reserved.


Notice:
© 2010 MP Game Studio. All rights reserved.

No part of this document and/or the help file may be stored in retrieval systems, or made available on the
internet or a web server without the prior written consent of the author.

Neither this document nor the help file may be included in the distribution of any software application or
any other document collection without the prior written consent of the author.

The author has made every effort in the preparation of this content to ensure the accuracy of the
information. However, the information is provided without warranty, either express or implied. The author
will not be held liable for any damages caused or alleged to be caused either directly or indirectly by this
content.

Karvonite is trademark of MP Game Studio.


Other product and company names mentioned herein may be the trademarks of their respective owners.

Copyright © MP Game Studio 2010. All Rights Reserved.


Table of Contents

1 Introduction ......................................................................................................................................... 1
1.1 What is Persistence? .................................................................................................................... 1
1.2 What is Karvonite? ........................................................................................................................ 1
1.3 Why Karvonite? ............................................................................................................................. 1
2 Karvonite Fundamentals .................................................................................................................... 3
2.1 The Persistence Ignorance Principle ............................................................................................ 3
2.2 The Persistence Model ................................................................................................................. 3
2.3 Karvonite Persistence Rules ......................................................................................................... 4
2.3.1 Types and Data Members ......................................................................................................... 4
2.3.2 About Predefined Types ............................................................................................................ 4
2.3.3 About Enumerations .................................................................................................................. 4
2.3.4 About Collections ...................................................................................................................... 4
2.3.5 About Dictionaries ..................................................................................................................... 5
2.4 Object Libraries ............................................................................................................................. 5
2.5 The ObjectSpace .......................................................................................................................... 6
2.5.1 Change Tracking ....................................................................................................................... 6
2.5.2 Saving Changes ........................................................................................................................ 6
2.5.3 Creating an ObjectSpace .......................................................................................................... 7
2.5.4 Adding Objects .......................................................................................................................... 7
2.5.5 Removing Objects ..................................................................................................................... 7
2.5.6 Retrieving Objects ..................................................................................................................... 8
2.6 Persistence Model Evolution ......................................................................................................... 9
2.6.1 Using a Read Only ObjectSpace ............................................................................................ 10
3 Quick Tour ......................................................................................................................................... 11
3.1 Creating an Address Book .......................................................................................................... 12
3.2 Creating the Persistence Model .................................................................................................. 12
3.2.1 Embedding the Persistence Model ......................................................................................... 14
3.3 Extending the Object Model ........................................................................................................ 14
3.4 Adding a Collection ..................................................................................................................... 15
3.5 Putting It All Together .................................................................................................................. 16
3.6 Multi-Targeting ............................................................................................................................ 18
4 Advanced Topics .............................................................................................................................. 19
4.1 Concurrency ................................................................................................................................ 19
4.1.1 Concurrency Control Strategy ................................................................................................. 19

Copyright © MP Game Studio 2010. All Rights Reserved.


4.2 Undo ............................................................................................................................................ 20
4.2.1 Announcing Object Changes .................................................................................................. 20
4.2.2 Nested Change Blocks ............................................................................................................ 20
5 Future Directions .............................................................................................................................. 22

Copyright © MP Game Studio 2010. All Rights Reserved.


1 Introduction

1.1 What is Persistence?


Persistence describes something that exists beyond its expected lifetime. As applied to
computer science, persistence describes objects that outlive the execution of the program that
created it, which is achieved in practice by storing the object data in a permanent storage - e.g.,
databases and XML files. Without this ability objects only exist in memory, and will be lost when
a program exits. Persistence is important because most applications must operate on data that
is retrieved from somewhere. As a result, persistence allows a program to be restarted and
reloaded with the objects saved in the data storage.

1.2 What is Karvonite?


Karvonite is an object persistence framework for the .NET platform (see Figure 1-1). Karvonite's
goal is to relieve the developer from all serialization related programming tasks in a non-
invasive way.

It can save you from writing a lot of boring and thus error-prone code for moving the in-memory
objects to and from the data storage. Karvonite is non code-invasive and requires no special
attributes, interfaces or base classes for saving objects.

The Karvonite API is exceptionally simple and provides a gradual learning curve with a very low
entry point.

1.3 Why Karvonite?


Despite the fact that .NET serialization and XML files are ideal for storing data easily and
effectively, in several cases they are not a viable replacement for a data engine. In contrast, not
all applications require the horsepower and the excessive functionality and optimization of an
enterprise database or a SQL engine; databases are a skill unto themselves and the learning
curve is not as fast as expected.

Also, Karvonite brings other remarkable features such as optimistic multi-user concurrency and
undo features. Karvonite supports the .NET Compact Framework and all target platforms of the
XNA Framework: Windows, Xbox360 and Zune.

Copyright © MP Game Studio 2010. All Rights Reserved. 1


Figure 1-1: Karvonite Architecture

Karvonite
Persistence Class Library
Model Editor Assembly
Application (Karvonite40.dll)

.NET
.NET
Compact XNA Framework
Framework
Framework

Windows Xbox 360 Zune

Although the samples in this document are shown in C#, you can use the Karvonite Class
Library with any other programming language that complies with the .NET Common Language
Specification (CLS).

Target Framework Path


.NET Framework 3.5 <InstallDir>\References\.NET Framework\v3.5

.NET Framework 4.0 <InstallDir>\References\.NET Framework\v4.0

.NET Compact Framework 3.5 <InstallDir>\References\.NET Compact Framework\v3.5

.NET Compact Framework 4.0 <InstallDir>\References\.NET Compact Framework\v4.0

XNA Framework (3.1) Windows <InstallDir>\References\XNA\v3.1\Windows\x86

XNA Framework (3.1) Xbox360 <InstallDir>\References\XNA\v3.1\Xbox360

XNA Framework (3.1) Zune <InstallDir>\References\XNA\v3.1\Zune

NOTE: If C:\ is your Windows drive, the default <InstallDir> is C:\Program


Files\Karvonite 4.0 (RC).

Karvonite requires installation of the corresponding .NET Framework on the target


client. In addition to the object libraries and other assemblies your application
references, you only need to distribute the Karvonite40.dll assembly.

2 Copyright © MP Game Studio 2010. All Rights Reserved.


2 Karvonite Fundamentals

2.1 The Persistence Ignorance Principle


Karvonite is based on a domain driven design principle called persistence ignorance. This
principle states that persistence code is orthogonal to domain logic. A domain object should not
care how it persists itself; instead it should rely on an infrastructure service to persist its state.
Karvonite complies with the persistence ignorance principle in the following way:

 You do not have to inherit from a particular base class on objects you want to persist.
 There is no special requirement to implement a specific interface.
 There is no requirement to instantiate objects through a factory. The only requirement is
a default constructor (which may be non-public).1
 You do not have to use specially provided data types.
 You do not have to provide specific fields in order to detect if an object has been
modified. The infrastructure will determine if an object has been modified.

2.2 The Persistence Model


Karvonite requires you to create a persistence model. A persistence model is an external file
that is used to instruct Karvonite about which object instances (classes and structures) and
which data members (fields and properties) should be saved or loaded. In this way, the details
of saving your objects are kept quite separately from the objects themselves; others will be
considered as transient parts of your application logic.

Persistence models are created with the Persistence Model Editor tool. At runtime, your
application will load the persistence model from an embedded resource (more on this later).

1
A default constructor is one that can be invoked without any arguments. This constructor will be used to
instantiate saved objects.

Copyright © MP Game Studio 2010. All Rights Reserved. 3


2.3 Karvonite Persistence Rules
2.3.1 Types and Data Members
The following table outlines the persistence rules for each specific type and data member:

Kind Constraints Remarks

Class  Must have a default constructor (which may be non- Class instances exist
public). independently of any
 Must not be static. other object holding
references to it. Class
 Must not be generic.
instances support
 Must not be a nested type. circular and shared
 Must have public visibility if class is defined in a references.
Microsoft assembly.
The default constructor
will be used to
instantiate saved
objects. Of course,
other constructors can
also be added.
Structure  Must not be generic. Structure instances are
 Must not be a nested type. automatically removed
 Must have public visibility if class is defined in a when its containing
type is removed.
Microsoft assembly.

Field  Must not be static.


 Must not be a literal (constant).
 Field type can be an enumeration, a predefined type
a collection, array, dictionary or any class or
structure defined in the persistence model.

Property  Must be read-write.


 Must not be static.
 Must not be an explicit interface implementation.
 Property type can be an enumeration, a predefined
type a collection, array, dictionary or any class or
structure defined in the persistence model.

2.3.2 About Predefined Types


Predefined types are .NET types that are automatically saved and restored by Karvonite. As a
result, you don’t need to define them in the persistence model. For simplicity they are already
implicitly defined. Most of the basic primitive CLR types are supported: Boolean, Byte, Char,
DateTime, Decimal, Double, Guid, Int16, Int32, Int64, SByte, Single, String, TimeSpan, UInt16,
UInt32, and UInt64.

2.3.3 About Enumerations


Karvonite can automatically save any field or property whose type is an enumeration (including
flag enumerations). Therefore, you don’t need to define them in the persistence model.

2.3.4 About Collections


Karvonite can automatically save any field or property whose type implements
System.Collections.IList and System.Collections.Generic.ICollection(T) generic

4 Copyright © MP Game Studio 2010. All Rights Reserved.


interface where T must be an enumeration type, a predefined type or any class or structure
defined in the persistence model. This also includes single-dimensional arrays.

2.3.5 About Dictionaries


Karvonite can automatically save any field or property whose type implements
System.Collections.IDictionary and
System.Collections.Generic.IDictionary(TKey, TValue) generic interface where TKey
and TValue must be an enumeration type, a predefined type or any class or structure defined in
the persistence model.

2.4 Object Libraries


An object library is responsible for storing object instances. It is a proprietary data storage
engineered and optimized for fast data retrieval and rapid, trouble-free deployment. Because an
object library runs in the same process as the .NET application, it has a significant performance
advantage even over many fast native databases.

Object libraries require no special installation or administration. Deployment becomes nothing


more than copy the object library file to its destination.

Object libraries are logically binded to a persistence model (Figure 2-1) and they do not contain
embedded metadata. As a result you don't have to worry about how the persistent store is
designed.

Figure 2-1: Any number of object libraries can share the same persistence model

Object
Library

Persistence
Model

Object Object
Library Library

When you modify a persistence model you are indirectly changing every object library binded to
it. These object libraries will automatically evolve at runtime transforming stored data to comply
with the new persistence model with no performance loss.

This is one of the most exciting features in Karvonite. Both the source code and the
persistence model can be freely changed without worrying about the persistent
store.

Copyright © MP Game Studio 2010. All Rights Reserved. 5


2.5 The ObjectSpace
The ObjectSpace is a collection of objects and the bridge between your persistent objects and
the object library. The ObjectSpace type implements the ICollection and ICollection(T)
interfaces.

When you open an ObjectSpace it retrieves object data from the specified object library and
transforms this data into an instance of the corresponding type. The ObjectSpace is
responsible for updating the object library with changes made to these types. All updates to the
object library are transactional, and if an update fails, both the ObjectSpace and the object
library will be unchanged.

Every object is never serialized more than once and has a unique identity. The ObjectSpace
transparently handles circular and shared references.

2.5.1 Change Tracking


The .NET Framework does not provide a way to detect object changes on the fly. To get around
this problem, the only real option is to keep the original state of the object somewhere to
compare against the actual state of it. As a result, the ObjectSpace performs automatic change
tracking by keeping the original state of each object in an internal copy called snapshot.

An ObjectSpace starts tracking your objects for changes as soon as they are retrieved from the
object library. Change tracking and persistence won’t be available for newly created objects until
you add them to the ObjectSpace. The ObjectSpace will track for changes only the objects that
it is aware of. This can happen:

 When saved objects are recreated during the opening process.


 When the ObjectSpace.Save() method is called.

2.5.2 Saving Changes


Despite of how many changes you make to your objects, nothing has yet happened to the
actual data in the object library. Transmission of this information to the object library will not
occur until you call Save() on the ObjectSpace. When you call this method changes are
transactionally committed to the object library.

6 Copyright © MP Game Studio 2010. All Rights Reserved.


2.5.3 Creating an ObjectSpace
An ObjectSpace must be initialized with the name of the persistence model file (compiled as an
embedded resource) and the name of the assembly containing the embedded resource.
// The ObjectSpace will try to load an embedded resource named
// “Model.kvtmodel” from the “MyAssembly” assembly
ObjectSpace os = new ObjectSpace(“Model.kvtmodel”,”MyAssembly”);

string fileName = “c:\\MyLib.dat”;

// Check if object library exists; otherwise a new one is created


if (!File.Exists(fileName))
os.CreateObjectLibrary(fileName, ExistingFileAction.DoNotOverwrite);

// Open connection to the object library


os.Open(fileName, ObjectSpaceMode.ReadWrite);

Do not open and close the ObjectSpace each time you want to retrieve objects. The
ObjectSpace creates instances of all saved objects each time it is opened. Always
keep the ObjectSpace opened. When an ObjectSpace is closed, all object
references are lost.

2.5.4 Adding Objects


In order to persist an object, you must register it with the ObjectSpace using the Add() method.
The type of the object must be a class (reference type) defined in the persistence model;
otherwise an exception will be raised. Objects frequently contain references to other instances
in member variables. The ObjectSpace automatically takes care of every related object that
should be persisted.

Once an object has been added, it will remain in the ObjectSpace until it is removed using the
Remove() method.

The following code shows an example of this:


Room room = new Room();
room.Doors.Add(new Door());
room.Doors.Add(new Door());

myObjectSpace.Add(room);

myObjectSpace.Save();

If you restart the application and retrieve the Room object from the ObjectSpace you will find out
that the Room object is intact.

2.5.5 Removing Objects


As explained above, object instances added using the Add() method will persist into the object
library until they are removed using the Remove() method.

Copyright © MP Game Studio 2010. All Rights Reserved. 7


Once an object has been removed it is no longer tracked by the ObjectSpace and will be
removed from the object library when you call Save().
// Remove Room object from ObjectSpace
myObjectSpace.Remove(room);

// Ensure changes
myObjectSpace.Save();

Because the .NET Framework is a managed execution environment, the object itself
cannot be deleted from memory. The runtime automatically handles object layout
and manages references to objects, releasing them when they are no longer being
used.

2.5.6 Retrieving Objects


You can retrieve objects from the ObjectSpace using the ICollection and ICollection(T)
methods. The following code shows a few examples:
// Foreach
foreach (object obj in myObjectSpace)
{
}

// Retrieves all added rooms


myObjectSpace.OfType<Room>();

// Retrieves all added rooms with more than 2 doors


myObjectSpace.OfType<Room>().Where(room => room.Doors.Count > 2);

// Retrieves any object that implements the ISomething interface


myObjectSpace.OfType<ISomething>();

// Linq query
var rooms = from room in objectSpace.OfType<Room>()
where room.Doors > 2
select room;

8 Copyright © MP Game Studio 2010. All Rights Reserved.


2.6 Persistence Model Evolution
An important factor in succeeding is the ability to deal with change. Things change all the time.
Your types may change several times and in many cases your persistence model will be
affected by these changes. When a persistence model change has been made, this change will
affect any object library that depends on it.

Karvonite uses an implicit evolution mechanism that saves you from doing all this work. The
evolution mechanism will attempt to do this in a way that preserves as much of the data as
possible, transforming data stored in the object library to comply with the new persistence
model. Table 2-1 describes how stored objects are affected by changes in the object model.

Table 2-1: Implicit Evolution Mechanism

In Object Model… In Persistence Model… Comments


A type has been removed. Type must be removed from persistence Saved class instances are
model; otherwise a ignored and cause no
PersistenceModelException will be raised performance loss. Object
when instantiating the ObjectSpace. libraries require no changes
and they will be physically
A concrete class is now No action required.
removed when performing a
abstract.
compact process.
Types

An abstract class is now No action required.


concrete.
A type has been renamed. Type must be renamed; otherwise a
PersistenceModelException will be raised
when instantiating ObjectSpace.

A type has been added. Type might be added if you want to persist
their instances.

A field or property has Data member must be removed from Stored value is ignored and will
been deleted. persistence model; otherwise a be removed from the object
PersistenceModelException will be raised library when performing a
when instantiating ObjectSpace. compact process.
Data Member

A new field or property Data member might be added if you want to Add data member to the
has been added. persist its value. persistence model if you want
to persit it.
A field or property has Data member must be renamed; otherwise
been renamed. a PersistenceModelException will be
raised when instantiating ObjectSpace.

The type of a field or No action required. Karvonite will try to preserve


property has been value(s) if the actual type is
changed. the same kind as the older
one.
A field or property has Drag the data member to the proper class. Data member data is not
been moved up/down in affected.
the inheritance hierarchy.

Copyright © MP Game Studio 2010. All Rights Reserved. 9


2.6.1 Using a Read Only ObjectSpace
There are many scenarios that don’t require updating the objects retrieved from the object
library. In all such cases, it is possible to improve performance and save memory use by
instructing the ObjectSpace not to track the changes:
// Create an object space
ObjectSpace os = new ObjectSpace(“Model.kvtmodel”,“MyAssembly”);

// Open object space


os.Open(“c:\\MyLib.dat”, ObjectSpaceMode.ReadOnly);

10 Copyright © MP Game Studio 2010. All Rights Reserved.


3 Quick Tour
This chapter demonstrates the most basic steps for using the Karvonite Framework. Namely, it
shows how to create a persistence model and how to persist and retrieve object instances. By
the end of this chapter you will be able to use Karvonite in your applications.

Remember that although the samples in this document are shown in C#, you can use the
Karvonite Class Library with any other programming language that complies with the .NET
Common Language Specification (CLS).

The image below shows the object model will be working on.

Figure 3-1: Contact manager object model

Copyright © MP Game Studio 2010. All Rights Reserved. 11


3.1 Creating an Address Book
The first step is declaring the object classes. First of all we’ll create the AddressBook and the
Contact class which is defined as follows:

namespace ContactManager
{
public sealed class AddressBook
{
private readonly Collection<Contact> contacts=new Collection<Contact>();

public Collection<Contact> Contacts


{
get { return contacts; }
}
}
}

namespace ContactManager
{
public class Contact
{
public Contact()
{
}

public string Address {get; set;}

public string FullName {get; set;}

public string PhoneNumber {get; set;}

public override string ToString()


{
return (string.IsNullOrEmpty(FullName) ? “[Empty]” : FullName)
}
}
}

Both class declarations have a few properties for simplicity purposes, but can include
application logic methods.

When it comes to developing object-oriented applications that use relational


databases, the domain object design has to be consistent with the database design.
In most cases the database is designed first, and the O/RM is fitted on top.
In Karvonite, there is no relational model and you don't have to worry at all about
how your persistence storage is designed.

3.2 Creating the Persistence Model


In order to define persistence boundaries Karvonite requires you to create a persistence model.
The persistence model tells Karvonite which object instances and data members (fields and
properties) should be persisted. The ObjectSpace will load the persistence model from an
embedded resource (more on this later).

This procedure shows how to create a persistence model:

12 Copyright © MP Game Studio 2010. All Rights Reserved.


1. Click the Start | Programs | Karvonite 4.0 | Persistence Model Editor menu
command.

2. Click the File | New menu command.


Enter the name for the new persistence model and the location where you want to create
it and the target platform (additional platforms can be added later). Then, click the OK
button to create the persistence model.

NOTE: The Import From field lets you import types and members from an existing model.

3. Click the Add assembly link in the left pane and select the assembly file containing
the AddressBook and Contact types.

4. Click the Add types and members link in the left pane and check the AddressBook
class in the ContactManager namespace. Then check the contacts field and the
Address, FullName and PhoneNumber properties.

NOTE: static classes, interfaces, delegates, generic types, nested types and non-public
types from Microsoft assemblies are not listed since they do not comply with the
Karvonite Persistence Rules.

5. Click the File | Save menu command to save changes.


It's recommended that you save the persistence model file in the same folder as the
application's source files.

As you may notice the mechanism is simple, intuitive and effortless.

Copyright © MP Game Studio 2010. All Rights Reserved. 13


3.2.1 Embedding the Persistence Model
The ObjectSpace will load the persistence model from an embedded resource. If you are using
Microsoft Visual Studio, right-click on your project in Solution Explorer, click Add, and then click
Existing Item. Find your persistence model file and click it. An entry for the persistence model
will appear in Solution Explorer. Click the entry for the model. In the Properties Window, set the
Build Action to Embedded Resource. This will build the file into the assembly as an embedded
resource.

3.3 Extending the Object Model


Having gone through the design of a simple class, we'll extend the object model by:

 Making the Contact type an abstract class.


 Declaring two concrete types of contacts: Person and Company.

The first type of contact is defined as follows:


public enum Gender { Male, Female }

public sealed class Person : Contact


{
private Gender gender;

public Person()
{
}

public Gender Gender {get; set;}


}

The second type of contact is defined as follows:


public sealed class Company : Contact
{
private string businessArea;

public Company()
{
}

public string BusinessArea {get; set;}


}

Finally, we need to update the persistence model (remember to recompile your project first).

1. Click the Add types and members link and add the Gender property in the Person
class and the BusinessArea property in the Company class.

14 Copyright © MP Game Studio 2010. All Rights Reserved.


2. Click the File | Save menu command to save changes.

3.4 Adding a Collection


Let's extend the object model defined above. We will add the ability to associate email
addresses with a contact.

Let’s start by defining the Email class:


public class Email
{
public Email()
{
}

public string Address {get; set;}

public override string ToString()


{
return Address;
}
}

The modified Contact declaration is as follows:


public abstract class Contact
{
private readonly Collection<Email> mails = new Collection<Email>();

protected Contact()
{
}

public string Address {get; set;}

public string FullName {get; set;}

public Collection<Email> Mails


{
get { return mails; }
}

public string PhoneNumber {get; set;}

public override string ToString()


{
return string.IsNullOrEmpty(FullName) ? "[Empty]" : FullName;
}
}

Copyright © MP Game Studio 2010. All Rights Reserved. 15


One of the major points in favor of Karvonite is that you can code without worrying
about persistence. It just works.

Add the new Email type together with its Address property to the persistence model and the
mails field to the Contact class.

As you can see, the object model and the Karvonite API are decoupled. The above classes do
not contain references to the Karvonite class library. The best practice is always to separate the
object model from the presentation layer. This approach decouples your object model into a
completely separate assembly file which may be used from various types of GUI applications
(e.g., WinForms, WPF) or different target platforms such as XNA or the .NET Compact
Framework.

3.5 Putting It All Together


We want to create an address book application that let us:

 Create a new address book.


 Open an existing address book.
 Add contacts (person and companies) to the active address book.
 Save changes made to an address book.
 Close an address book.

16 Copyright © MP Game Studio 2010. All Rights Reserved.


private ObjectSpace os = new ObjectSpace(“ObjectModel.kvtmodel”, ”MyAssembly”);
private AddressBook addressBook;

private void NewAddressBook(string fileName)


{
// Close active address book
CloseAddressBook();

// Create an object library


os.CreateObjectLibrary(fileName, ExistingFileAction.Overwrite);

// Open empty ObjectSpace


os.Open(fileName, ObjectSpaceMode.ReadWrite);

// Create new address book


addressBook = new AddressBook();

// Add address book instance


os.Add(addressBook);

// Ensures changes
os.Save();
}

Note that in the method above, we just are just adding an address book instance to the
ObjectSpace. This is enough. You don’t need to call the ObjectSpace.Add() method never
again in your application. Once you call ObjectSpace.Save(), any new instance that is
attached to the address book object and that is inside the persistence boundaries will be
automatically handled by the ObjectSpace.
private void OpenAddressBook(string fileName)
{
// Open ObjectSpace
os.Open(fileName, ObjectSpaceMode.ReadWrite);

// Retrieve address book (we know that there is just one instance)
AddressBook addressBook = os.OfType<AddressBook>().First();
}

private void AddPerson(string fullName, Gender gender)


{
Person person = new Person();
person.FullName = fullName;
person.Gender = gender;
addressBook.Contacts.Add(person);
}

private void AddCompany(string fullName, Gender gender)


{
Company company = new Company();
company.FullName = fullName;
addressBook.Contacts.Add(company);
}

private void Save()


{
os.Save();
}

private void Close()


{
if (os.HasChanges())
Save();
}

Copyright © MP Game Studio 2010. All Rights Reserved. 17


3.6 Multi-Targeting
The Persistence Model Editor supports multi-targeting - which allows you to use a single
persistence model to target multiple frameworks. To add or remove additional platforms click the
Model | Target Platforms… menu command.

After adding one or more target platforms you’ll get some validation errors. This is because
every assembly in the persistence model must indicate the corresponding assembly path for
each different platform. To do this, right-click an assembly node and then select the Assembly
Location… menu command.

18 Copyright © MP Game Studio 2010. All Rights Reserved.


4 Advanced Topics

4.1 Concurrency
Concurrency is the handling of multiple users attempting to access the same data at the same
time.

For example, consider the Contact Manager sample that allows users to add and edit contacts.
Adding contacts is not a problem since each new contact generates a new object, several users
can simultaneously add contacts to the object library without interfering with one another.

Editing contacts, on the other hand, can result in concurrency problems. Object instances are
created locally on each computer. Once changes are made, the data is sent back to the object
library. Now, if two users simultaneously edit the same contact, they both have object instances
of the same object data, and can make changes to it. What happens if they both choose to save
changes?

Managing multi-user access to a shared resource is a challenge. Any resource that can be
accessed by more than one user requires software logic to protect that resource by managing
the way multiple users can access and change it at the same time.

4.1.1 Concurrency Control Strategy


Karvonite implements an optimistic concurrency control. The idea is to accept the fact that
collisions occur infrequently, and instead of trying to prevent them Karvonite simply chooses to
detect them and then resolve the collision by merging changes. Consequently, when
ObjectSpace.Save() is called, Karvonite will only save those properties that have been
changed and then refresh those objects that have been modified in the meantime.

To enable concurrency you must set the ObjectSpace.EnableConcurrency property to true.


The ObjectSpace must be closed; otherwise an exception will be raised.

Use the ObjectSpace.Refresh() method to refresh in-memory objects without saving


changes. The Refresh() method takes a RefreshMode parameter which defines how the
method handles concurrency conflicts:

 KeepChanges. Forces the Refresh() method to keep data members that have been
locally changed, but updates the unchanged data members with corresponding values
from the object library.

 Overwrite. Forces the Refresh() method to override all data members of each
changed object with corresponding values from the object library.

Use the ObjectSpace.HasRemoteChanges() method to check if one or more objects have


been remotely changed in the object library.

Copyright © MP Game Studio 2010. All Rights Reserved. 19


4.2 Undo
A change block logically groups multiple changes into a single undo unit. Changes within a
change block may be reversed or reapplied repeatedly restoring the ObjectSpace and the
implicated objects to the exact state they were in at the point the change block was started. The
ObjectSpace may hold multiple change blocks, new change blocks are always placed at the
top of the undo list.

A change block is started by calling the ObjectSpace.BeginChange() method. Calling this


method causes all subsequent changes to be included in the started change block until a
corresponding call to the ObjectSpace.EndChange() method is made.

Use the ObjectSpace.CanUndo and ObjectSpace.CanRedo properties to determine whether


there are any operations to be reversed or reapplied.

Use ObjectSpace.GetRedoList() to retrieve the descriptions of the available change blocks


that may have their contents re-applied and ObjectSpace.GetUndoList() to retrieve the
descriptions of the change blocks that may have their contents reversed.

Finally, the ObjectSpace.Undo() and ObjectSpace.Redo() methods undoes/redoes the most


recent change block on the undo/redo stack.

NOTE: The ObjectSpace.Refresh() operation is not undoable and will clear the undo/redo
stacks.

4.2.1 Announcing Object Changes


Since there is no way to detect object changes on the fly such changes must be explicitly
announced to the change block. The BeginChange() has two overloads that take a parameter
containing the list of objects that will be affected by the change action allowing the change block
to record the original values so they can be reversed/reapplied later. The type of each supplied
object must be defined in the persistence model; otherwise an exception will be raised.
os.BeginChange(“Change Address Property”, someContact);
someContact.Address = “Somewhere in the world”;
os.EndChange();

Some changes might spread among other related objects making them change too. This is why
BeginChange() takes a list of objects. Though, this is rarely the case and in the majority of
cases specifying just one object will be enough.

4.2.2 Nested Change Blocks


A nested change block occurs when a new change block is started on an ObjectSpace that is
already inside the scope of an existing change block. The new, nested change block is said to
be nested within (or below the level of) the existing change block. Changes made within the
nested change block are invisible to the top-level change block until the nested change block is
committed. Even then, the changes are not still visible outside the top-level change block until
that change block is committed. BeginChange() returns the level of nesting: a return value of 1

20 Copyright © MP Game Studio 2010. All Rights Reserved.


indicates a top-level change block (that is, a change block that is not nested within another
change block); 2 indicates a second-level change block (a change block nested within a top-
level change block); and so on.

Calling EndChange() will commit or discard the change block at the current (lowest) nesting
level.

Copyright © MP Game Studio 2010. All Rights Reserved. 21


5 Future Directions
Karvonite is continuing to evolve beyond the technology preview described in this document.
Your feedback will also help us shape the Karvonite features.

Tell us what you like about Karvonite, or things that you think we could have done better. Also
let us know if there are any features that you would like to see in future versions:

http://code.msdn.microsoft.com/karvonite

http://twitter.com/karvonite

http://www.facebook.com/pages/Karvonite/114157805264969?ref=ts

22 Copyright © MP Game Studio 2010. All Rights Reserved.


6 Usage Guidelines

 DO separate your object model from your presentation layer. This approach decouples your
object model into a completely separate assembly file which may be used from various
types of GUI applications (e.g., WinForms, WPF) or different target platforms such as XNA
or the .NET Compact Framework.

 DO NOT open and close the ObjectSpace each time you want to add, remove or retrieve
objects. Because there is no delayed object construction the ObjectSpace will create
instances of all saved objects each time it is opened.

In addition, when an ObjectSpace is closed, all references to their tracked objects are lost.
For example, the following sample will result in no changes at all:
ObjectSpace os = new ObjectSpace("Model.kvtmodel”,”MyAssembly");

os.Open("c:\\MyLib.dat", ObjectSpaceMode.ReadWrite);
List<Person> persons = new List<Person>(os.OfType<Person>());
os.Close();

persons[0].FullName = "Mateo Felipe";

// The ObjectSpace will instantiate a new set of Person objects


// persons[0] is no longer being tracked. A new Person
// object with the same data will be created
os.Open("c:\\MyLib.dat", ObjectSpaceMode.ReadWrite);

// Nothing will be saved because nothing has been changed


os.Save();

 CONSIDER using fields instead of properties while creating the persistence model. A
property accessor might be augmented over time with extra actions that you don’t want or
expect to occur. Fields offer better maintainability and scalability to the persistence model:

// Version 1
public string Name
{
get { return name; }
set { name = value; }
}

// Version 2
public string Name
{
get { return name; }
set
{
if (value == null) throw
new ArgumentNullException(“value”);

name = value;
}
}

Copyright © MP Game Studio 2010. All Rights Reserved. 23


The above code shows a typical scenario where a property setter (version 1) is augmented with
some logic (version 2). The augmented setter will raise an exception if the ObjectSpace tries to
assign a null value during object deserialization.

 DO open the ObjectSpace in read-only mode if the scenario doesn’t require updates. In all
such cases, it is possible to improve performance and significantly reduce memory use by
instructing the ObjectSpace not to track changes which will suppress the creation of internal
copies.

ObjectSpace os = new ObjectSpace(“Model.kvtmodel”,”MyAssembly”);


os.Open(“c:\\MyLib.dat”, ObjectSpaceMode.ReadOnly);

 DO call ObjectSpace.Dump() in scenarios where you just need to create a new object
library from scratch, add objects and immediately close the ObjectSpace. For example,
saving a game session, creating a log file and so on.
ObjectSpace os = new ObjectSpace(“Model.kvtmodel”,”MyAssembly”);

object[] items = new object[] { gameSessionInfo, world };

Os.Dump(“c:\\MyLib.dat”, items);

24 Copyright © MP Game Studio 2010. All Rights Reserved.

You might also like