ObjectRepository β€” .NET in-memory repository pattern для Π²Π°ΡˆΠΈΡ… Π΄ΠΎΠΌΠ°ΡˆΠ½ΠΈΡ… ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΎΠ²

Π—Π°Ρ‡Π΅ΠΌ Ρ…Ρ€Π°Π½ΠΈΡ‚ΡŒ всС Π΄Π°Π½Π½Ρ‹Π΅ Π² памяти?

Для хранСния Π΄Π°Π½Π½Ρ‹Ρ… сайта ΠΈΠ»ΠΈ бСкэнда ΠΏΠ΅Ρ€Π²Ρ‹ΠΌ ΠΆΠ΅Π»Π°Π½ΠΈΠ΅ΠΌ Π±ΠΎΠ»ΡŒΡˆΠΈΠ½ΡΡ‚Π²Π° здравомыслящих людСй Π²Ρ‹Π±Π΅Ρ€Π΅Ρ‚ SQL Π±Π°Π·Ρƒ Π΄Π°Π½Π½Ρ‹Ρ….Β 

Но ΠΈΠ½ΠΎΠ³Π΄Π° Π² Π³ΠΎΠ»ΠΎΠ²Ρƒ ΠΏΡ€ΠΈΡ…ΠΎΠ΄ΠΈΡ‚ ΠΌΡ‹ΡΠ»ΡŒ Ρ‡Ρ‚ΠΎ модСль Π΄Π°Π½Π½Ρ‹Ρ… Π½Π΅ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ΠΈΡ‚ для SQL: Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ΠΏΡ€ΠΈ построСнии поиска ΠΈΠ»ΠΈ ΡΠΎΡ†ΠΈΠ°Π»ΡŒΠ½ΠΎΠ³ΠΎ Π³Ρ€Π°Ρ„Π° Π½ΡƒΠΆΠ΅Π½ поиск ΠΏΠΎ слоТным связям ΠΌΠ΅ΠΆΠ΄Ρƒ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°ΠΌΠΈ.Β 

Π₯ΡƒΠΆΠ΅ всСго ситуация, ΠΊΠΎΠ³Π΄Π° Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚Π΅ Π² ΠΊΠΎΠΌΠ°Π½Π΄Π΅, ΠΈ ΠΊΠΎΠ»Π»Π΅Π³Π° Π½Π΅ ΡƒΠΌΠ΅Π΅Ρ‚ ΡΡ‚Ρ€ΠΎΠΈΡ‚ΡŒ быстрыС запросы. Бколько Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ Π²Ρ‹ ΠΏΠΎΡ‚Ρ€Π°Ρ‚ΠΈΠ»ΠΈ Π½Π° Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌ N+1 ΠΈ Π½Π° построСниС Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Ρ… индСксов, Ρ‡Ρ‚ΠΎΠ±Ρ‹ SELECT Π½Π° Π³Π»Π°Π²Π½ΠΎΠΉ страницС ΠΎΡ‚Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Π» Π·Π° Ρ€Π°Π·ΡƒΠΌΠ½ΠΎΠ΅ врСмя?

Π”Ρ€ΡƒΠ³ΠΈΠΌ популярным ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ΠΎΠΌ являСтся NoSQL. НСсколько Π»Π΅Ρ‚ Π½Π°Π·Π°Π΄ Π±Ρ‹Π» большой Ρ…Π°ΠΉΠΏ Π²ΠΎΠΊΡ€ΡƒΠ³ этой Ρ‚Π΅ΠΌΡ‹ β€” для любого ΡƒΠ΄ΠΎΠ±Π½ΠΎΠ³ΠΎ случая Ρ€Π°Π·Π²ΠΎΡ€Π°Ρ‡ΠΈΠ²Π°Π»ΠΈ MongoDB ΠΈ Ρ€Π°Π΄ΠΎΠ²Π°Π»ΠΈΡΡŒ ΠΎΡ‚Π²Π΅Ρ‚Π°ΠΌ Π² Π²ΠΈΠ΄Π΅ json-Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚ΠΎΠ² (кстати, сколько костылСй ΠΏΡ€ΠΈΡˆΠ»ΠΎΡΡŒ Π²ΡΡ‚Π°Π²ΠΈΡ‚ΡŒ ΠΈΠ·-Π·Π° цикличСских ссылок Π² Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°Ρ…?).

