viernes, 4 de octubre de 2019

CosmosDB Singleton


Hace unos días me toco trabajar en proyecto grande con cosmosDB. Inicialmente cada petición que tenia se establecía una conexión y al terminar se terminaba la conexión. Con el incremento de peticiones al servicio Rest iniciamos a tener errores con el Socket. La conexión la tenía implementada con TCP.

Por lo que cambie la conexión del API a utilizar un singleton para poder eliminar el problema.

Vamos a ver como podemos hacer el singleton para un proyecto de Rest API con C# y cosmosDB.
Primero vamos hacer un proyecto nuevo de MVC para crear nuestro Rest API.


Con el proyecto listo lo primero que vamos hacer es una carpeta nueva que se llama services. Aquí vamos a guardar las clases y la interfaz que nos permitirá hacer las consultas a nuestra base de datos.


En la interfaz ICosmosDBServices lo que debemos agregar son los métodos que van a realizar las operaciones en cosmosDB. En nuestro ejemplo tenemos métodos para buscar información agregar documentos y borrar, pero podríamos hacer todos los que necesitamos.


namespace CosmosDBSingleton.Serivces
{
    public interface ICosmosDBService
    {
        dynamic FindDocument(string documentid);
        Task<dynamic> CreateNewDocumentAsync();
        Task<bool> ReplaceDocument();
    }
}

La clase CosmosDBServices la vamos a usar para desarrollar los métodos que se definieron en la interfaz. Para nuestro ejemplo solo usaremos en método de buscar. Pero se pueden crear todos.


public class CosmosDBService : ICosmosDBService
    {

        private readonly DocumentClient _client;
        private readonly FeedOptions _queryOptions;

        public CosmosDBService(string endpointUrl, string primaryKey)
        {
            var connectionPolicy = new ConnectionPolicy()
            {
                ConnectionMode = ConnectionMode.Direct,
                ConnectionProtocol = Protocol.Tcp,
                EnableEndpointDiscovery = true
            };
            _client = new DocumentClient(new Uri(endpointUrl), primaryKey, connectionPolicy);
            _queryOptions = new FeedOptions { MaxItemCount = 1000, EnableCrossPartitionQuery = true };
        }

En esta clase también vamos a definir un objeto de conexión a la base de datos. Que tiene de nombre _client. El constructor de la clase requiere que se le pase el endpoint de conexión y la llave de seguridad de la base de datos para poder crear esta conexión. Dentro del constructor configuramos alguna información de la conexión y establecemos la conexión a la base de datos.

En este momento ya podemos usar la conexión de la base de datos para consultas borrar o actualizar documentos.

Como vemos el método FindDocument requiere de un parámetro para hacer la consulta y ya con esto podemos iniciar la consulta de documentos a la base de datos.


 public Task<dynamic> CreateNewDocumentAsync()
        {
            throw new NotImplementedException();
        }

        public dynamic FindDocument(string documentid)
        {
            var query = new SqlQuerySpec()
            {
                QueryText = "SELECT top 1 * FROM c where c.id=@id",
                Parameters = new SqlParameterCollection() { new SqlParameter("@id", documentid) }
            };
            var crossQueryInSql = _client.CreateDocumentQuery<dynamic>(UriFactory.CreateDocumentCollectionUri("cosmosdb database", "cosmosdb collection"), query, _queryOptions).ToList();
            return crossQueryInSql.FirstOrDefault();
        }

        public Task<bool> ReplaceDocument()
        {
            throw new NotImplementedException();
        }

        public async Task<bool> OpenConnection()
        {
            await _client.OpenAsync();
            return true;
        }

Ahora nos debemos ir a la clase Startup donde crearemos la conexión a cosmos. Usando la configuración de los appsettings pasamos la información requerida a la clase de cosmosDBServices.


 public void ConfigureServices(IServiceCollection services)
        {
            services.Configure(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            services.AddSingleton(InitializeCosmosClientInstanceAsync(_config.GetSection("CosmosDBSettings")));

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

services.AddSingleton(InitializeCosmosClientInstanceAsync(_config.GetSection("CosmosDBSettings")));

Dentro de esta clase creamos un método llamado InitializeCosmosClientInstanceAsync el cual recibe la información del appsettings con la información de cosmosdb. Ahora lo que debemos hacer es crear una instancia de CosmosDBService con los settings y retornar el objeto listo con la información de la conexión. De esta manera vamos a tener una conexión única para hacer las operaciones a cosmosdb.


  private static ICosmosDBService InitializeCosmosClientInstanceAsync(IConfigurationSection configurationSection)
        {
            string endpoint = configurationSection.GetSection("CosmosDBAPi").Value;
            string key = configurationSection.GetSection("CosmosDBKey").Value;

            var cosmosDbService = new CosmosDBService(endpoint, key);
            cosmosDbService.OpenConnection().GetAwaiter().GetResult();
            return cosmosDbService;
        }


appsettings


Después de esto los errores con las conexiones y los sockets se terminaron.


Etiquetas: , , , , , ,

0 comentarios:

Publicar un comentario

Suscribirse a Enviar comentarios [Atom]

<< Inicio