es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

¿Cómo bloquear solicitudes iguales a un servicio externo en C#?

Digamos que tengo un servicio delimitado llamado IPackageLoader con un método simple GetAsync(string packageName) que nos permite recuperar un paquete mediante una llamada a HTTP GET.

Dado que es una llamada a HTTP GET, es idempotente, por lo que no tiene sentido hacer múltiples solicitudes si se llama a GetAsync(string packageName) varias veces.

Ejemplo:

packageLoader.GetAsync("dotnet/aspnetcore")

packageLoader.GetAsync("dotnet/aspnetcore")

packageLoader.GetAsync("dotnet/efcore")

Si no hay un mecanismo de bloqueo aquí, la segunda solicitud se realizará mientras podría esperar simplemente por la primera, guardarla y devolverla. Sin embargo, la tercera solicitud, ya que es para obtener otro paquete, podría hacerse sin esperar.

Intenté usar SemaphoreSlim pero fallé, ya que bloqueé la primera solicitud, y la segunda esperaría a que la primera llegue como se quiere. Sin embargo, si intentara obtener un paquete diferente, utilizando SemaphoreSlim necesitaría esperar innecesariamente.

¿Alguna idea de cómo lograr este comportamiento? ¿Quizás alguna clase de la biblioteca que me permita bloquear según alguna clave, en lugar de bloquear mientras se realiza cualquier solicitud?

Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    Un enfoque para resolver este problema es utilizar un mecanismo de caché para almacenar los paquetes previamente obtenidos. Cada vez que se llame a GetAsync(string packageName), se puede verificar primero si el paquete ya está presente en la caché. Si es así, se devuelve la versión en caché. Si no está presente, se realiza la llamada HTTP GET y se almacena el resultado en la caché antes de devolverlo.

    Para implementar esto, se puede utilizar un diccionario concurrente como caché, con el nombre del paquete como clave y los datos del paquete como valor. Esto permitirá que múltiples hilos accedan y modifiquen el diccionario sin causar problemas de concurrencia.

    Aquí hay un código de ejemplo para demostrar este enfoque:

    public class PackageLoader : IPackageLoader
    {
        private readonly ConcurrentDictionary<string, packagedata=""> _cache = new ConcurrentDictionary<string, packagedata="">();
    
        public async Task<packagedata> GetAsync(string packageName)
        {
            if (_cache.TryGetValue(packageName, out PackageData packageData))
            {
                // El paquete ya está presente en la caché, se devuelve la versión en caché
                return packageData;
            }
    
            // El paquete no está en caché, se realiza la llamada HTTP GET y se almacena el resultado en la caché
            var result = await MakeHttpGetCall(packageName);
            packageData = ConvertResultToPackageData(result);
            _cache.TryAdd(packageName, packageData);
    
            return packageData;
        }
    
        private async Task<string> MakeHttpGetCall(string packageName)
        {
            // Realizar la llamada HTTP GET aquí
        }
    
        private PackageData ConvertResultToPackageData(string result)
        {
            // Convertir el resultado a PackageData aquí
        }
    }
    

    Con este enfoque, las llamadas múltiples a GetAsync para el mismo paquete devolverán la versión en caché, evitando llamadas HTTP GET innecesarias. Al mismo tiempo, se pueden realizar llamadas para distintos paquetes de forma concurrente, ya que no estarán bloqueadas por el mecanismo de bloqueo.</string,></string,>

Comments are closed.