Skip to content

Commit c8a34e1

Browse files
committed
CSHARP-1396: added eventing docs to core.
1 parent a551f55 commit c8a34e1

File tree

4 files changed

+163
-3
lines changed

4 files changed

+163
-3
lines changed

Docs/reference/content/reference/driver/connecting.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ var settings = new MongoClientSettings { ReplicaSetName = "rs0" };
7676
var client = new MongoClient(settings);
7777
```
7878

79+
### Low-Level Customization
80+
81+
There are a number of settings that are not configurable directly from [`MongoClientSettings`]({{< apiref "T_MongoDB_Driver_MongoClientSettings" >}}). These settings are able to be configured through the [`ClusterConfigurator`]({{< apiref "P_MongoDB_Driver_MongoClientSettings_ClusterConfigurator" >}}) property which provides a [`ClusterBuilder`]({{< apiref "T_MongoDB_Driver_Core_Configuration_ClusterBuilder" >}}) to manipulate. An example of these is adding a logger using the [eventing infrastructure]({{< relref "reference\driver_core\events.md" >}}).
82+
7983
### Re-use
8084

8185
It is recommended to store a [`MongoClient`]({{< apiref "T_MongoDB_Driver_MongoClient" >}}) instance in a global place, either as a static variable or in an IoC container with a singleton lifetime.

Docs/reference/content/reference/driver/index.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,4 @@ The MongoDB .NET Driver is mostly just a wrapper around [MongoDB.Driver.Core]({{
1919
- [Administration]({{< relref "reference\driver\admin.md" >}})
2020
- [Definitions and Builders]({{< relref "reference\driver\definitions.md" >}})
2121
- [CRUD Operations]({{< relref "reference\driver\crud\index.md" >}})
22-
- [Error Handling]({{< relref "reference\driver\error_handling.md" >}})
23-
- Experimental Features
22+
- [Error Handling]({{< relref "reference\driver\error_handling.md" >}})
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
+++
2+
date = "2015-09-08T15:36:56Z"
3+
draft = false
4+
title = "Eventing"
5+
[menu.main]
6+
parent = "Driver Core"
7+
identifier = "Eventing"
8+
weight = 60
9+
pre = "<i class='fa'></i>"
10+
+++
11+
12+
## Eventing
13+
14+
The .NET Driver core provides a robust model for event publication and subscription. Each event is represented by a class or struct which contains all the information related to the particular event.
15+
16+
17+
### ClusterBuilder
18+
19+
The [`ClusterBuilder`]({{< apiref "T_MongoDB_Driver_Core_Configuration_ClusterBuilder" >}}) class contains two methods for subscribing to events.
20+
21+
22+
#### IEventSubscriber
23+
24+
The first version of [`Subscribe`]({{< apiref "M_MongoDB_Driver_Core_Configuration_ClusterBuilder_Subscribe" >}}) takes an [`IEventSubscriber`]({{< apiref "T_MongoDB_Driver_Core_Events_IEventSubscriber" >}}). [`IEventSubscriber`]({{< apiref "T_MongoDB_Driver_Core_Events_IEventSubscriber" >}}) contains a single method to implement, [`TryGetEventHandler`]({{< apiref "M_MongoDB_Driver_Core_Events_IEventSubscriber_TryGetEventHandler_1" >}}). It takes a generic parameter indicating the type of event and sets an out parameter with the handler.
25+
26+
{{% note %}}This method will be invoked once on each subscriber per event type. Therefore, performance of this method is not critical.{{% /note %}}
27+
28+
For instance, to handle the [`ConnectionPoolAddedConnectionEvent`]({{< apiref "T_MongoDB_Driver_Core_Events_ConnectionPoolAddedConnectionEvent" >}}), the following could be done:
29+
30+
```csharp
31+
public class MyEventSubscriber : IEventSubscriber
32+
{
33+
public bool TryGetEventHandler<TEvent>(out Action<TEvent> handler)
34+
{
35+
if(typeof(TEvent)) == typeof(ConnectionPoolAddedConnectionEvent))
36+
{
37+
handler = (Action<TEvent>)HandleConnectionPoolAddedConnectionEvent;
38+
return true;
39+
}
40+
41+
handler = null;
42+
return false;
43+
}
44+
45+
private void HandleConnectionPoolAddedConnectionEvent(ConnectionPoolAddedConnectionEvent @event)
46+
{
47+
Console.WriteLine("Added a connection to the pool.");
48+
}
49+
}
50+
```
51+
52+
This could quickly become unmaintainable with multiple events. To make this easier, we have implemented the [`ReflectionEventSubscriber`]({{< apiref "T_MongoDB_Driver_Core_Events_ReflectionEventSubscriber" >}}). It uses reflection to find all the event handler methods inside a class based on certain constructor parameters for the method name and the binding flags. For instance, we could change the above `MyEventSubscriber` class as follows:
53+
54+
```csharp
55+
public class MyEventSubscriber : IEventSubscriber
56+
{
57+
private readonly IEventSubscriber _subscriber;
58+
59+
public MyEventSubscriber()
60+
{
61+
_subscriber = new ReflectionEventSubscriber(this);
62+
}
63+
64+
public bool TryGetEventHandler<TEvent>(out Action<TEvent> handler)
65+
{
66+
return _subscriber.TryGetEventHandler(out handler);
67+
}
68+
69+
private void Handle(ConnectionPoolAddedConnectionEvent @event)
70+
{
71+
Console.WriteLine("Added a connection to the pool.");
72+
}
73+
74+
private void Handle(ConnectionPoolRemovedConnectionEvent @event)
75+
{
76+
Console.WriteLine("Removed a connection from the pool.");
77+
}
78+
}
79+
```
80+
81+
{{% note %}}The default method name is "Handle" and the default binding flags are for public instance methods.{{% /note %}}
82+
83+
The [`PerformanceCounterEventSubscriber`]({{< srcref "MongoDB.Driver.Core/Core/Events/Diagnostics/PerformanceCounterEventSubscriber.cs" >}}) is a good example of utilizing the [`ReflectionEventSubscriber`]({{< apiref "T_MongoDB_Driver_Core_Events_ReflectionEventSubscriber" >}}).
84+
85+
86+
#### Method
87+
88+
The second version of [`Subscribe`]({{< apiref "M_MongoDB_Driver_Core_Configuration_ClusterBuilder_Subscribe" >}}) takes an [`Action<TEvent>`]({{< msdnref "018hxwa8" >}}). For example, to use a static method:
89+
90+
```csharp
91+
static void Main()
92+
{
93+
var builder = new ClusterBuilder();
94+
95+
builder.Subscribe<ConnectionPoolAddedConnectionEvent>(Handle);
96+
97+
// ... snip
98+
}
99+
100+
private static void Handle(ConnectionPoolAddedConnectionEvent @event)
101+
{
102+
Console.WriteLine("Added a connection to the pool.");
103+
}
104+
```
105+
106+
Alternatively, a lambda expression could be used:
107+
108+
```csharp
109+
static void Main()
110+
{
111+
var builder = new ClusterBuilder();
112+
113+
builder.Subscribe<ConnectionPoolAddedConnectionEvent>(x => Console.WriteLine("Added a connection to the pool."));
114+
115+
// ... snip
116+
}
117+
```
118+
119+
120+
### Operation Ids
121+
122+
Any commands that could occur based on user initiation will contain an operation identifier. This identifer can be used to link together all events that occured due to the user initiated action.
123+
124+
125+
### ClusterId, ServerId, and ConnectionId
126+
127+
All events will contain at least one of these identifiers. They can be used to uniquely attribute a particular event to a cluster, a server, or a connection. In addition, the ConnectionId also contains a local value and a server value where the server value contains the same value that will show up in the server logs for its connection logging.
128+
129+
130+
### Command Events
131+
132+
There are three events related to monitoring data sent on the wire. These are the [`CommandStartedEvent`]({{< apiref "T_MongoDB_Driver_Core_Events_CommandStartedEvent" >}}), the [`CommandSucceededEvent`]({{< apiref "T_MongoDB_Driver_Core_Events_CommandSucceededEvent" >}}), and the [`CommandFailedEvent`]({{< apiref "T_MongoDB_Driver_Core_Events_CommandFailedEvent" >}}). For every started event, there will always be a succeeded or failed event.
133+
134+
In addition, any messages sent to the server that are not already commands will be upconverted for the sake of consumption. For instance, an `OP_DELETE` wire protocol message on server 2.4 will appear as though it were a [`delete`]({{< docsref "reference/command/delete/" >}}) command.
135+
136+
{{% note class="warning" %}}These are heavy events to generate. Do not subscribe to these unless they provide necessary information.{{% /note %}}
137+
138+
{{% note %}}Certain information has been removed for security reasons. For instance, the [`authenticate`]({{< docsref "reference/command/authenticate/" >}}) command will not contain the actual command or its reply. However, it will still contain the command name itself.{{% /note %}}
139+
140+
141+
#### CommandStartedEvent
142+
143+
The [`CommandStartedEvent`]({{< apiref "T_MongoDB_Driver_Core_Events_CommandStartedEvent" >}}) contains, amoungst other information, the command name as well as the command itself. While the command also contains the command name, the command is potentially heavy to access and will not live beyond the lifetime of the event. Any information necessary from the command should be pulled out and used immediately or stored.
144+
145+
146+
#### CommandSucceededEvent
147+
148+
The [`CommandSucceededEvent`]({{< apiref "T_MongoDB_Driver_Core_Events_CommandSucceededEvent" >}}) contains, amoungst other information, the command name, the duration of the command, and the reply. The reply is potentially heavy to access and will not live beyond the lifetime of the event. Any information necessary from the reply should be pulled out and used immediately or stored.
149+
150+
151+
#### CommandFailedEvent
152+
153+
The [`CommandFailedEvent`]({{< apiref "T_MongoDB_Driver_Core_Events_CommandFailedEvent" >}}) contains, amoungst other information, the command name, the duration of the command, and the exception. The exception is potentially heavy to access and will not live beyond the lifetime of the event. Any information necessary from the exception should be pulled out and used immediately or stored.

Docs/reference/content/reference/driver_core/index.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,8 @@ A large number of operations have been implemented for everything from a generic
3535

3636
### Bindings
3737

38-
Bindings glue together server selection and operation execution by influencing how and where operations get executed. It would be possible to construct bindings that, for instance, pipeline multiple operations down the same connection or ensure that OP_GETMORE requests are sent down the same connection as the initial OP_QUERY.
38+
Bindings glue together server selection and operation execution by influencing how and where operations get executed. It would be possible to construct bindings that, for instance, pipeline multiple operations down the same connection or ensure that OP_GETMORE requests are sent down the same connection as the initial OP_QUERY.
39+
40+
### [Eventing]({{< relref "events.md" >}})
41+
42+
The driver provides many events related to server selection, connection pooling, cluster monitoring, command execution, etc... These events are subscribable to provide solutions such as logging and performance counters.

0 commit comments

Comments
 (0)