Π― ΠΏΡ€Π΅Π΄Π»Π°Π³Π°ΡŽ ΠΏΠΎΠΏΡ€ΠΎΠ±ΠΎΠ²Π°Ρ‚ΡŒ Π΅Ρ‰Ρ‘ ΠΎΠ΄ΠΈΠ½, Π°Π»ΡŒΡ‚Π΅Ρ€Π½Π°Ρ‚ΠΈΠ²Π½Ρ‹ΠΉ способ β€” ΠΏΠΎΡ‡Π΅ΠΌΡƒ Π±Ρ‹ Π½Π΅ ΠΏΠΎΠΏΡ€ΠΎΠ±ΠΎΠ²Π°Ρ‚ΡŒ Ρ…Ρ€Π°Π½ΠΈΡ‚ΡŒ всС Π΄Π°Π½Π½Ρ‹Π΅ Π² памяти прилоТСния, пСриодичСски сохраняя Π² ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ»ΡŒΠ½ΠΎΠ΅ Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π΅ (Ρ„Π°ΠΉΠ», удалСнная Π±Π°Π·Π° Π΄Π°Π½Π½Ρ‹Ρ…)?Β 

ΠŸΠ°ΠΌΡΡ‚ΡŒ стала Π΄Π΅ΡˆΡ‘Π²ΠΎΠΉ, Π° Π»ΡŽΠ±Ρ‹Π΅ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹Π΅ Π΄Π°Π½Π½Ρ‹Π΅ Π±ΠΎΠ»ΡŒΡˆΠΈΠ½ΡΡ‚Π²Π° ΠΌΠ°Π»Ρ‹Ρ… ΠΈ срСдних ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΎΠ² Π²Π»Π΅Π·ΡƒΡ‚ Π² 1 Π“Π± памяти. (НапримСр, ΠΌΠΎΠΉ Π»ΡŽΠ±ΠΈΠΌΡ‹ΠΉ домашний ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ β€” финансовый Ρ‚Ρ€Π΅ΠΊΠ΅Ρ€, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π²Π΅Π΄Π΅Ρ‚ Π΅ΠΆΠ΅Π΄Π½Π΅Π²Π½ΡƒΡŽ статистику ΠΈ ΠΈΡΡ‚ΠΎΡ€ΠΈΡŽ ΠΌΠΎΠΈΡ… Ρ‚Ρ€Π°Ρ‚, балансов, ΠΈ Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΉ Π·Π° ΠΏΠΎΠ»Ρ‚ΠΎΡ€Π° Π³ΠΎΠ΄Π° потрСбляСт всСго 45 Мб памяти.)

ΠŸΠ»ΡŽΡΡ‹:

  • Доступ ΠΊ Π΄Π°Π½Π½Ρ‹ΠΌ становится ΠΏΡ€ΠΎΡ‰Π΅ β€” Π½Π΅ Π½ΡƒΠΆΠ½ΠΎ Π·Π°Π±ΠΎΡ‚ΠΈΡ‚ΡŒΡΡ ΠΎ запросах, Π»Π΅Π½ΠΈΠ²ΠΎΠΉ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠ΅, особСнностях ORM, Ρ€Π°Π±ΠΎΡ‚Π° происходит с ΠΎΠ±Ρ‹Ρ‡Π½Ρ‹ΠΌΠΈ C# ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°ΠΌΠΈ;
  • НСт ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌ, связанных с доступом ΠΈΠ· Ρ€Π°Π·Π½Ρ‹Ρ… ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ²;
  • ΠžΡ‡Π΅Π½ΡŒ быстро β€” Π½Π΅Ρ‚Ρƒ сСтСвых запросов, отсутствуСт трансляция ΠΊΠΎΠ΄Π° Π² язык запросов, Π½Π΅ Π½ΡƒΠΆΠ½Π° (Π΄Π΅)сСриализация ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ²;
  • Допустимо Ρ…Ρ€Π°Π½ΠΈΡ‚ΡŒ Π΄Π°Π½Π½Ρ‹Π΅ Π² любом Π²ΠΈΠ΄Π΅ β€” Ρ…ΠΎΡ‚ΡŒ Π² XML Π½Π° дискС, Ρ…ΠΎΡ‚ΡŒ Π² SQL Server, Ρ…ΠΎΡ‚ΡŒ Π² Azure Table Storage.

