Asp.net Core DI: использование SemaphoreSlim для операций записи и чтения с помощью Singleton - PullRequest
0 голосов
/ 06 марта 2019

Я перерабатываю приложение ASP.NET CORE 2.2, чтобы избежать использования шаблона локатора служб в сочетании со статическими классами.Double bad!

Переоборудование включает создание и внедрение объекта Singleton в качестве хранилища для некоторых глобальных данных.Идея здесь состоит в том, чтобы избежать попадания в мой SQL-сервер некоторых базовых / глобальных данных, которые снова и снова используются в запросах.Однако эти данные необходимо обновлять ежечасно (не только при запуске приложения).Таким образом, для управления ситуацией я использую SemaphoreSlim для одновременного доступа к объектам данных.

Вот парный эскиз того, что я делаю:

namespace MyApp.Global
{

    public interface IMyGlobalDataService
    {
        Task<List<ImportantDataItem>> GetFilteredDataOfMyList(string prop1);
        Task LoadMyImportantDataListAsync();
    }

    public class MyGlobalDataService: IMyGlobalDataService
    {
        private MyDbContext _myDbContext;

        private readonly SemaphoreSlim myImportantDataLock = new SemaphoreSlim(1, 1);
        private List<ImportantDataItem> myImportantDataList { get; set; }
        public async Task<List<ImportantDataItem>> GetFilteredDataOfMyList(string prop1)
        {
            List<ImportantDataItem> list;
            myImportantDataLock.WaitAsync();
            try
            {
                list = myImportantDataList.Where(itm => itm.Prop1 == prop1).ToList();
            }
            finally
            {
                myImportantDataLock.Release();
            }
            return list;
        }

        public async Task LoadMyImportantDataListAsync()
        {
            // this method gets called when the Service is created and once every hour thereafter

            myImportantDataLock.WaitAsync();
            try
            {
                this.MyImportantDataList = await _myDbContext.ImportantDataItems.ToListAsync();
            }
            finally
            {
                myImportantDataLock.Release();
            }
            return;
        }


        public MyGlobalDataService(MyDbContext myDbContext) {
            _myDbContext = myDbContext;
        };
    }
}

Таким образом, в действительности я использую SemaphoreSlim для ограничения доступа по одному за раз, как для READ, так и для UPDATING до myImportantDataList.Это действительно неопределенная территория для меня.Похоже ли это на подходящий подход для управления внедрением глобальных данных Singleton в мое приложение?Или я должен ожидать безумную блокировку / блокировку потоков?

Ответы [ 2 ]

1 голос
/ 06 марта 2019

Проблема с использованием SemaphoreSlim заключается в масштабируемости.

Поскольку это относится к веб-приложению, справедливо предположить, что вы хотите, чтобы несколько читателей могли одновременно получать доступ к данным.Однако вы (по понятным причинам) ограничивает число запросов на семафор, которые могут быть сгенерированы одновременно, до 1 (чтобы предотвратить одновременные запросы на чтение и запись).Это означает, что вы также будете сериализовать все операции чтения.

Вам необходимо использовать что-то вроде ReaderWriterLockSlim , чтобы разрешить чтение нескольких потоков, но обеспечить эксклюзивный доступ для записи.

0 голосов
/ 12 марта 2019

Ответ Крейка ударил меня по голове: с помощью ReaderWriterLockSlim. Поэтому я отметил это как принятый ответ. Но я публикую свое пересмотренное решение на случай, если оно кому-нибудь пригодится. Важно отметить, что я использую следующий пакет для обеспечения асинхронной функциональности ReaderWriterLockSlim: https://www.nuget.org/packages/Nito.AsyncEx/

using Nito.AsyncEx;
using System;
using System.Collections.Generic;
using System.Text;

namespace MyApp.Global
{

    public interface IMyGlobalDataService
    {
        Task<List<ImportantDataItem>> GetFilteredDataOfMyList(string prop1);
        Task LoadMyImportantDataListAsync();
    }

    public class MyGlobalDataService : IMyGlobalDataService
    {
        private MyDbContext _myDbContext;

        private readonly AsyncReaderWriterLock myImportantDataLock = new AsyncReaderWriterLock();
        private List<ImportantDataItem> myImportantDataList { get; set; }
        public async Task<List<ImportantDataItem>> GetFilteredDataOfMyList(string prop1)
        {
            List<ImportantDataItem> list;
            using (await myImportantDataLock.ReaderLockAsync())
            {
                list = myImportantDataList.Where(itm => itm.Prop1 == prop1).ToList();
            }
            return list;
        }

        public async Task LoadMyImportantDataListAsync()
        {
            // this method gets called when the Service is created and once every hour thereafter

            using (await myImportantDataLock.WriterLockAsync())
            {
                this.MyImportantDataList = await _myDbContext.ImportantDataItems.ToListAsync();
            }
            
            return;
        }


        public MyGlobalDataService(MyDbContext myDbContext)
        {
            _myDbContext = myDbContext;
        };
    }
}
...