Mds.Libraries.CSharp.Updates 6.1.0-beta.106612

Mds.Libraries.CSharp.Updates - пакет для работы с изменениями и их отслеживанием

Задачи пакета

  • Средства для обновления в апи: PartialUpdater
  • Применение и отcлеживание изменений: Updater
  • Синхронизация: IUpdatesIndex, CollectionsSynchronizer и GroupsSynchronizer

Синхронизация

Для синхронизации необходимо реализовать адаптер.

Есть несколько типов адаптера:

Синхронизация коллекций

Варианты адаптеров:

  • ICollectionBindingSynchronizeAdapter
  • ICollectionAtomicSynchronizeAdapter
  • ICollectionSynchronizeAdapter

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
}

Групповая синхронизация

Варианты адаптеров:

  • IGroupsBindingSynchronizeAdapter
  • IGroupsAtomicSynchronizeAdapter
  • IGroupsSynchronizeAdapter

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
Пакет для интеграции шины для обновления сущностей.
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

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