ΠœΠΈΠ½ΡƒΡΡ‹:

  • ВСряСтся Π³ΠΎΡ€ΠΈΠ·ΠΎΠ½Ρ‚Π°Π»ΡŒΠ½ΠΎΠ΅ ΠΌΠ°ΡΡˆΡ‚Π°Π±ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅, ΠΈ ΠΊΠ°ΠΊ слСдствиС нСльзя ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ zero downtime deployment;
  • Если ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΡƒΠΏΠ°Π΄Π΅Ρ‚ β€” ΠΌΠΎΠΆΠ½ΠΎ частично ΠΏΠΎΡ‚Π΅Ρ€ΡΡ‚ΡŒ Π΄Π°Π½Π½Ρ‹Π΅. (Но вСдь нашС ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅-Ρ‚ΠΎ Π½ΠΈΠΊΠΎΠ³Π΄Π° Π½Π΅ ΠΏΠ°Π΄Π°Π΅Ρ‚, ΠΏΡ€Π°Π²Π΄Π°?)

Как это Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚?

Алгоритм ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΉ:

  • На стартС устанавливаСтся соСдинСниС с Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π΅ΠΌ Π΄Π°Π½Π½Ρ‹Ρ…, ΠΈ происходит Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠ° Π΄Π°Π½Π½Ρ‹Ρ…;
  • Бтроится ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π½Π°Ρ модСль, ΠΏΠ΅Ρ€Π²ΠΈΡ‡Π½Ρ‹Π΅ индСксы, ΠΈ индСксы ΠΎΡ‚Π½ΠΎΡˆΠ΅Π½ΠΈΠΉ (1:1, 1:Many);
  • БоздаСтся подписка Π½Π° измСнСния свойств ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² (INotifyPropertyChanged) ΠΈ Π½Π° Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΈΠ»ΠΈ ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠ΅ элСмСнтов Π² ΠΊΠΎΠ»Π»Π΅ΠΊΡ†ΠΈΡŽ (INotifyCollectionChanged);
  • ΠŸΡ€ΠΈ срабатывании подписки β€” измСнившийся ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ добавляСтся Π² ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ Π½Π° запись Π² Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π΅ Π΄Π°Π½Π½Ρ‹Ρ…;
  • ΠŸΠ΅Ρ€ΠΈΠΎΠ΄ΠΈΡ‡Π΅ΡΠΊΠΈ (ΠΏΠΎ Ρ‚Π°ΠΉΠΌΠ΅Ρ€Ρƒ) Π² Ρ„ΠΎΠ½ΠΎΠ²ΠΎΠΌ ΠΏΠΎΡ‚ΠΎΠΊΠ΅ ΡΠΎΡ…Ρ€Π°Π½ΡΡŽΡ‚ΡΡ измСнСния Π² Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π΅;
  • ΠŸΡ€ΠΈ Π²Ρ‹Ρ…ΠΎΠ΄Π΅ ΠΈΠ· прилоТСния Ρ‚Π°ΠΊΠΆΠ΅ ΡΠΎΡ…Ρ€Π°Π½ΡΡŽΡ‚ΡΡ измСнСния Π² Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π΅.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ ΠΊΠΎΠ΄Π°

ДобавляСм Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΡ‹Π΅ зависимости

// Основная Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ°
Install-Package OutCode.EscapeTeams.ObjectRepository
Β  Β Β 
// Π₯Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π΅ Π΄Π°Π½Π½Ρ‹Ρ…, Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ Π±ΡƒΠ΄ΡƒΡ‚ ΡΠΎΡ…Ρ€Π°Π½ΡΡ‚ΡŒΡΡ измСнСния
// Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ Ρ‚ΠΎ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌ Π±ΡƒΠ΄Π΅Ρ‚Π΅ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ.
Install-Package OutCode.EscapeTeams.ObjectRepository.File
Install-Package OutCode.EscapeTeams.ObjectRepository.LiteDb
Install-Package OutCode.EscapeTeams.ObjectRepository.AzureTableStorage
Β  Β Β 
// ΠžΠΏΡ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½ΠΎ - Ссли Π½ΡƒΠΆΠ½ΠΎ Ρ…Ρ€Π°Π½ΠΈΡ‚ΡŒ модСль Π΄Π°Π½Π½Ρ‹Ρ… для Hangfire
// Install-Package OutCode.EscapeTeams.ObjectRepository.Hangfire

