nocscienceat.CudManager2
3.1.0
dotnet add package nocscienceat.CudManager2 --version 3.1.0
NuGet\Install-Package nocscienceat.CudManager2 -Version 3.1.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="nocscienceat.CudManager2" Version="3.1.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="nocscienceat.CudManager2" Version="3.1.0" />
<PackageReference Include="nocscienceat.CudManager2" />
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add nocscienceat.CudManager2 --version 3.1.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: nocscienceat.CudManager2, 3.1.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package nocscienceat.CudManager2@3.1.0
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=nocscienceat.CudManager2&version=3.1.0
#tool nuget:?package=nocscienceat.CudManager2&version=3.1.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
nocscienceat.CudManager2
Lightweight C# utility for comparing two IEnumerable sequences and determining which items should be created, updated, deleted, or are already in sync. Targets .NET Standard 2.0 and uses C# 10.
Features
- Single-key and multi-key comparison flows via
CudManagerandCudManagerMultiKey. - Adapter-based design: plug in your own key extraction and comparison logic with:
ICudDataAdapter<TKey, TSourceItem, TSync2Item>ICudDataAdapterMultiKey<TKey, TSourceItem, TSync2Item>
- Clear outputs:
Items2CreateItems2UpdateItemsInSyncItems2Delete
- Difference reporting via
ComparisonResult<TSync2Item>.DiffersBy.Properties. - Update projection via
ComparisonResult<TSync2Item>.DiffersBy.SyncItemUpdatedandAbstractCudManager.ItemLinkUpdate.Sync2ItemUpdated. - Duplicate key handling with configurable behavior via
ThrowOnDuplicateKeys.
Key concepts
ICudDataAdapter<TKey, TSourceItem, TSync2Item>: provides key extraction for each side plus a comparison result.ICudDataAdapterMultiKey<TKey, TSourceItem, TSync2Item>: variant that can work with multiple keys for a source item (TSync2Item, TSync2Item are linked if the key of TSync2Item matches any key of TSourceItem).ComparisonResult: returnIsEqualwhen items match, orDiffersBywith a list of differing property names.CudManager/CudManagerMultiKey: orchestrate the comparison and expose the four result sets.
Installation
dotnet add package nocscienceat.CudManager2
Usage (single key)
// implementig the ICudDataAdapter for your types
public sealed class PersonAdapter : ICudDataAdapter<int, PersonDto, PersonEntity>
{
public int GetKeyFromSourceItem(PersonDto dto) => dto.Id;
public int GetKeyFromSync2Item(PersonEntity entity) => entity.Id;
public ComparisonResult Compare<PersonEntity>(PersonDto dto, PersonEntity entity)
{
var diffs = new List<string>();
PersonEntity sync2ItemUpdated = new(); // PersonEntity sync2ItemUpdated = new(entity); // if a copy constructor is available
if (dto.Name != entity.Name)
{
diffs.Add(nameof(PersonEntity.Name));
sync2ItemUpdated.Name = dto.Name;
}
if (dto.Email != entity.Email)
{
diffs.Add(nameof(PersonEntity.Email));
sync2ItemUpdated.Email = dto.Email;
}
return diffs.Count == 0
? new ComparisonResult.IsEqual()
: new ComparisonResult.DiffersBy { Properties = diffs, SyncItemUpdated = sync2ItemUpdated };
}
}
// code from the 'synchronization' context
var inboundDtos = _personDtoService.GetAll(); // IEnumerable<PersonDto>
var existingEntities = _personEntityService.GetAll(); // IEnumerable<PersonEntity>
var manager = new CudManager<int, PersonDto, PersonEntity>(
new PersonAdapter(),
sourceItems: inboundDtos,
sync2Items: existingEntities);
// Trigger comparison lazily via the exposed properties
foreach (var toCreate in manager.Items2Create) { /* insert */ }
foreach (var update in manager.Items2Update) { /* update update.Sync2Item with update.SourceItem */ }
foreach (var toDelete in manager.Items2Delete) { /* delete */ }
Applying updates
foreach (var update in manager.Items2Update)
{
// Since the assumed PersonEntityService usually only works with PersonEntity instances (it knows nothing about PersonDto),
// we call its hypothetical update method with the original PersonEntity item and the PersonEntity item with the updated Properties as well as the list of differing properties.
_personEntityService.Update(update.Sync2Item, update.Sync2ItemUpdated, update.DifferingProperties);
...
}
Duplicate Key Handling
The CudManager<TKey, TSourceItem, TSync2Item> class provides a ThrowOnDuplicateKeys property to control how duplicate keys are handled during comparison:
Default Behavior (ThrowOnDuplicateKeys = false)
var manager = new CudManager<int, PersonDto, PersonEntity>(
new PersonAdapter(),
sourceItems: inboundDtos,
sync2Items: existingEntities);
// Duplicate keys are silently ignored (subsequent items with the same key are skipped)
- Source items: Duplicate keys are skipped; only the first item with each key is processed
- Sync2 items: Duplicate keys are skipped; only the first item with each key is processed
- No exception is thrown; processing continues normally
Strict Mode (ThrowOnDuplicateKeys = true)
var manager = new CudManager<int, PersonDto, PersonEntity>(
new PersonAdapter(),
sourceItems: inboundDtos,
sync2Items: existingEntities);
manager.ThrowOnDuplicateKeys = true;
// An ArgumentException is thrown if duplicates are detected
foreach (var toCreate in manager.Items2Create) { /* ... */ }
- Source items:
ArgumentExceptionthrown if a duplicate key is found - Sync2 items:
ArgumentExceptionthrown if a duplicate key is found - Useful for validating data integrity before synchronization
Example
// Data with duplicate key (ID = 3)
var sourceItems = new List<PersonDto>
{
new PersonDto { Id = 1, Name = "Alice" },
new PersonDto { Id = 3, Name = "Bob" },
new PersonDto { Id = 3, Name = "Bobby" } // Duplicate!
};
var manager = new CudManager<int, PersonDto, PersonEntity>(
new PersonAdapter(),
sourceItems: sourceItems,
sync2Items: existingEntities);
// Silent handling (default)
manager.ThrowOnDuplicateKeys = false;
var toCreate = manager.Items2Create.ToList(); // Only first item with ID=3 is processed
// Strict mode
manager.ThrowOnDuplicateKeys = true;
var toCreate = manager.Items2Create.ToList(); // Throws ArgumentException: "Duplicate key found in source items"
Notes
- Results are computed lazily when accessing the public properties; repeated access will not recompute.
- Duplicate source keys are skipped by default to avoid ambiguous updates; enable
ThrowOnDuplicateKeysto detect data quality issues. CudManagerMultiKeyhandles multiple keys per source item and always skips duplicates without exception support.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
-
.NETStandard 2.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.