Repository navigation
platform sdk detecting entity changes
Entity change notifications help your application keep its view of Security Center entities current. Use them when your application must react to edits to entity properties, custom field values, relationships, or supported runtime states.
These notifications signal that your application should reread current entity data and update its saved state. Engine-level notifications identify affected entities by globally unique identifier (GUID) and entity type. Instance events normally identify the entity through sender. The notifications are not a history of what changed, and most do not include the previous value, the new value, or the name of the changed property.
Each Engine stores entity data in its own entity cache. Its lifecycle events report when entity data enters that cache, is invalidated, or is removed. These events describe the cache, not a history of operations in Security Center. For the complete cache lifecycle, see About the Engine entity cache.
First decide whether your application needs an Engine cache notification or an event from a loaded entity object.
Use the Engine-level lifecycle events to monitor changes to the Engine's entity cache.
| Notification | Use it when | Available information |
|---|---|---|
Engine.EntitiesAdded |
You need to know when the Engine adds an entity to its cache | A batch of entity GUIDs and entity types, plus IsLocalUpdate for the batch |
Engine.EntitiesInvalidated |
You need to refresh cached data after an update invalidates one or more monitored entities | A batch of entity GUIDs and entity types, plus IsLocalUpdate for the batch |
Engine.EntitiesRemoved |
You need to remove saved application state after the Engine removes an entity from its cache | A batch of entity GUIDs and entity types, plus IsLocalUpdate for the batch |
EntitiesAdded has two common meanings: the Engine loaded an existing entity for the first time, or it received a newly created entity after registering that entity type. The event does not distinguish between these cases.
EntitiesRemoved can follow deletion from Security Center or an SDK rollback of a local creation that did not succeed. It is not raised merely because disconnection clears the Engine cache.
Use events from a loaded entity object when your application monitors particular fields, custom fields, relationships, or runtime states.
| Notification | Use it when | Available information |
|---|---|---|
Entity.FieldsChanged |
You monitor common built-in properties or a supported type-specific setting on one loaded entity | The entity in sender and IsLocalUpdate
|
Entity.CustomFieldsChanged |
You monitor custom field values on one loaded entity | The entity in sender; the event uses EventArgs and does not report IsLocalUpdate
|
| Entity-specific events | You monitor one relationship or supported state, such as group members, alarm recipients, cardholder credentials, or access rules | The entity and an event-specific argument type; many relationship events report IsLocalUpdate
|
Use EntitiesInvalidated as a broad signal. Each EntityUpdateInfo contains only EntityGuid and EntityType, so the handler must read the current values that it needs.
EntitiesInvalidated and FieldsChanged are not equivalent. EntitiesInvalidated is the broader cache notification. FieldsChanged is an instance event raised only for supported configuration-field updates on a loaded entity.
The same configuration update can raise both events. For example, changing the name of a loaded cardholder group can raise FieldsChanged for the group and include the group in EntitiesInvalidated. Other updates do not raise FieldsChanged. Adding a cardholder to the group can raise CardholderGroup.MembersChanged and include the group in EntitiesInvalidated without raising FieldsChanged.
Use FieldsChanged for common built-in properties such as Name and Description, and for some type-specific configuration properties. Even a built-in scalar property is not guaranteed to raise this event. Use EntitiesInvalidated when your application must refresh a broader set of entity data. FieldsChanged is also not a fallback for relationship, custom field value, or runtime state changes.
Use CustomFieldsChanged when any custom field value on a specific entity matters. The event does not identify the custom field. Keep the values your application uses and compare them with the entity's current values after the event.
Use relationship-specific events when the relationship itself matters. Examples include Alarm.RecipientsChanged, AccessPoint.AccessRulesChanged, Cardholder.CredentialChanged, and CardholderGroup.MembersChanged. For the supported change events and the payload of each event, see Entity change event reference.
Logging on connects the Engine to Security Center. It does not automatically load every entity or subscribe to every type of change.
Loading, filtering, and type registration have different purposes:
- Loading retrieves entity data and places it in the Engine's entity cache. Load an entity when your application needs its properties, relationships, or instance events.
- Query filters determine which entities the query returns.
- Type registration enables the Engine to receive notifications for an entity type. Adding a type to
EntityTypeFilterboth filters the query results and registers that complete entity type. Other query filters can narrow the results, but they do not narrow the type registration.
Type registration allows the Engine to report future additions and a limited set of common or type-specific updates. It does not load every entity, load relationship data, or guarantee every invalidation and removal notification.
Choose how to load and register entities based on what your application monitors:
- To monitor known entities, use
EntityGuidsin a non-GuidsOnlyquery. Alternatively, callEngine.GetEntity. Then read the properties and relationships that your application uses before subscribing to instance events. - To receive notifications when entities of a type are added, add that type to
EntityTypeFilter. The registration covers the complete entity type, even when other filters restrict the query results. - To monitor a relationship, load the relationship before subscribing to its event. Set
DownloadAllRelatedDatatotrue, or read the relationship property.
Important
A GuidsOnly query returns entity identifiers without loading entity properties or relationships. GuidsOnly does not register an entity type by itself. If the query also contains an EntityTypeFilter, that filter still registers its entity types. Do not rely on a GuidsOnly query alone for entity-instance events or for invalidation and removal notifications about the returned entities.
For example, to monitor a known cardholder group, retrieve the CardholderGroup, read Children, and then subscribe to MembersChanged. Load each cardholder's Parents only if your application must also refresh the cardholder side of the relationship.
IsLocalUpdate reports whether the SDK classifies the notification as local to the same Engine instance:
-
true: The sameEngineinstance initiated the operation, and the SDK reports the notification as local. -
false: The SDK does not report the notification as local to thatEngineinstance.
A notification can have IsLocalUpdate set to false for these reasons:
- The Engine received the update from Security Center. This includes changes made through Config Tool, Security Desk, a system role, or another
Engineinstance. - The SDK raised the notification while rolling back a local operation that did not succeed.
-
IEngine.ViewLocalUpdatesAsRemoteistrue, so the Engine reports its local updates as remote.
The value does not identify the person or component that initiated the operation. A local operation can raise a notification with IsLocalUpdate set to true before the operation succeeds. If the operation does not succeed, the SDK can raise a later rollback notification with IsLocalUpdate set to false.
CustomFieldsChanged does not provide IsLocalUpdate. If origin matters for custom field changes, handle Engine.EntitiesInvalidated for the entity and compare the current custom field values with your saved values. Do not infer the origin from CustomFieldsChanged.
For configuration and relationship notifications, treat the notification as a request to refresh saved application state. Read the current values from the entity object or its loaded relationship collection. Do not try to reconstruct a change from event arguments that do not contain a field-level delta.
For a runtime-state event that provides the reported state, use the event payload for that transition. A cached entity property is not guaranteed to be refreshed before the callback. For example, DoorIOStateChangeEventArgs.IsActive can report that a door sensor is inactive before Door.IsOpen reflects the closed state. If your application needs a complete entity snapshot, defer or coalesce the refresh, and then reread and compare the properties that it uses.
Do not assume that a handler runs on the user interface thread. If a handler updates user interface controls, marshal that work to the application's dispatcher. Keep shared application state synchronized with the mechanism used by your application.
One operation can produce more than one notification. The same event can be raised more than once, and an Engine batch can contain the same entity more than once. Changing group membership can also invalidate both sides of the relationship and raise a relationship-specific event. If you subscribe to both Engine-level and entity-level notifications, send them through one refresh method so that the same operation does not trigger duplicate work.
Design the refresh method so that running it twice produces the same application state as running it once. When an invalidation batch contains multiple entries for work that you can combine, group the work by EntityUpdateInfo.EntityGuid before refreshing application state.
After loading a cardholder and its credential relationship, retrieve the Cardholder object and subscribe to its change events. The following class uses a separate method for each event, such as OnFieldsChanged. It unsubscribes those methods in Dispose and routes every notification through one application-supplied refresh method. That method must reread the properties and collections the application uses, compare them with its saved snapshot, and perform work only when the values differ.
The constructor subscribes before calling the refresh method so that notifications raised during the initial refresh are handled. One of these event-handler methods can run before the initial refresh finishes. The refresh method must synchronize access to its saved snapshot, tolerate repeated or concurrent calls, and not depend on which call finishes first.
using System;
using Genetec.Sdk;
using Genetec.Sdk.Entities;
public sealed class CardholderChangeMonitor : IDisposable
{
private readonly Cardholder m_cardholder;
private readonly Action<Cardholder, bool?> m_refresh;
public CardholderChangeMonitor(
Cardholder cardholder,
Action<Cardholder, bool?> refresh)
{
m_cardholder = cardholder;
m_refresh = refresh;
m_cardholder.FieldsChanged += OnFieldsChanged;
m_cardholder.CustomFieldsChanged += OnCustomFieldsChanged;
m_cardholder.CredentialChanged += OnCredentialChanged;
m_refresh(m_cardholder, null);
}
private void OnFieldsChanged(
object sender,
FieldsChangedEventArgs e)
{
m_refresh(m_cardholder, e.IsLocalUpdate);
}
private void OnCustomFieldsChanged(
object sender,
EventArgs e)
{
m_refresh(m_cardholder, null);
}
private void OnCredentialChanged(
object sender,
CredentialsChangedEventArg e)
{
m_refresh(m_cardholder, e.IsLocalUpdate);
}
public void Dispose()
{
m_cardholder.FieldsChanged -= OnFieldsChanged;
m_cardholder.CustomFieldsChanged -= OnCustomFieldsChanged;
m_cardholder.CredentialChanged -= OnCredentialChanged;
}
}The nullable origin argument is null for the initial read and for CustomFieldsChanged, which does not report IsLocalUpdate. A value of false means that the SDK did not classify the notification as local to the same Engine instance.
Event publishers retain subscriptions. Subscribe with methods that you can later unsubscribe, such as OnFieldsChanged. Unsubscribe each method when the monitoring component is disposed, when the monitored entity changes, or before the Engine disconnects. Apply the same cleanup to Engine.EntitiesInvalidated and to entity-specific events.
Entity change notifications and Security Center events answer different questions, but one occurrence can produce both:
- Entity change notifications answer, "Does my application need to refresh current entity data?"
-
Engine.EventReceivedanswers, "What security operation or occurrence was reported?"
For example, renaming a door is only an entity change. A door opening is an operational Security Center event and can also raise Door.SensorStateChanged, which reports the Door's current sensor state. Use the state event when your application maintains current Door state. Use Engine.EventReceived when it needs the operation's event type, source, context, or timestamp. Do not use EntitiesInvalidated as a replacement for the Security Center event stream. For subscription guidance, see Working with Security Center events.
- Understanding entity loading, cache behavior, and query options: Choosing how to load entity data and relationships.
- Overview
- Connecting to Security Center
- SDK certificates
- Referencing SDK assemblies
- ASP.NET hosting
- SDK compatibility
- Migrating to modern .NET
- Entities
- Entity cache
- Entity loading, cache, and query options
- Detecting entity changes
- Entity change event reference
- Transactions
- Security Center events
- Actions
- Security Desk
- Custom events
- Event-to-actions
- Managing macros
- ReportManager
- ReportManager query reference
- Access control raw events
- DownloadAllRelatedData and StrictResults
- Privileges
- Partitions
- Entity write restrictions
- About custom fields
- About video
- About cameras
- Enrolling a video unit
- Ingesting third-party video
- Archiver and auxiliary archiver roles
- Archive transfer
- About access control
- About cardholders and credentials
- About doors, areas, elevators, and access points
- About access rules and schedules
- About access control units and interface modules
- Enrolling an access control unit
- Door templates
- Visitors
- Mobile credentials
- About threat levels
- About alarms
- About incidents
- Maps
- Logging
- Overview
- Localization
- Certificates
- Lifecycle
- Threading
- State management
- Configuration
- Restricted configuration
- Events
- Queries
- Request manager
- Database
- Entity ownership
- Engine extensions
- Entity mappings
- Server management
- Custom privileges
- Custom entity types
- Resolving non-SDK assemblies
- Deploying plugins
- Modern .NET support
- Overview
- Localization
- Certificates
- Creating modules
- Tasks
- Pages
- Components
- Tile extensions
- Services
- Contextual actions
- Custom actions
- Options extensions
- Configuration pages
- Monitors
- Shared components
- Commands
- Extending events
- Map extensions
- Timeline providers
- Image extractors
- Credential encoders
- Credential readers
- Cardholder fields extractors
- Badge printers
- Content builders
- Dashboard widgets
- Incidents
- Logon providers
- Pinnable content builders
- Custom report pages
- Overview
- Getting started
- Media file formats
- MediaPlayer
- VideoSourceFilter
- MediaExporter
- MediaFile
- G64 converters
- FileCryptingManager
- PlaybackSequenceQuerier
- PlaybackStreamReader
- OverlayFactory
- PtzCoordinatesManager
- AudioTransmitter
- AudioRecorder
- AnalogMonitorController
- Camera blocking
- Overview
- Getting started
- Referencing entities
- Entity operations
- About access control in the Web SDK
- About video in the Web SDK
- Users and user groups
- Partitions
- Custom fields
- Custom card formats
- Actions
- Events and alarms
- Incidents
- Reports
- Tasks
- Macros
- Custom entity types
- System endpoints
- Performance guide
- Reference
- Endpoints
- Constants
- Enumerations
- Under the hood
- Troubleshooting