ΠžΠΏΠΈΡΡ‹Π²Π°Π΅ΠΌ модСль Π΄Π°Π½Π½Ρ‹Ρ…, которая Π±ΡƒΠ΄Π΅Ρ‚ ΡΠΎΡ…Ρ€Π°Π½ΡΡ‚ΡŒΡΡ Π² Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π΅

public class ParentEntity : BaseEntity
{
Β  Β  public ParentEntity(Guid id) => Id = id;
}
Β  Β Β 
public class ChildEntity : BaseEntity
{
Β  Β  public ChildEntity(Guid id) => Id = id;
Β  Β  public Guid ParentId { get; set; }
Β  Β  public string Value { get; set; }
}

Π—Π°Ρ‚Π΅ΠΌ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π½ΡƒΡŽ модСль:

public class ParentModel : ModelBase
{
Β  Β  public ParentModel(ParentEntity entity)
Β  Β  {
Β  Β  Β  Β  Entity = entity;
Β  Β  }
Β  Β Β 
Β  Β  public ParentModel()
Β  Β  {
Β  Β  Β  Β  Entity = new ParentEntity(Guid.NewGuid());
Β  Β  }
Β  Β Β 
Β  Β  public Guid? NullableId => null;
Β  Β Β 
Β  Β  // ΠŸΡ€ΠΈΠΌΠ΅Ρ€ связи 1:Many
Β  Β  public IEnumerable<ChildModel> Children => Multiple<ChildModel>(x => x.ParentId);
Β  Β Β 
Β  Β  protected override BaseEntity Entity { get; }
}
Β  Β Β 
public class ChildModel : ModelBase
{
Β  Β  private ChildEntity _childEntity;
Β  Β Β 
Β  Β  public ChildModel(ChildEntity entity)
Β  Β  {
Β  Β  Β  Β  _childEntity = entity;
Β  Β  }
Β  Β Β 
Β  Β  public ChildModel()Β 
Β  Β  {
Β  Β  Β  Β  _childEntity = new ChildEntity(Guid.NewGuid());
Β  Β  }
Β  Β Β 
Β  Β  public Guid ParentId
Β  Β  {
Β  Β  Β  Β  get => _childEntity.ParentId;
Β  Β  Β  Β  set => UpdateProperty(() => _childEntity.ParentId, value);
Β  Β  }
Β  Β Β 
Β  Β  public string Value
Β  Β  {
Β  Β  Β  Β  get => _childEntity.Value;
Β  Β  Β  Β  set => UpdateProperty(() => _childEntity.Value, value
Β  Β  }
Β  Β Β 
Β  Β  // Доступ с поиском ΠΏΠΎ индСксу
Β  Β  public ParentModel Parent => Single<ParentModel>(ParentId);
Β  Β Β 
Β  Β  protected override BaseEntity Entity => _childEntity;
}

И Π½Π°ΠΊΠΎΠ½Π΅Ρ† сам класс-Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ для доступа ΠΊ Π΄Π°Π½Π½Ρ‹ΠΌ:

public class MyObjectRepository : ObjectRepositoryBase
{
Β  Β  public MyObjectRepository(IStorage storage) : base(storage, NullLogger.Instance)
Β  Β  {
Β  Β  Β  Β  IsReadOnly = true; // Для тСстов, позволяСт Π½Π΅ ΡΠΎΡ…Ρ€Π°Π½ΡΡ‚ΡŒ измСнСния Π² Π±Π°Π·Ρƒ
Β  Β Β 
Β  Β  Β  Β  AddType((ParentEntity x) => new ParentModel(x));
Β  Β  Β  Β  AddType((ChildEntity x) => new ChildModel(x));
Β  Β Β 
Β  Β  Β  Β  // Если ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Hangfire ΠΈ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Ρ…Ρ€Π°Π½ΠΈΡ‚ΡŒ модСль Π΄Π°Π½Π½Ρ‹Ρ… для Hangfire Π² ObjectRepository
Β  Β  Β  Β  // this.RegisterHangfireScheme();Β 
Β  Β Β 
Β  Β  Β  Β  Initialize();
Β  Β  }
}

Π‘ΠΎΠ·Π΄Π°Ρ‘ΠΌ экзСмпляр ObjectRepository:

var memory = new MemoryStream();
var db = new LiteDatabase(memory);
var dbStorage = new LiteDbStorage(db);
Β  Β Β 
var repository = new MyObjectRepository(dbStorage);
await repository.WaitForInitialize();

Если Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ HangFire

public void ConfigureServices(IServiceCollection services, ObjectRepository objectRepository)
{
Β  Β  services.AddHangfire(s => s.UseHangfireStorage(objectRepository));
}

Вставка Π½ΠΎΠ²ΠΎΠ³ΠΎ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°:

var newParent = new ParentModel()
repository.Add(newParent);

ΠŸΡ€ΠΈ этом Π²Ρ‹Π·ΠΎΠ²Π΅ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ ParentModel добавляСтся ΠΈ Π² Π»ΠΎΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΉ кэш, ΠΈ Π² ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ Π½Π° запись Π² Π±Π°Π·Ρƒ. ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ эта опСрация Π·Π°Π½ΠΈΠΌΠ°Π΅Ρ‚ O(1), ΠΈ с этим ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠΌ ΠΌΠΎΠΆΠ½ΠΎ сразу Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ.

НапримСр, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π½Π°ΠΉΡ‚ΠΈ этот ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ Π² Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΈ ΠΈ ΡƒΠ±Π΅Π΄ΠΈΡ‚ΡŒΡΡ Ρ‡Ρ‚ΠΎ Π²Π΅Ρ€Π½ΡƒΠ²ΡˆΠΈΠΉΡΡ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ являСтся Ρ‚Π΅ΠΌ ΠΆΠ΅ экзСмпляром:

var parents = repository.Set<ParentModel>();
var myParent = parents.Find(newParent.Id);
Assert.IsTrue(ReferenceEquals(myParent, newParent));

Π§Ρ‚ΠΎ ΠΏΡ€ΠΈ этом происходит? Set<ParentModel>() Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ TableDictionary<ParentModel>, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ содСрТит Π² сСбС ConcurrentDictionary<ParentModel, ParentModel> ΠΈ прСдоставляСт Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π» ΠΏΠ΅Ρ€Π²ΠΈΡ‡Π½Ρ‹Ρ… ΠΈ Π²Ρ‚ΠΎΡ€ΠΈΡ‡Π½Ρ‹Ρ… индСксов. Π­Ρ‚ΠΎ позволяСт ΠΈΠΌΠ΅Ρ‚ΡŒ ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ для поиска ΠΏΠΎ Id (ΠΈΠ»ΠΈ Π΄Ρ€ΡƒΠ³ΠΈΠΌ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ»ΡŒΠ½Ρ‹ΠΌ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΠΌ индСксам) Π±Π΅Π· ΠΏΠΎΠ»Π½ΠΎΠ³ΠΎ ΠΏΠ΅Ρ€Π΅Π±ΠΎΡ€Π° всСх ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ².

ΠŸΡ€ΠΈ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠΈ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² Π² ObjectRepository добавляСтся подписка Π½Π° ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ ΠΈΡ… свойств, поэтому любоС ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ свойств Ρ‚Π°ΠΊΠΆΠ΅ ΠΏΡ€ΠΈΠ²ΠΎΠ΄ΠΈΡ‚ добавлСнию этого ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° Π² ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ Π½Π° запись.Β 
ОбновлСниС свойств снаруТи выглядит Ρ‚Π°ΠΊ ΠΆΠ΅, ΠΊΠ°ΠΊ ΠΈ Ρ€Π°Π±ΠΎΡ‚Π° с POCO-ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠΌ:

myParent.Children.First().Property = "Updated value";

Π£Π΄Π°Π»ΠΈΡ‚ΡŒ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ ΠΌΠΎΠΆΠ½ΠΎ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌΠΈ способами:

repository.Remove(myParent);
repository.RemoveRange(otherParents);
repository.Remove<ParentModel>(x => !x.Children.Any());

ΠŸΡ€ΠΈ этом Ρ‚Π°ΠΊΠΆΠ΅ происходит Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° Π² ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ Π½Π° ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠ΅.

Как Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ сохранСниС?

ObjectRepository ΠΏΡ€ΠΈ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΈ отслСТиваСмых ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² (ΠΊΠ°ΠΊ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΈΠ»ΠΈ ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠ΅, Ρ‚Π°ΠΊ ΠΈ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ свойств) Π²Ρ‹Π·Ρ‹Π²Π°Π΅Ρ‚ событиС ModelChanged, Π½Π° ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ подписан IStorage. Π Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ IStorage ΠΏΡ€ΠΈ Π²ΠΎΠ·Π½ΠΈΠΊΠ½ΠΎΠ²Π΅Π½ΠΈΠΈ события ModelChanged ΡΠΊΠ»Π°Π΄Ρ‹Π²Π°ΡŽΡ‚ измСнСния Π² 3 ΠΎΡ‡Π΅Ρ€Π΅Π΄ΠΈ β€” Π½Π° Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅, Π½Π° ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅, ΠΈ Π½Π° ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠ΅.

Π’Π°ΠΊΠΆΠ΅ Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ IStorage ΠΏΡ€ΠΈ ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ ΡΠΎΠ·Π΄Π°ΡŽΡ‚ Ρ‚Π°ΠΉΠΌΠ΅Ρ€, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΊΠ°ΠΆΠ΄Ρ‹Π΅ 5 сСкунд Π²Ρ‹Π·Ρ‹Π²Π°Π΅Ρ‚ сохранСниС ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ.Β 

ΠšΡ€ΠΎΠΌΠ΅ Ρ‚ΠΎΠ³ΠΎ сущСствуСт API для ΠΏΡ€ΠΈΠ½ΡƒΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠ³ΠΎ Π²Ρ‹Π·ΠΎΠ²Π° сохранСния: ObjectRepository.Save().

ΠŸΠ΅Ρ€Π΅Π΄ ΠΊΠ°ΠΆΠ΄Ρ‹ΠΌ сохранСниСм сначала происходит ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠ΅ ΠΈΠ· ΠΎΡ‡Π΅Ρ€Π΅Π΄Π΅ΠΉ бСссмыслСнных ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΉ (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Π΄ΡƒΠ±Π»ΠΈΠΊΠ°Ρ‚Ρ‹ событий β€” ΠΊΠΎΠ³Π΄Π° ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ мСнялся Π΄Π²Π°ΠΆΠ΄Ρ‹ ΠΈΠ»ΠΈ быстроС Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅/ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠ΅ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ²), ΠΈ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΏΠΎΡ‚ΠΎΠΌ само сохранСниС.Β 

Π’ΠΎ всСх случаях сохраняСтся Π°ΠΊΡ‚ΡƒΠ°Π»ΡŒΠ½Ρ‹ΠΉ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ Ρ†Π΅Π»ΠΈΠΊΠΎΠΌ, поэтому Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Π° ситуация, ΠΊΠΎΠ³Π΄Π° ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ ΡΠΎΡ…Ρ€Π°Π½ΡΡŽΡ‚ΡΡ Π² Π΄Ρ€ΡƒΠ³ΠΎΠΌ порядкС, Ρ‡Π΅ΠΌ мСнялись, Π² Ρ‚ΠΎΠΌ числС ΠΌΠΎΠ³ΡƒΡ‚ ΡΠΎΡ…Ρ€Π°Π½ΡΡ‚ΡŒΡΡ Π±ΠΎΠ»Π΅Π΅ Π½ΠΎΠ²Ρ‹Π΅ вСрсии ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ², Ρ‡Π΅ΠΌ Π½Π° ΠΌΠΎΠΌΠ΅Π½Ρ‚ добавлСния Π² ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ.

Π§Ρ‚ΠΎ Π΅ΡΡ‚ΡŒ Π΅Ρ‰Ρ‘?

