/** * This method allows for a concurrency-safe pattern to check for the existence of a given entity and, if it * does not exist to create it, or if it does exist, to update it. * * This is concurrency-safe as it ensures that any two concurrent importer processes that are trying to create * or update the same entity type with the same ID will not be able to do so at the same time. Rather, the * first thread to reach this code will acquire a lock and prevent the second thread from being able to check * for the existence of the entity until the first thread has finished creating it. At this point, the second * thread is freed up to check if the entity already exists, which by this point, it will. */ private Task <TEntity> CreateOrUpdateWithExclusiveLock <TEntity, TMessageObject>( string entityName, StatisticsDbContext statisticsDbContext, TMessageObject objectFromMessage, Guid entityId, TEntity entityToCreate) where TEntity : class where TMessageObject : class { var typeName = typeof(TEntity).Name; return(DbUtils.ExecuteWithExclusiveLock( statisticsDbContext, $"CreateOrUpdate{typeName}-{entityId}", async(context) => { _logger.LogInformation( $"Checking for existence of {typeName} \"{entityName}\""); var existing = await statisticsDbContext.Set <TEntity>().FindAsync(entityId); if (existing == null) { _logger.LogInformation($"Creating {typeName} \"{entityName}\""); TEntity created = (await statisticsDbContext.Set <TEntity>().AddAsync(entityToCreate)).Entity; await statisticsDbContext.SaveChangesAsync(); return created; } _logger.LogInformation($"{typeName} \"{entityName}\" already exists - updating"); TEntity mappedEntity = _mapper.Map(objectFromMessage, existing); TEntity updated = statisticsDbContext.Set <TEntity>().Update(mappedEntity).Entity; await statisticsDbContext.SaveChangesAsync(); return updated; })); }
protected DbSet <TEntity> DbSet() { return(_context.Set <TEntity>()); }