using System.Linq.Expressions; using Microsoft.Extensions.Logging; using MongoDB.Bson.Serialization; using MongoDB.Driver; using DomainResults.Common; using Core.Abstractions.DomainObjects; namespace DataProviders.Abstractions { public abstract class DataProviderBase where T : DomainObjectDocumentBase { private protected const string _databaseName = "reactredux"; private protected readonly ILogger> _logger; private protected readonly IMongoClient _client; private protected readonly IIdGenerator _idGenerator; private protected readonly ISessionService _sessionService; private protected List? _collection; /// /// Main constructor /// /// /// /// /// public DataProviderBase( ILogger> logger, IMongoClient client, IIdGenerator idGenerator, ISessionService sessionService ) { _logger = logger; _client = client; _idGenerator = idGenerator; _sessionService = sessionService; } /// /// Testing constructor /// /// /// public DataProviderBase( ILogger> logger, ISessionService sessionService, List collection ) { _logger = logger; _sessionService = sessionService; _collection = collection ?? new List(); } #region Insert private protected (Guid?, IDomainResult) Insert(T obj, string collectionName) => InsertAsync(obj, collectionName).Result; private protected (Guid?, IDomainResult) Insert(T obj, string collectionName, Guid sessionId) => InsertAsync(obj, collectionName, sessionId).Result; private protected Task<(Guid?, IDomainResult)> InsertAsync(T obj, string collectionName) => InsertAsyncCore(obj, collectionName, null); private protected Task<(Guid?, IDomainResult)> InsertAsync(T obj, string collectionName, Guid sessionId) => InsertAsyncCore(obj, collectionName, sessionId); #endregion #region InsertMany private protected (List?, IDomainResult) InsertMany(List objList, string collectionName) => InsertManyAsync(objList, collectionName).Result; private protected (List?, IDomainResult) InsertMany(List objList, string collectionName, Guid sessionId) => InsertManyAsync(objList, collectionName, sessionId).Result; private protected Task<(List?, IDomainResult)> InsertManyAsync(List objList, string collectionName) => InsertManyAsyncCore(objList, collectionName, null); private protected Task<(List?, IDomainResult)> InsertManyAsync(List objList, string collectionName, Guid sessionId) => InsertManyAsyncCore(objList, collectionName, sessionId); #endregion #region Get private protected (List?, IDomainResult) GetWithPredicate(Expression> predicate, string collectionName) => GetWithPredicateCore(predicate, collectionName); #endregion #region Update private protected (Guid?, IDomainResult) UpdateWithPredicate(T obj, Expression> predicate, string collectionName) => UpdateWithPredicateAsync(obj, predicate, collectionName).Result; private protected (Guid?, IDomainResult) UpdateWithPredicate(T obj, Expression> predicate, string collectionName, Guid sessionId) => UpdateWithPredicateAsync(obj, predicate, collectionName, sessionId).Result; private protected Task<(Guid?, IDomainResult)> UpdateWithPredicateAsync(T obj, Expression> predicate, string collectionName) => UpdateWithPredicateAsyncCore(obj, predicate, collectionName, null); private protected Task<(Guid?, IDomainResult)> UpdateWithPredicateAsync(T obj, Expression> predicate, string collectionName, Guid sessionId) => UpdateWithPredicateAsyncCore(obj, predicate, collectionName, sessionId); #endregion #region Exists private protected (Guid?, IDomainResult) Exists(Guid id, string collectionName) { var (_resultList, result) = GetWithPredicate(x => x.Id == id, collectionName); return (result.Status != DomainOperationStatus.Failed && _resultList != null && _resultList.Count > 0 ? id :null, result); } #endregion #region Delete private protected IDomainResult DeleteWithPredicate(Expression> predicate, string collectionName) => DeleteWithPredicateAsync(predicate, collectionName).Result; private protected IDomainResult DeleteWithPredicate(Expression> predicate, string collectionName, Guid sessionId) => DeleteWithPredicateAsync(predicate, collectionName, sessionId).Result; private protected Task DeleteWithPredicateAsync(Expression> predicate, string collectionName) => DeleteWithPredicateAsyncCore(predicate, collectionName, null); private protected Task DeleteWithPredicateAsync(Expression> predicate, string collectionName, Guid sessionId) => DeleteWithPredicateAsyncCore(predicate, collectionName, sessionId); #endregion #region DeleteMany private protected IDomainResult DeleteManyWithPredicate(Expression> predicate, string collectionName) => DeleteManyWithPredicateAsync(predicate, collectionName).Result; private protected IDomainResult DeleteManyWithPredicate(Expression> predicate, string collectionName, Guid sessionId) => DeleteManyWithPredicateAsync(predicate, collectionName, sessionId).Result; private protected Task DeleteManyWithPredicateAsync(Expression> predicate, string collectionName) => DeleteManyWithPredicateAsyncCore(predicate, collectionName, null); private protected Task DeleteManyWithPredicateAsync(Expression> predicate, string collectionName, Guid sessionId) => DeleteManyWithPredicateAsyncCore(predicate, collectionName, sessionId); #endregion #region Core methods private async protected Task<(Guid?, IDomainResult)> InsertAsyncCore(T obj, string collectionName, Guid? sessionId) { try { if (_collection != null) { obj.Id = Guid.NewGuid(); _collection.Add(obj); return IDomainResult.Success(obj.Id); } var collection = _client.GetDatabase(_databaseName).GetCollection(collectionName); if (sessionId != null) await collection.InsertOneAsync(_sessionService.GetSession(sessionId.Value), obj); else collection.InsertOne(obj); return IDomainResult.Success(obj.Id); } catch (Exception ex) { _logger.LogError(ex, "Data provider error"); return IDomainResult.Failed(); } } private async Task<(List?, IDomainResult)> InsertManyAsyncCore(List objList, string collectionName, Guid? sessionId) { try { if (_collection != null) { _collection = _collection.Concat(objList).ToList(); return IDomainResult.Success(objList.Select(x => x.Id).ToList()); } var collection = _client.GetDatabase(_databaseName).GetCollection(collectionName); if (sessionId != null) await collection.InsertManyAsync(_sessionService.GetSession(sessionId.Value), objList); else collection.InsertMany(objList); return IDomainResult.Success(objList.Select(x => x.Id).ToList()); } catch (Exception ex) { _logger.LogError(ex, "Data provider error"); return IDomainResult.Failed?>(); } } private (List?, IDomainResult) GetWithPredicateCore(Expression> predicate, string collectionName) { try { List? result; if (_collection != null) { result = _collection?.AsQueryable() .Where(predicate).ToList(); } else { result = _client.GetDatabase(_databaseName).GetCollection(collectionName) .Find(predicate).ToList(); } return result != null ? IDomainResult.Success(result) : IDomainResult.NotFound?>(); } catch (Exception ex) { _logger.LogError(ex, "Data provider error"); return IDomainResult.Failed?>(); } } private async Task<(Guid?, IDomainResult)> UpdateWithPredicateAsyncCore(T obj, Expression> predicate, string collectionName, Guid? sessionId) { try { if (_collection != null) { // remove element(s) from list foreach (var element in _collection.AsQueryable().Where(predicate)) _collection = _collection.Where(x => x.Id != element.Id).ToList(); // add updated element _collection.Add(obj); } else { var collection = _client.GetDatabase(_databaseName).GetCollection(collectionName); if (sessionId != null) await collection.ReplaceOneAsync(_sessionService.GetSession(sessionId.Value), predicate, obj); else await collection.ReplaceOneAsync(predicate, obj); } return IDomainResult.Success(obj.Id); } catch (Exception ex) { _logger.LogError(ex, "Data provider error"); return IDomainResult.Failed(); } } private async Task DeleteWithPredicateAsyncCore(Expression> predicate, string collectionName, Guid? sessionId) { try { var collection = _client.GetDatabase(_databaseName).GetCollection(collectionName); if (sessionId != null) await collection.DeleteOneAsync(_sessionService.GetSession(sessionId.Value), predicate); else await collection.DeleteOneAsync(predicate); return IDomainResult.Success(); } catch (Exception ex) { _logger.LogError(ex, "Data provider error"); return IDomainResult.Failed(); } } private async Task DeleteManyWithPredicateAsyncCore(Expression> predicate, string collectionName, Guid? sessionId) { try { var collection = _client.GetDatabase(_databaseName).GetCollection(collectionName); if (sessionId != null) await collection.DeleteManyAsync(_sessionService.GetSession(sessionId.Value), predicate); else await collection.DeleteManyAsync(predicate); return IDomainResult.Success(); } catch (Exception ex) { _logger.LogError(ex, "Data provider error"); return IDomainResult.Failed(); } } #endregion } }