Escolar Documentos
Profissional Documentos
Cultura Documentos
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.
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.
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
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.
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.
Karvonite
Persistence Class Library
Model Editor Assembly
Application (Karvonite40.dll)
.NET
.NET
Compact XNA Framework
Framework
Framework
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).
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.
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.
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.
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.
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.
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:
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.
Once an object has been added, it will remain in the ObjectSpace until it is removed using the
Remove() method.
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.
// 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.
// Linq query
var rooms = from room in objectSpace.OfType<Room>()
where room.Doors > 2
select room;
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.
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.
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.
namespace ContactManager
{
public sealed class AddressBook
{
private readonly Collection<Contact> contacts=new Collection<Contact>();
namespace ContactManager
{
public class Contact
{
public Contact()
{
}
Both class declarations have a few properties for simplicity purposes, but can include
application logic methods.
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.
public Person()
{
}
public Company()
{
}
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.
protected Contact()
{
}
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.
// 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();
}
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.
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.
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.
NOTE: The ObjectSpace.Refresh() operation is not undoable and will clear the undo/redo
stacks.
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.
Calling EndChange() will commit or discard the change block at the current (lowest) nesting
level.
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
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();
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;
}
}
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.
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”);
Os.Dump(“c:\\MyLib.dat”, items);