  • ВсС Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ основаны Π½Π° .NET Standard 2.0. МоТно ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π² любом соврСмСнном .NET ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅.
  • API потокобСзопасСн. Π’Π½ΡƒΡ‚Ρ€Π΅Π½Π½ΠΈΠ΅ ΠΊΠΎΠ»Π»Π΅ΠΊΡ†ΠΈΠΈ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½Ρ‹ Π½Π° Π±Π°Π·Π΅ ConcurrentDictionary, ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ событий ΠΈΠΌΠ΅ΡŽΡ‚ Π»ΠΈΠ±ΠΎ Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΊΠΈ, Π»ΠΈΠ±ΠΎ Π½Π΅ Π½ΡƒΠΆΠ΄Π°ΡŽΡ‚ΡΡ Π² Π½ΠΈΡ….Β 
    ЕдинствСнноС ΠΎ Ρ‡Π΅ΠΌ стоит ΠΏΠΎΠΌΠ½ΠΈΡ‚ΡŒ β€” ΠΏΡ€ΠΈ Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΠΈ прилоТСния Π²Ρ‹Π·Π²Π°Ρ‚ΡŒ ObjectRepository.Save();
  • ΠŸΡ€ΠΎΠΈΠ·Π²ΠΎΠ»ΡŒΠ½Ρ‹Π΅ индСксы (Ρ‚Ρ€Π΅Π±ΡƒΡŽΡ‚ ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ):

repository.Set<ChildModel>().AddIndex(x => x.Value);
repository.Set<ChildModel>().Find(x => x.Value, "myValue");

ΠšΡ‚ΠΎ это ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚?

Π›ΠΈΡ‡Π½ΠΎ я Π½Π°Ρ‡Π°Π» ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ этот ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ Π²ΠΎ всСх Ρ…ΠΎΠ±Π±ΠΈ-ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°Ρ…, ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ это ΡƒΠ΄ΠΎΠ±Π½ΠΎ, ΠΈ Π½Π΅ Ρ‚Ρ€Π΅Π±ΡƒΠ΅Ρ‚ Π±ΠΎΠ»ΡŒΡˆΠΈΡ… Π·Π°Ρ‚Ρ€Π°Ρ‚ Π½Π° написаниС слоя доступа ΠΊ Π΄Π°Π½Π½Ρ‹ΠΌ ΠΈΠ»ΠΈ разворачивания тяТСлой инфраструктуры. Π›ΠΈΡ‡Π½ΠΎ ΠΌΠ½Π΅, ΠΊΠ°ΠΊ ΠΏΡ€Π°Π²ΠΈΠ»ΠΎ, достаточно хранСния Π΄Π°Π½Π½Ρ‹Ρ… Π² litedb ΠΈΠ»ΠΈ Π² Ρ„Π°ΠΉΠ»Π΅.Β 

Но Π² ΠΏΡ€ΠΎΡˆΠ»ΠΎΠΌ, ΠΊΠΎΠ³Π΄Π° с ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ Π΄Π΅Π»Π°Π»ΠΈ Π½Ρ‹Π½Π΅ ΠΏΠΎΡ‡ΠΈΠ²ΡˆΠΈΠΉ стартап EscapeTeams (Π΄ΡƒΠΌΠ°Π» Π²ΠΎΡ‚ ΠΎΠ½ΠΈ, дСньги β€” Π°Π½ Π½Π΅Ρ‚, ΠΎΠΏΡΡ‚ΡŒ ΠΎΠΏΡ‹Ρ‚) β€” использовали для хранСния Π΄Π°Π½Π½Ρ‹Ρ… Azure Table Storage.

ΠŸΠ»Π°Π½Ρ‹ Π½Π° Π±ΡƒΠ΄ΡƒΡ‰Π΅Π΅

Π₯очСтся ΠΏΠΎΡ‡ΠΈΠ½ΠΈΡ‚ΡŒ ΠΎΠ΄ΠΈΠ½ ΠΈΠ· основных минусов Π΄Π°Π½Π½ΠΎΠ³ΠΎ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Π° β€” Π³ΠΎΡ€ΠΈΠ·ΠΎΠ½Ρ‚Π°Π»ΡŒΠ½ΠΎΠ΅ ΠΌΠ°ΡΡˆΡ‚Π°Π±ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅. Для этого Π½ΡƒΠΆΠ½Ρ‹ Π»ΠΈΠ±ΠΎ распрСдСлСнныС Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΈ (sic!), Π»ΠΈΠ±ΠΎ ΠΏΡ€ΠΈΠ½ΡΡ‚ΡŒ Π²ΠΎΠ»Π΅Π²ΠΎΠ΅ Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅, Ρ‡Ρ‚ΠΎ ΠΎΠ΄Π½ΠΈ ΠΈ Ρ‚Π΅ ΠΆΠ΅ Π΄Π°Π½Π½Ρ‹Π΅ ΠΈΠ· Ρ€Π°Π·Π½Ρ‹Ρ… инстансов ΠΌΠ΅Π½ΡΡ‚ΡŒΡΡ Π½Π΅ Π΄ΠΎΠ»ΠΆΠ½Ρ‹, Π»ΠΈΠ±ΠΎ пускай ΠΌΠ΅Π½ΡΡŽΡ‚ΡΡ ΠΏΠΎ ΠΏΡ€ΠΈΠ½Ρ†ΠΈΠΏΡƒ Β«ΠΊΡ‚ΠΎ послСдний β€” Ρ‚ΠΎΡ‚ ΠΈ ΠΏΡ€Π°Π²Β».

Π‘ тСхничСской Ρ‚ΠΎΡ‡ΠΊΠΈ зрСния я Π²ΠΈΠΆΡƒ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΠΉ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΡƒΡŽ схСму:

