Mds.Libraries.CSharp.Updates 6.0.0
Mds.Libraries.CSharp.Updates - пакет для работы с изменениями и их отслеживанием
Задачи пакета
- Средства для обновления в апи:
PartialUpdater - Применение и отcлеживание изменений:
Updater - Синхронизация:
IUpdatesIndex,CollectionsSynchronizerиGroupsSynchronizer
Синхронизация
Для синхронизации необходимо реализовать адаптер.
Есть несколько типов адаптера:
Синхронизация коллекций
Варианты адаптеров:
ICollectionBindingSynchronizeAdapterICollectionAtomicSynchronizeAdapterICollectionSynchronizeAdapter
Cинхронизации происходит по выбранному ключу.
ICollectionBindingSynchronizeAdapter
При использовании адаптера ICollectionBindingSynchronizeAdapter происходит обновление полей сущности, которые были изменены и указаны в bindings. Остальные поля в итоговый запрос к бд не попадают. Рекомендуется применять, когда используется стандартная работа bindings.
file class ImportAdapter : ICollectionBindingSynchronizeAdapter<TestGood, long>
{
#region ICollectionBindingSynchronizeAdapter
Expression<Func<TestGood, long>> ICollectionBindingSynchronizeAdapter<TestGood, long>.KeySelector => e => e.Id;
UpdateBindings<TestGood> ICollectionBindingSynchronizeAdapter<TestGood, long>.GetBindings(TestGood entity, TestGood update)
{
var bindings = new UpdateBindings<TestGood>()
{
{ e => e.ExternalId },
{ e => e.Name },
{ e => e.Manufacturer },
};
if (entity.ExternalHash != update.ExternalHash)
{
bindings.Add(e => e.Quantity);
}
return bindings;
}
#endregion ICollectionBindingSynchronizeAdapter
}
ICollectionAtomicSynchronizeAdapter
Адаптер ICollectionAtomicSynchronizeAdapter дает возможность вручную сформировать EntityChanges<> для каждой сущности. Поля не указанные в EntityChanges в запрос к бд не попадают. Рекомендуется использовать, когда необходима дополнительная логика обновления, кроме bindings.
file class ImportAdapter : ICollectionAtomicSynchronizeAdapter<TestGood, long>
{
private static readonly UpdateBindings<TestGood> _bindings;
static ImportAdapter()
{
_bindings = new UpdateBindings<TestGood>()
{
{ e => e.ExternalId },
{ e => e.Name },
{ e => e.Manufacturer },
};
}
#region ICollectionAtomicSynchronizeAdapter
Expression<Func<TestGood, long>> ICollectionAtomicSynchronizeAdapter<TestGood, long>.KeySelector => e => e.Id;
EntityChanges<TestGood> ICollectionAtomicSynchronizeAdapter<TestGood, long>.UpdateEntity(TestGood entity, TestGood update)
{
return UpdateAdapter.Apply(entity, update, _bindings);
}
#endregion ICollectionAtomicSynchronizeAdapter
}
ICollectionSynchronizeAdapter
При использовании адаптера ICollectionSynchronizeAdapter происходит обновление всех полей сущности, которые указаны в bindings, вне зависимости от того, какие изменялись. Это повышает нагрузку на базу данных. Рекомендуется применять в исключительных случаях.
file class ImportAdapter : ICollectionSynchronizeAdapter<TestGood, long>
{
private static readonly UpdateBindings<TestGood> _bindings;
static ImportAdapter()
{
_bindings = new UpdateBindings<TestGood>()
{
{ e => e.ExternalId },
{ e => e.Name },
{ e => e.Manufacturer },
};
}
#region ICollectionSynchronizeAdapter
Expression<Func<TestGood, long>> ICollectionSynchronizeAdapter<TestGood, long>.KeySelector => e => e.Id;
bool ICollectionSynchronizeAdapter<TestGood, long>.UpdateEntity(TestGood entity, TestGood update)
{
return UpdateAdapter.Apply(entity, update, _bindings);
}
#endregion ICollectionSynchronizeAdapter
}
Групповая синхронизация
Варианты адаптеров:
IGroupsBindingSynchronizeAdapterIGroupsAtomicSynchronizeAdapterIGroupsSynchronizeAdapter
Cинхронизации происходит по выбранному ключу с группировкой по групповому ключу.
IGroupsBindingSynchronizeAdapter
Адаптер IGroupsBindingSynchronizeAdapter аналогичен ICollectionBindingSynchronizeAdapter, но с добавление группового ключа.
file class ImportAdapter : IGroupsBindingSynchronizeAdapter<TestGood, string, long>
{
#region IGroupsBindingSynchronizeAdapter
Expression<Func<TestGood, string>> IGroupsBindingSynchronizeAdapter<TestGood, string, long>.GroupKeySelector => e => e.ExternalId;
Expression<Func<TestGood, long>> ICollectionBindingSynchronizeAdapter<TestGood, long>.KeySelector => e => e.Id;
UpdateBindings<TestGood> ICollectionBindingSynchronizeAdapter<TestGood, long>.GetBindings(TestGood entity, TestGood update)
{
var bindings = new UpdateBindings<TestGood>()
{
{ e => e.Name },
{ e => e.Manufacturer },
};
if (entity.ExternalHash != update.ExternalHash)
{
bindings.Add(e => e.Quantity);
}
return bindings;
}
#endregion IGroupsBindingSynchronizeAdapter
}
IGroupsAtomicSynchronizeAdapter
Адаптер IGroupsAtomicSynchronizeAdapter аналогичен ICollectionAtomicSynchronizeAdapter, но с добавление группового ключа.
file class ImportAdapter : IGroupsAtomicSynchronizeAdapter<TestGood, string, long>
{
private static readonly UpdateBindings<TestGood> _bindings;
static ImportAdapter()
{
_bindings = new UpdateBindings<TestGood>()
{
{ e => e.Name },
{ e => e.Manufacturer },
};
}
#region IGroupsAtomicSynchronizeAdapter
Expression<Func<TestGood, string>> IGroupsAtomicSynchronizeAdapter<TestGood, string, long>.GroupKeySelector => e => e.ExternalId;
Expression<Func<TestGood, long>> ICollectionAtomicSynchronizeAdapter<TestGood, long>.KeySelector => e => e.Id;
EntityChanges<TestGood> ICollectionAtomicSynchronizeAdapter<TestGood, long>.UpdateEntity(TestGood entity, TestGood update)
{
return UpdateAdapter.Apply(entity, update, _bindings);
}
#endregion IGroupsAtomicSynchronizeAdapter
}
IGroupsSynchronizeAdapter
Адаптер IGroupsSynchronizeAdapter аналогичен ICollectionSynchronizeAdapter, но с добавление группового ключа.
file class ImportAdapter : IGroupsSynchronizeAdapter<TestGood, string, long>
{
private static readonly UpdateBindings<TestGood> _bindings;
static ImportAdapter()
{
_bindings = new UpdateBindings<TestGood>()
{
{ e => e.Name },
{ e => e.Manufacturer },
};
}
#region IGroupsSynchronizeAdapter
Expression<Func<TestGood, string>> IGroupsSynchronizeAdapter<TestGood, string, long>.GroupKeySelector => e => e.ExternalId;
Expression<Func<TestGood, long>> ICollectionSynchronizeAdapter<TestGood, long>.KeySelector => e => e.Id;
bool ICollectionSynchronizeAdapter<TestGood, long>.UpdateEntity(TestGood entity, TestGood update)
{
return UpdateAdapter.Apply(entity, update, _bindings);
}
#endregion IGroupsSynchronizeAdapter
}
Обьявление адаптера и его применение
Пример синхронизации коллекции
Создаем синхронизатор.
var synchronizer = CollectionsSynchronizerBuilder.Custom<TestGood, long>(new ImportAdapter());
// При желании можно настроить синхронизатор
adapter.Settings = new CollectionsSynchronizerSettings
{
ImportMode = ImportMode.Partial
};
Примеры использования синхронизатора для формирования индекса обновлений.
Запрашивание данных из базы
// Данные на импорт
var import = new List<TestGood> { ... };
// Когда синхронизируем все элементы в базе
var index = await synchronizer.SyncAsync(import, context);
Синхронизация данных, которые уже есть.
// Данные на импорт
var import = new List<TestGood> { ... };
// Существующие данные
var stored = new List<TestGood> { ... };
var index = synchronizer.Sync(import, stored);
index - это индекс обновленией. Он содержит данные для сохранения в базу. Какие элементы надо добавить, какие обновить, какие удалить и поля которые были обновлены, если используется соотвествующий адаптер.
Фиксируем изменения в БД.
await index.SaveAsync(context);
Пример группой синхронизации
Аналогично синхронизации коллекций.
Создаем синхронизатор.
var synchronizer = GroupsSynchronizerBuilder.Custom(new ImportAdapter(), s =>
{
s.ImportMode = ImportMode.Full;
s.AllowAdd = false;
s.AllowUpdate = true;
s.AllowRemove = true;
});
Поведение синхронизатора аналогично. Есть некоторые методы специфичные, только для упрощения формирования групп.
Использование ISynchronizationProvider
В реальности чаще всего вы будете использовать ISynchronizationProvider. Он скрывает логику работы с базой и автоматически сохраняет данные индекса с использованием транзакции и стратегии. Аналогично, на его уровне можно настроить мягкое удаление сущностей.
Для регистрации в DI:
services.AddSynchronizationProvider<WriteSqlContext>();
Дальше в конструкторе сервиса можно запросить ISynchronizationProvider:
internal sealed class Service
{
private readonly ISynchronizationProvider _provider;
public Service(ISynchronizationProvider provider)
{
_provider = provider;
}
}
Использование:
async Task Service.ImportAsync(IEnumerable<GoodDto> goods)
{
await _provider.ImportAsync(async () =>
{
var import = await ConvertAsync(goods, _context);
// Тут можно вернуть индекс с изменениями, сформировать его можно любым образом
// Тут мы его формируем через синхронизатор
return await _synchronizer.SyncAsync(import, _context);
});
}
Showing the top 20 packages that depend on Mds.Libraries.CSharp.Updates.
| Packages | Downloads |
|---|---|
|
Mds.Libraries.CSharp.ChangesBus
Пакет для интеграции шины для обновления сущностей.
|
1 |
|
Mds.Libraries.CSharp.ChangesBus
Пакет для интеграции шины для обновления сущностей.
|
2 |
|
Mds.Libraries.CSharp.Db.Mongo
Пакет для удобной работы с mongoDb.
|
1 |
|
Mds.Libraries.CSharp.Db.Mongo
Пакет для удобной работы с mongoDb.
|
2 |
|
Mds.Libraries.CSharp.Server
Package with tools for server from MedvedStudio.
|
4 |
.NET 7.0
- EFCore.BulkExtensions (>= 7.0.4)
- Mds.Libraries.CSharp.Extensions (>= 2.1.0)
- Microsoft.EntityFrameworkCore (>= 7.0.5)
- Microsoft.EntityFrameworkCore.SqlServer (>= 7.0.5)
- Npgsql.EntityFrameworkCore.PostgreSQL (>= 7.0.4)
| Version | Downloads | Last updated |
|---|---|---|
| 7.0.1 | 3 | 02/28/2026 |
| 6.2.1 | 1 | 02/28/2026 |
| 6.2.0 | 1 | 02/28/2026 |
| 6.2.0-beta.120674 | 1 | 02/28/2026 |
| 6.2.0-beta.107775 | 1 | 02/28/2026 |
| 6.1.1 | 1 | 02/28/2026 |
| 6.1.0 | 1 | 02/28/2026 |
| 6.1.0-beta.106612 | 1 | 02/28/2026 |
| 6.1.0-beta.106605 | 1 | 02/28/2026 |
| 6.1.0-beta.106595 | 1 | 02/28/2026 |
| 6.1.0-beta.106589 | 1 | 02/28/2026 |
| 6.0.1 | 1 | 02/28/2026 |
| 6.0.0 | 1 | 02/28/2026 |
| 6.0.0-beta.99971 | 1 | 02/28/2026 |
| 6.0.0-beta.57258 | 1 | 02/28/2026 |
| 5.4.0 | 1 | 02/28/2026 |
| 5.4.0-beta.103919 | 1 | 02/28/2026 |
| 5.3.0 | 1 | 02/28/2026 |
| 5.3.0-beta.104867 | 1 | 02/28/2026 |
| 5.3.0-beta.71649 | 1 | 02/28/2026 |
| 5.3.0-beta.61482 | 1 | 02/28/2026 |
| 5.3.0-beta.58276 | 1 | 02/28/2026 |
| 5.2.1 | 1 | 02/28/2026 |
| 5.2.0 | 1 | 02/28/2026 |
| 5.2.0-beta.29757 | 1 | 02/28/2026 |
| 5.2.0-beta.29727 | 1 | 02/28/2026 |
| 5.2.0-beta.29724 | 1 | 02/28/2026 |
| 5.2.0-beta.29722 | 1 | 02/28/2026 |
| 5.2.0-beta.29719 | 1 | 02/28/2026 |
| 5.1.0 | 1 | 02/28/2026 |
| 5.1.0-beta.104532 | 1 | 02/28/2026 |
| 5.1.0-beta.18822 | 1 | 02/28/2026 |
| 5.0.3 | 1 | 02/28/2026 |
| 5.0.3-beta.11921 | 1 | 02/28/2026 |
| 5.0.2 | 1 | 02/28/2026 |
| 5.0.2-beta.11265 | 1 | 02/28/2026 |
| 5.0.1 | 1 | 02/28/2026 |
| 5.0.1-beta.105682 | 1 | 02/28/2026 |
| 5.0.0 | 1 | 02/28/2026 |
| 4.5.4-beta.11277 | 1 | 02/28/2026 |
| 4.5.3 | 1 | 02/28/2026 |
| 4.5.2 | 1 | 02/28/2026 |
| 4.5.1 | 1 | 02/28/2026 |
| 4.5.0 | 1 | 02/28/2026 |
| 4.0.3 | 1 | 02/28/2026 |
| 4.0.1 | 1 | 02/28/2026 |
| 4.0.0 | 1 | 02/28/2026 |
| 3.1.3 | 1 | 02/28/2026 |
| 3.1.1.9498 | 1 | 02/28/2026 |