ObjectSync is a C# analyzer that generates code for synchronizing properties. This analyzer is a work in progress.
- Generation of Synchronized Properties from annotated fields
- Threadsafe Synchronization of Properties
- Pre-Built Static Synchronization Authority for synchronizing inside a single Appdomain
The most recent version of ObjectSync is 1.1.2.
ObjectSync uses Semantic Versioning 2.0.0.
Nuget Gallery: https://www.nuget.org/packages/RhoMicro.ObjectSync
Package Manager: Install-Package RhoMicro.ObjectSync -Version 1.1.2
.Net CLI: dotnet add package RhoMicro.ObjectSync --version 1.1.2
ObjectSync works by generating a partial class for types annotated with the SynchronizationTarget
attribute. It will generate a nested context type that manages synchronization state and logic for instances of your type.
using ObjectSync.Attributes;
using ObjectSync.Synchronization;
namespace MyNamespace
{
[SynchronizationTarget]
internal partial class MyType
{
}
}
Note that is currently required to declare synchronized types inside a namespace.
Your type must provide a synchronization authority property for use by this context. Synchronization authorities are responsible for communication between contexts. As such, they could enable synchronization across the internet, among select clients, or inside a single Appdomain. A static synchronization authority has been provided for synchronizing properties inside a single app domain. Access it using StaticSynchronizationAuthority.Instance
.
using ObjectSync.Attributes;
using ObjectSync.Synchronization;
namespace MyNamespace
{
[SynchronizationTarget]
internal partial class MyType
{
[SynchronizationAuthorityAttribute]
private ISynchronizationAuthority Authority { get; } = MemorySynchronizationAuthority.Instance;
}
}
Now to actually define the data to be synchronized, annotate fields with the Synchronized
attribute. This will instruct ObjectSync to generate a property based on this backing field that synchronizes its state using the types synchronization context.
using ObjectSync.Attributes;
using ObjectSync.Synchronization;
namespace MyNamespace
{
[SynchronizationTarget]
internal partial class MyType
{
[SynchronizationAuthorityAttribute]
private ISynchronizationAuthority Authority { get; } = MemorySynchronizationAuthority.Instance;
[Synchronized]
private String _synchronizedValue;
}
}
To enable synchronization between instances, you may use the synchronization methods provided by SynchronizationContext
, a generated property containing the context that manages the synchronization state of MyType
instances. Synchronized values are managed using four values:
- Type Id, identifying each synchronized type
- Instance Id, identifying each synchronized instance
- Field Name, identifying each synchronized field
- Source Instance Id, instances sharing the same Source Instance Id will be synchronized
Note that all of these will be generated by default, although you may choose to implement them yourself as well.
using ObjectSync.Attributes;
using ObjectSync.Synchronization;
namespace MyNamespace
{
[SynchronizationTarget]
internal partial class MyType
{
public MyType()
{
SynchronizationContext.Synchronize();
}
[SynchronizationAuthorityAttribute]
private ISynchronizationAuthority Authority { get; } = MemorySynchronizationAuthority.Instance;
[Synchronized]
private String _synchronizedValue;
public void Synchronize(MyType to)
{
SynchronizationContext.DesynchronizeInvokeSynchronize(() =>
{
SourceInstanceId = to.SourceInstanceId;
});
}
public void Desynchronize()
{
SynchronizationContext.DesynchronizeInvokeSynchronize(() =>
{
SourceInstanceId = Guid.NewGuid().ToString();
});
}
}
}
The DesynchronizeInvokeSynchronize
method will first desynchronize the instance from its authority, then invoke the delegate provided and finally synchronize the instance again.
A simple demonstration can be seen using these top level statements:
using MyNamespace;
var instance1 = new MyType();
instance1.SynchronizedValue = "Value";
var instance2 = new MyType();
instance2.SynchronizedValue = "No Value";
Console.WriteLine($"Value for Instance1: {instance1.SynchronizedValue}");
Console.WriteLine($"Value for Instance2: {instance2.SynchronizedValue}");
Console.WriteLine("Synchronizing instance2 to instance1...");
instance2.Synchronize(instance1);
Console.WriteLine($"Value for Instance1: {instance1.SynchronizedValue}");
Console.WriteLine($"Value for Instance2: {instance2.SynchronizedValue}");
instance1.SynchronizedValue = "New Value";
Console.WriteLine($"Value for Instance1: {instance1.SynchronizedValue}");
Console.WriteLine($"Value for Instance2: {instance2.SynchronizedValue}");
instance2.SynchronizedValue = "Another Value";
Console.WriteLine($"Value for Instance1: {instance1.SynchronizedValue}");
Console.WriteLine($"Value for Instance2: {instance2.SynchronizedValue}");
This will result in the following output:
Value for Instance1: Value
Value for Instance2: No Value
Synchronizing instance2 to instance1...
Value for Instance1: Value
Value for Instance2: Value
Value for Instance1: New Value
Value for Instance2: New Value
Value for Instance1: Another Value
Value for Instance2: Another Value