Committed callbacks can be used in various scenarios:
In our example we will create an implementation for the last case.
When several clients are working
on the same objects it is very possible that the data will be outdated on some
of the clients. Before the commit-callbacks feature was introduced the solution
was to call refresh
regularly to get object updates from the
server. With the commit-callback this process can be easily automated:
Let's open 2 clients, which will work with Car objects, and register committed event listeners for them.
01private IObjectContainer OpenClient() 02
{ 03
try 04
{ 05
IObjectContainer client = Db4oFactory.OpenClient("localhost", Port, User, 06
Password); 07
CommitEventHandler committedEventHandler = CreateCommittedEventHandler(client); 08
IEventRegistry eventRegistry = EventRegistryFactory.ForObjectContainer(client); 09
eventRegistry.Committed += committedEventHandler; 10
// save the client-listener pair in a map, so that we can 11
// remove the listener later 12
clientListeners.Add(client, committedEventHandler); 13
return client; 14
} 15
catch (Exception ex) 16
{ 17
System.Console.WriteLine(ex.ToString()); 18
} 19
return null; 20
}
01private CommitEventHandler CreateCommittedEventHandler(IObjectContainer objectContainer) 02
{ 03
return new CommitEventHandler(delegate(object sender, CommitEventArgs args) 04
{ 05
// get all the updated objects 06
IObjectInfoCollection updated = args.Updated; 07
08
foreach (IObjectInfo info in updated) 09
{ 10
Object obj = info.GetObject(); 11
// refresh object on the client 12
objectContainer.Ext().Refresh(obj, 2); 13
} 14
}); 15
}
01Private Function OpenClient() As IObjectContainer 02
Try 03
Dim client As IObjectContainer = Db4oFactory.OpenClient("localhost", Port, User, Password) 04
Dim committedEventHandler As CommittedEventHandler = New CommittedEventHandler(client) 05
Dim eventRegistry As IEventRegistry = EventRegistryFactory.ForObjectContainer(client) 06
AddHandler eventRegistry.Committed, AddressOf committedEventHandler.OnCommitted 07
' save the client-listener pair in a map, so that we can 08
' remove the listener later 09
clientListeners.Add(client, committedEventHandler) 10
Return client 11
Catch ex As Exception 12
System.Console.WriteLine(ex.ToString) 13
End Try 14
Return Nothing 15
End Function
01' Copyright (C) 2007 db4objects Inc. http://www.db4o.com 02
Imports Db4objects.Db4o 03
Imports Db4objects.Db4o.Events 04
Imports Db4objects.Db4o.Ext 05
Imports Db4objects.Db4o.Foundation 06
07
08
Namespace Db4objects.Db4odoc.CommitCallbacks 09
Public Class CommittedEventHandler 10
11
Private _objectContainer As IObjectContainer 12
13
Private Delegate Sub OnCommittedHandler(ByVal sender As Object, ByVal args As CommitEventArgs) 14
15
Public Sub New(ByVal objectContainer As IObjectContainer) 16
_objectContainer = objectContainer 17
End Sub 18
19
Private Function CreateCommittedEventHandler(ByVal objectContainer As IObjectContainer) As OnCommittedHandler 20
Return AddressOf OnCommitted 21
End Function 22
23
Public Sub OnCommitted(ByVal sender As Object, ByVal args As CommitEventArgs) 24
' get all the updated objects 25
Dim updated As IObjectInfoCollection = args.Updated 26
For Each info As IObjectInfo In updated 27
Dim obj As Object = info.GetObject() 28
' refresh object on the client 29
_objectContainer.Ext().Refresh(obj, 2) 30
Next 31
End Sub 32
33
End Class 34
End Namespace
Run the following method to see how the 2 clients work concurrently on the same object:
01public void Run() 02
{ 03
File.Delete(Db4oFileName); 04
IObjectServer server = Db4oFactory.OpenServer(Db4oFileName, Port); 05
try 06
{ 07
server.GrantAccess(User, Password); 08
09
IObjectContainer client1 = OpenClient(); 10
IObjectContainer client2 = OpenClient(); 11
12
if (client1 != null && client2 != null) 13
{ 14
try 15
{ 16
// wait for the operations to finish 17
WaitForCompletion(); 18
19
// save pilot with client1 20
Car client1Car = new Car("Ferrari", 2006, new Pilot("Schumacher")); 21
client1.Set(client1Car); 22
client1.Commit(); 23
24
WaitForCompletion(); 25
26
// retrieve the same pilot with client2 27
Car client2Car = (Car)client2.Query(typeof(Car)).Next(); 28
System.Console.WriteLine(client2Car); 29
30
// modify the pilot with pilot1 31
client1Car.Model = 2007; 32
client1Car.Pilot = new Pilot("Hakkinnen"); 33
client1.Set(client1Car); 34
client1.Commit(); 35
36
WaitForCompletion(); 37
38
// client2Car has been automatically updated in 39
// the committed event handler because of the 40
// modification and the commit by client1 41
System.Console.WriteLine(client2Car); 42
43
WaitForCompletion(); 44
} 45
catch (Exception ex) 46
{ 47
System.Console.WriteLine(ex.ToString()); 48
} 49
finally 50
{ 51
CloseClient(client1); 52
CloseClient(client2); 53
} 54
} 55
} 56
catch (Exception ex) 57
{ 58
System.Console.WriteLine(ex.ToString()); 59
} 60
finally 61
{ 62
server.Close(); 63
} 64
}
01Public Sub Run() 02
File.Delete(Db4oFileName) 03
Dim server As IObjectServer = Db4oFactory.OpenServer(Db4oFileName, Port) 04
Try 05
server.GrantAccess(User, Password) 06
Dim client1 As IObjectContainer = OpenClient 07
Dim client2 As IObjectContainer = OpenClient 08
If Not (client1 Is Nothing) AndAlso Not (client2 Is Nothing) Then 09
Try 10
' wait for the operations to finish 11
WaitForCompletion() 12
13
'save pilot with client1 14
Dim client1Car As Car = New Car("Ferrari", 2006, New Pilot("Schumacher")) 15
client1.Set(client1Car) 16
client1.Commit() 17
WaitForCompletion() 18
19
' retrieve the same pilot with client2 20
Dim client2Car As Car = CType(client2.Query(GetType(Car)).Next, Car) 21
System.Console.WriteLine(client2Car) 22
23
' modify the pilot with pilot1 24
client1Car.Model = 2007 25
client1Car.Pilot = New Pilot("Hakkinnen") 26
client1.Set(client1Car) 27
client1.Commit() 28
WaitForCompletion() 29
30
' client2Car has been automatically updated in 31
' the committed event handler because of the 32
' modification and the commit by client1 33
System.Console.WriteLine(client2Car) 34
WaitForCompletion() 35
Catch ex As Exception 36
System.Console.WriteLine(ex.ToString) 37
Finally 38
CloseClient(client1) 39
CloseClient(client2) 40
End Try 41
End If 42
Catch ex As Exception 43
System.Console.WriteLine(ex.ToString) 44
Finally 45
server.Close() 46
End Try 47
End Sub
You should see that client2 picked up the changes committed from the client1 automatically due to the committed event handler.
Working with the committed event you should remember that
the listener is called in a separate thread, which needs to be synchronized
with the main application thread. This functionality is not implemented in the presented example, instead a simple thread Sleep(1000) method is used (WaitForCompletion method), which is not realiable at all. For a reliable execution use events and notifications from the committed callbacks.
It is a good practice to remove the committed event handlers from the registry before shutting down the clients:
01private void CloseClient(IObjectContainer client) 02
{ 03
// remove listeners before shutting down 04
if (clientListeners[client] != null) 05
{ 06
IEventRegistry eventRegistry = EventRegistryFactory.ForObjectContainer(client); 07
eventRegistry.Committed -= (CommitEventHandler)clientListeners[client]; 08
clientListeners.Remove(client); 09
} 10
client.Close(); 11
}
1Private Sub CloseClient(ByVal client As IObjectContainer) 2
' remove listeners before shutting down 3
If Not (clientListeners(client) Is Nothing) Then 4
Dim eventRegistry As IEventRegistry = EventRegistryFactory.ForObjectContainer(client) 5
RemoveHandler eventRegistry.Committed, AddressOf CType(clientListeners(client), CommittedEventHandler).OnCommitted 6
clientListeners.Remove(client) 7
End If 8
client.Close() 9
End Sub