  • Π₯Ρ€Π°Π½ΠΈΡ‚ΡŒ вмСсто ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π½ΠΎΠΉ ΠΌΠΎΠ΄Π΅Π»ΠΈ EventLog ΠΈ Snapshot
  • ΠΠ°Ρ…ΠΎΠ΄ΠΈΡ‚ΡŒ Π΄Ρ€ΡƒΠ³ΠΈΠ΅ инстансы (Π΄ΠΎΠ±Π°Π²Π»ΡΡ‚ΡŒ Π² настройки ΠΊΠΎΠ½Π΅Ρ‡Π½Ρ‹Π΅ Ρ‚ΠΎΡ‡ΠΊΠΈ всСх инстансов? udp discovery? master/slave?)
  • Π Π΅ΠΏΠ»ΠΈΡ†ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΌΠ΅ΠΆΠ΄Ρƒ инстансами EventLog Ρ‡Π΅Ρ€Π΅Π· любой ΠΈΠ· Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠΎΠ² консСнсуса, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€ RAFT.

Π’Π°ΠΊ ΠΆΠ΅ сущСствуСт Π΅Ρ‰Ρ‘ ΠΎΠ΄Π½Π° ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ°, которая мСня бСспокоит β€” это каскадноС ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠ΅, Π»ΠΈΠ±ΠΎ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½ΠΈΠ΅ случаСв удалСния ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ², Π½Π° ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π΅ΡΡ‚ΡŒ ссылки ΠΈΠ· Π΄Ρ€ΡƒΠ³ΠΈΡ… ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ².Β 

Π˜ΡΡ…ΠΎΠ΄Π½Ρ‹ΠΉ ΠΊΠΎΠ΄

Если Π²Ρ‹ Π΄ΠΎΡ‡ΠΈΡ‚Π°Π»ΠΈ Π΄ΠΎ сюда β€” Ρ‚ΠΎ дальшС остаСтся Ρ‡ΠΈΡ‚Π°Ρ‚ΡŒ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΊΠΎΠ΄, Π΅Π³ΠΎ ΠΌΠΎΠΆΠ½ΠΎ Π½Π°ΠΉΡ‚ΠΈ Π½Π° GitHub:
https://github.com/DiverOfDark/ObjectRepository

Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com

ΠšΡƒΠΏΠΈΡ‚ΡŒ Π½Π°Π΄Π΅ΠΆΠ½Ρ‹ΠΉ хостинг для сайтов с Π·Π°Ρ‰ΠΈΡ‚ΠΎΠΉ ΠΎΡ‚ DDoS, VPS VDS сСрвСры πŸ”₯ ΠšΡƒΠΏΠΈΡ‚ΡŒ Π½Π°Π΄Π΅ΠΆΠ½Ρ‹ΠΉ хостинг для сайтов с Π·Π°Ρ‰ΠΈΡ‚ΠΎΠΉ ΠΎΡ‚ DDoS, VPS VDS сСрвСры | ProHoster