I know that MongoDB is not supposed to support unit of work, etc. But I think it would be nice to implement the repository which would store only the intentions (similar to criteria) and then commit them to the DB. Otherwise in every method in your repository you have to create connection to DB and then close it. If we place the connection to DB in some BaseRepository class, then we tie our repository to concrete DB and it is really difficult to test repositories, to test IoC which resolve repositories.
Is creating a session in MongoDB a bad idea? Is there a way to separate the connection logic from repository?
Here is some code by Rob Conery. Is it a good idea to always connect to your DB on every request? What is the best practice?
There is one more thing. Imagine I want to provide an index for a collection. Previously I did in a constructor but with Rob's approach this seems out of logic to do it there.
using Norm;
using Norm.Responses;
using Norm.Collections;
using Norm.Linq;
public class MongoSession {
private string _connectionString;
public MongoSession() {
//set this connection as you need. This is left here as an example, but you could, if you wanted,
_connectionString = "mongodb://127.0.0.1/MyDatabase?strict=false";
}
public void Delete<T>(System.Linq.Expressions.Expression<Func<T, bool>> expression) where T : class, new() {
//not efficient, NoRM should do this in a way that sends a single command to MongoDB.
var items = All<T>().Where(expression);
foreach (T item in items) {
Delete(item);
}
}
public void Delete<T>(T item) where T : class, new() {
using(var db = Mongo.Create(_connectionString))
{
db.Database.GetCollection<T>().Delete(item);
}
}
public void DeleteAll<T>() where T : class, new() {
using(var db = Mongo.Create(_connectionString))
{
db.Database.DropCollection(typeof(T).Name);
}
}
public T Single<T>(System.Linq.Expressions.Expression<Func<T, bool>> expression) where T : class, new() {
T retval = default(T);
using(var db = Mongo.Create(_connectionString))
{
retval = db.GetCollection<T>().AsQueryable()
.Where(expression).SingleOrDefault();
}
return retval;
}
public IQueryable<T> All<T>() where T : class, new() {
//don't keep this longer than you need it.
var db = Mongo.Create(_connectionString);
return db.GetCollection<T>().AsQueryable();
}
public void Add<T>(T item) where T : class, new() {
using(var db = Mongo.Create(_connectionString))
{
db.GetCollection<T>().Insert(item);
}
}
public void Add<T>(IEnumerable<T> items) where T : class, new() {
//this is WAY faster than doing single inserts.
using(var db = Mongo.Create(_connectionString))
{
db.GetCollection<T>().Insert(items);
}
}
public void Update<T>(T item) where T : class, new() {
using(var db = Mongo.Create(_connectionString))
{
db.GetCollection<T>().UpdateOne(item, item);
}
}
//this is just some sugar if you need it.
public T MapReduce<T>(string map, string reduce) {
T result = default(T);
using(var db = Mongo.Create(_connectionString))
{
var mr = db.Database.CreateMapReduce();
MapReduceResponse response =
mr.Execute(new MapReduceOptions(typeof(T).Name) {
Map = map,
Reduce = reduce
});
MongoCollection<MapReduceResult<T>> coll = response.GetCollection<MapReduceResult<T>>();
MapReduceResult<T> r = coll.Find().FirstOrDefault();
result = r.Value;
}
return result;
}
public void Dispose() {
_server.Dispose();
}
}
Don't worry too much about opening and closing connections. The MongoDB C# driver maintains an internal connection pool, so you won't suffer overheads of opening and closing actual connections each time you create a new MongoServer object.
You can create a repository interface that exposes your data logic, and build a MongoDB implementation that is injected where it's needed. That way, the MongoDB specific connection code is abstratced away from your application, which only sees the IRepository.
Be careful trying to implement a unit-of-work type pattern with MongoDB. Unlike SQL Server, you can't enlist multiple queries in a transaction that can be rolled back if one fails.
For a simple example of a repository pattern that has MongoDB, SQL Server and JSON implementations, check out the NBlog storage code. It uses Autofac IoC to inject concrete repositories into an ASP.NET MVC app.
While researching design patterns, I was creating a basic repository pattern for .Net Core and MongoDB. While reading over the MongoDB documentation I came across an article about transactions in MongoDB. In the article it specified that:
Starting in version 4.0, MongoDB provides the ability to perform
multi-document transactions against replica sets.
Looking around the intertubes I came across a library that does a really good job of implementing the Unit of Work pattern for MongoDB.
If you are interested in an implementation similar to Rob Connery's and NBlog storage code but using the mongodb csharp driver 2.0 (that is asynchronous), you can look at:
https://github.com/alexandre-spieser/mongodb-generic-repository
You can then write a custom repository inheriting from BaseMongoRepository.
public interface ITestRepository : IBaseMongoRepository
{
void DropTestCollection<TDocument>();
void DropTestCollection<TDocument>(string partitionKey);
}
public class TestRepository : BaseMongoRepository, ITestRepository
{
public TestRepository(string connectionString, string databaseName) : base(connectionString, databaseName)
{
}
public void DropTestCollection<TDocument>()
{
MongoDbContext.DropCollection<TDocument>();
}
public void DropTestCollection<TDocument>(string partitionKey)
{
MongoDbContext.DropCollection<TDocument>(partitionKey);
}
}
Briefly
You can use this nuget-package UnitOfWork.MongoDb. This is a wrapper for MongoDb.Driver with some helpful functions and features. Also you can find sample code and video (ru).
Read settings for connection
// read MongoDb settings from appSettings.json
services.AddUnitOfWork(configuration.GetSection(nameof(DatabaseSettings)));
// --- OR ----
// use hardcoded
services.AddUnitOfWork(config =>
{
config.Credential = new CredentialSettings { Login = "sa", Password = "password" };
config.DatabaseName = "MyDatabase";
config.Hosts = new[] { "Localhost" };
config.MongoDbPort = 27017;
config.VerboseLogging = false;
});
Injections
namespace WebApplicationWithMongo.Pages
{
public class IndexModel : PageModel
{
private readonly IUnitOfWork _unitOfWork;
private readonly ILogger<IndexModel> _logger;
public IndexModel(IUnitOfWork unitOfWork, ILogger<IndexModel> logger)
{
_unitOfWork = unitOfWork;
_logger = logger;
}
public IPagedList<Order>? Data { get; set; }
}
}
After injection you can get repository.
Get repository
public async Task<IActionResult> OnGetAsync(int pageIndex = 0, int pageSize = 10)
{
var repository = _unitOfWork.GetRepository<Order, int>();
Data = await repository.GetPagedAsync(pageIndex, pageSize, FilterDefinition<Order>.Empty, HttpContext.RequestAborted);
return Page();
}
GetPagedAsync one of some helpful implementations
Transactions
If you need ACID operations (transactions) you can use IUnitOfWork something like this. (Replicate Set should be correctly set up). For example:
await unitOfWork.UseTransactionAsync<OrderBase, int>(ProcessDataInTransactionAsync1, HttpContext.RequestAborted, session);
Method ProcessDataInTransactionAsync1 can be looks like this:
async Task ProcessDataInTransactionAsync1(IRepository<OrderBase, int> repositoryInTransaction, IClientSessionHandle session, CancellationToken cancellationToken)
{
await repository.Collection.DeleteManyAsync(session, FilterDefinition<OrderBase>.Empty, null, cancellationToken);
var internalOrder1 = DocumentHelper.GetInternal(99);
await repositoryInTransaction.Collection.InsertOneAsync(session, internalOrder1, null, cancellationToken);
logger!.LogInformation("InsertOne: {item1}", internalOrder1);
var internalOrder2 = DocumentHelper.GetInternal(100);
await repositoryInTransaction.Collection.InsertOneAsync(session, internalOrder2, null, cancellationToken);
logger!.LogInformation("InsertOne: {item2}", internalOrder2);
var filter = Builders<OrderBase>.Filter.Eq(x => x.Id, 99);
var updateDefinition = Builders<OrderBase>.Update.Set(x => x.Description, "Updated description");
var result = await repositoryInTransaction.Collection
.UpdateOneAsync(session, filter, updateDefinition, new UpdateOptions { IsUpsert = false }, cancellationToken);
if (result.IsModifiedCountAvailable)
{
logger!.LogInformation("Update {}", result.ModifiedCount);
}
throw new ApplicationException("EXCEPTION! BANG!");
}
This nuget is open-source Calabonga.UnitOfWork.MongoDb
Related
Problem:
I am new to writing unit testing in c# using xunit. So I am trying to mock the MongoDB connection. In my project I have use repository pattern there I have used unit of work class like this. so I am accessing every repository via it. so the unit of work class code is here.
namespace QuestionBank.API.Repositories
{
public class UnitOfWork : IUnitOfWork
{
public readonly IQuestionsBankDBContext _context;
private readonly ILogger<UnitOfWork> _logger;
private Dictionary<Type, object> repositories;
private IQuestionsRepository _questionsRepository;
private ICampaignQuestionsRepository _campaignQuestionsRepository;
private ICandidateAnswerRepository _candidateAnswerRepository;
private IIntergrationEventLogRepository _integrationEventLogRepository;
private IControlRepository _controlRepository;
public UnitOfWork(IQuestionsBankDBContext context, ILogger<UnitOfWork> logger)
{
_context = context;
_logger = logger;
}
public IQuestionsRepository QuestionsRepository
{
get
{
this._questionsRepository = new QuestionsRepository(_context as IQuestionsBankDBContext, this, _logger);
return this._questionsRepository;
}
}
public ICandidateAnswerRepository CandidateAnswerRepository
{
get
{
this._candidateAnswerRepository = new CandidateAnswerRepository(_context as IQuestionsBankDBContext, this, _logger);
return this._candidateAnswerRepository;
}
}
public ICampaignQuestionsRepository CampaignQuestionsRepository
{
get
{
this._campaignQuestionsRepository = new CampaignQuestionsRepository(_context as IQuestionsBankDBContext, this, _logger);
return this._campaignQuestionsRepository;
}
}
public IIntergrationEventLogRepository IntegrationEventLogRepository
{
get
{
this._integrationEventLogRepository = new IntergrationEventLogRepository(_context as IQuestionsBankDBContext, this, _logger);
return this._integrationEventLogRepository;
}
}
public IControlRepository ControlRepository
{
get
{
this._controlRepository = new ControlRepository(_context as IQuestionsBankDBContext, this, _logger);
return this._controlRepository;
}
}
public IGenericRepository<TDocument> GetRepository<TDocument>() where TDocument : IDocument
{
if (this.repositories == null)
{
this.repositories = new Dictionary<Type, object>();
}
var type = typeof(TDocument);
if (!this.repositories.ContainsKey(type))
{
this.repositories[type] = new GenericRepository<TDocument>(_context);
}
return (IGenericRepository<TDocument>)this.repositories[type];
}
}
}
So in the unit test to mock services and repositories, I need to pass database context to unitofwork. I tried it this way.
var mockDbContext = new Mock<QuestionsBankDBContext>();
var dbContext = mockDbContext.Object;
var mock = new Mock<ILogger<UnitOfWork>>();
_logger = mock.Object;
unitOfWork = new UnitOfWork(dbContext, _logger);
questionsService = new QuestionsService(unitOfWork);
campaignQuestionsService = new CampaignQuestionsService(unitOfWork);
tokenService = new TokenService();
stringLocalizer = new Mock<IStringLocalizer<SharedResource>>();
questionBankIntergrationEventService = new Mock<IQuestionBankIntergrationEventService>();
questionsController = new QuestionsController(questionsService, campaignQuestionsService, stringLocalizer.Object, tokenService, questionBankIntergrationEventService.Object);
contextMock = new Mock<HttpContext>();
And this is my DB context class.
using MongoDB.Driver;
using QuestionBank.API.Models;
namespace QuestionBank.API.Data
{
public class QuestionsBankDBContext : IQuestionsBankDBContext
{
public IMongoClient Client { get; set; }
public IMongoDatabase Database { get; set; }
public QuestionsBankDBContext(IQuestionBankDatabaseSettings settings)
{
Client = new MongoClient(settings.ConnectionString);
Database = Client.GetDatabase(settings.DatabaseName);
}
}
}
Then I wrote a unit test like this.
[Theory]
[InlineData("61879e54e86be1fa5e41831f")]
[InlineData("61879e54e86be1fa5e41831e")]
public async Task GetQuestionById(string questionId)
{
var actionResult = await questionsController.GetQuestionById(questionId);
var result = actionResult as ObjectResult;
Assert.NotNull(result.Value);
if (result.StatusCode == (int)System.Net.HttpStatusCode.OK)
{
Assert.IsType<Questions>(result.Value);
}
else if (result.StatusCode == (int)System.Net.HttpStatusCode.NotFound)
{
Assert.Contains("ErrorCode", result.Value.ToString());
}
else if (result.StatusCode == (int)System.Net.HttpStatusCode.InternalServerError)
{
var code = (int)ErroCodes.InternalServerError;
Assert.Contains(code.ToString(), result.Value.ToString());
}
}
Then when running this it gives
And my question controller GetQuestionById is like this.
[HttpGet]
//[Authorize(Roles = "SuperAdmin,Admin")]
[Route("getquestionbyidfrombank")]
[ProducesResponseType(typeof(Questions), 200)]
[ProducesResponseType(typeof(string), 404)]
[ProducesResponseType(typeof(string), 500)]
public async Task<IActionResult> GetQuestionById([FromQuery] string questionId)
{
try
{
string errorText;
if (!string.IsNullOrEmpty(questionId))
{
var question = await
questionsService.GetQuestionById(questionId);
return Ok(question);
}
else
{
errorText = string.Format(stringLocalizer[Constants.ErrorCodeString],
(int)ErroCodes.SpecifiedItemNotFound,
stringLocalizer[Helper.ToEnumString(ErroCodes.SpecifiedItemNotFound)]);
return StatusCode(404, errorText);
}
}
catch (Exception ex)
{
string exceptionData =
$"Exception occured while getiing question by id. " +
$"\nException Data: Message- {ex.Message}; " +
$"InnerException- {ex.InnerException}; StackTrace- {ex.StackTrace}";
string errorText = string.Format(stringLocalizer[Constants.ErrorCodeString],
(int)ErroCodes.InternalServerError,
stringLocalizer[Helper.ToEnumString(ErroCodes.InternalServerError)]);
return StatusCode(500, errorText);
}
}
this is how I do the service instantiation.
public QuestionsService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
Get question by id function
public async Task<Questions> GetQuestionById(string id)
{
var question = await _unitOfWork.QuestionsRepository.FindByIdAsync(id);
return question;
}
Can someone help me to write this unit test correctly and solve this issue. I tried a lot to find out a way to do this but I could not able to do it. Thank you
Don't - just because something can be mocked, doesn't mean it should.
Instead, you can a docker image to run Mongo, drop and create a DB per test class init, drop collections per test init.
Testing a DAL (data-access-layer) without the DB is a waste of time and won't help you actually find bugs.
When you unit test your other components, mock the entire DAL to return the objects you expect.
And do not skip writing a test for your entire service that includes an empty/pre-populated-via-test-config-data DB.
Also, the fields in your DbContext should be private, not public and while passing a constant instance of a DbContext to MongoDB is OK, because MongoDB context is stateless (no transactions and connections are pooled), in general passing a DbContext via constructor is wrong (because relational DBs have transactions and the connections should not be kept open), instead pass a Func<DbContext> (e.g. public MyClass(Func<DbContext> contextConstructor)) which returns the constructor and have DbContext implement IDisposable. This way the client class can do using (context = contextCreator()) { ... }.
You should create a mock of the interface IQuestionsBankDBContext not the class QuestionsBankDBContext. Apart from that your test is not a unit test, it's more of an integration test because you are creating an instance of a controller and some services, and you mock only the database layer of your application. In unit tests you should test only a single layer. If you write unit tests for a controller then you should mock the services and their behavior (the direct dependencies of the controller).
Looking at your controller method, first unit test should check that when you pass not null and not empty question id then your method returns OkResult. For this test the mock of questionsService could return a question object.
Next test should check the opposite case, when it gets empty string it should return a response with code 404. (In my opinion it should be 400 but we are talking about unit tests)
Last test should check the exception handling. For example you pass a valid questionId in the test, but the mock of questionService throws an exception. In this test you can assert that the response has status code 500.
If you want to test many cases with one test method (a Theory in XUnit) then all these cases should have the same result, or you should provide an expected result with each case.
Introduction. I have found one question and one issue related to the problem :
elastic-db-tools github issue. 2015
stackoverflow question. 2015
Actually I work with multi-tenant architecture. Using latest .net core, ef core and ElasticScale. Most of the time I work with only one tenant - usual case. ShardMapManager provide SqlConnection, and then I can create DbContext using it.
Also I have the following case - I need to request more than one shard at a time. Something like - looking for a Customer through all tenants.
ElasticScale has MultiShardConnection for that. I have to write usual ADO.NET queries to solve this problem. I don't like ADO.NET queries for real. The whole solution works with the ORM - EF, thus I want to use ORM everywhere! I was trying to find something like an adapter for the EF...
Okay. I wrote the following solution:
public sealed class TenantDataContextFactory : ITenantDataContextFactory
{
private readonly Lazy<string> _cachedConnectionString;
private readonly IShardingService _shardingService;
private readonly IConfigurationManager _configurationManager;
private readonly ILogger<TenantDataContextFactory> _logger;
//sp_set_session_context required MSSQL SERVER 2016 or above!!!
private const string SpSetSessionContextQuery = #"exec sp_set_session_context #key=N'TenantId', #value=#TenantId";
public async Task<TenantDataContext> CreateAsync(Guid tenantId)
{
SqlConnection sqlConnection = null;
try
{
sqlConnection = await _shardingService.ShardMap
.OpenConnectionForKeyAsync(key: tenantId,
connectionString: _cachedConnectionString.Value,
options: ConnectionOptions.Validate)
.ConfigureAwait(false);
var cmd = sqlConnection.CreateCommand();
cmd.CommandText = SpSetSessionContextQuery;
cmd.Parameters.AddWithValue("#TenantId", tenantId);
await cmd.ExecuteNonQueryAsync().ConfigureAwait(false);
return new TenantDataContext(tenantId, sqlConnection);
}
catch (Exception ex)
{
sqlConnection?.Dispose();
_logger.LogCritical(ex, $"Create(). Create {nameof(TenantDataContext)} for {tenantId} was ended with an error!");
throw;
}
}
}
public class MultiTenantConnectionFactory : IMultiTenantConnectionFactory
{
private readonly IShardingService _shardingService;
private readonly ITenantDataContextFactory _tenantDataContextFactory;
public async Task<IReadOnlyCollection<TenantDataContext>> GetContexts()
{
var shards = _shardingService.RegisteredTenants;
var connectionsTasks = shards.Select(x => _tenantDataContextFactory.CreateAsync(x));
return await Task.WhenAll(connectionsTasks).ConfigureAwait(false);
}
}
public sealed class MultiTenantRepository<T> : IMultiTenantRepository<T> where T : class, ITenantEntity
{
private readonly IMultiTenantConnectionFactory _multiTenantConnectionFactory;
public async Task<IList<T>> Find(Expression<Func<T, bool>> filter)
{
var dataContexts = await _multiTenantConnectionFactory.GetContexts().ConfigureAwait(false);
var tasksList = dataContexts.Select(async x =>
{
await using (x)
{
return await x.Set<T>().Where(filter).ToListAsync().ConfigureAwait(false);
}
});
var results = await Task.WhenAll(tasksList).ConfigureAwait(false);
return results.SelectMany(x => x).ToList();
}
}
But I don't like this solution! Managing connections, awaiting a lot of tasks... I think this solution is slow... Can anybody explain how should I work with the MultiShardConnection without ADO.NET?!
I'd like to test my solution and ADO.NET query performance. I think I will have to give up if I don't find another solution or use ADO.NET.
P.S. I know about ValueTask! I will change usual Task<T> to the ValueTask soon.
I'm working on .NET Core Web API and I have one endpoint where I want to run three operations in parallel. All three of them use the same database, so I need three copies of DbContext. I created a simple Factory class, which I later inject into my "Data" class.
Is it possible (if it's, is a good practice), to inject DbContext into my factory class (using built in .NET Core IoC) and when someone calls "CreateMyDbContext" method, just deep clone the one which was injected at the beginning?
EDIT:
Here is the example with the DbContext Pool:
public class FooData : IFooData
{
private readonly Func<DisposableScopedContextWrapper> _func;
public FooData(Func<DisposableScopedContextWrapper> func)
{
_func = func;
}
public async Task<List<Apple>> GetApples()
{
using (var wrapper = _func())
{
var apples = await wrapper.Context.Apples.FromSqlRaw("SELECT.... complicated query").ToListAsync();
return apples;
}
}
public async Task<List<Orange>> GetOranges()
{
using (var wrapper = _func())
{
var oranges = await wrapper.Context.Oranges.FromSqlRaw("SELECT.... complicated query").ToListAsync();
return oranges;
}
}
}
public class FooService
{
private readonly IFooData _fooData;
public FooData(IFooData fooData)
{
_fooData = fooData;
}
public async Task<List<Fruit>> GetFruits()
{
var appleTask = _fooData.GetApples();
var orangeTask = _fooData.GetOranges();
(var result1, var result2) = await (appleTask, orangeTask).WhenAll();
// ...
}
}
I definitely would not recommend any deepcloning for multiple reasons, one of them being that you will need to figure out a lot of EF internals to make it right, and internals can change (and you will need to spend some time on it).
Second option would be just creating your context manually, which I would recommend against also cause modern infrastructure uses DbContext pooling.
So what you can to register Func<DbContext> (or create your own factory) like this:
services.AddSingleton<Func<DbContext>>(provider => () =>
{
var scope = provider.CreateScope();
return scope.ServiceProvider.GetRequiredService<DbContext>();
});
the issue here is that scope here would not be disposed and you can't (if you have default scope for your DbContext) dispose the scope inside the Func cause your context will be disposed also. So you can try creating some disposable wrapper so you can manually dispose everything like this:
public class DisposableScopedContextWrapper : IDisposable
{
private readonly IServiceScope _scope;
public DbContext Context { get; }
public DisposableScopedContextWrapper(IServiceScope scope)
{
_scope = scope;
Context = _scope.ServiceProvider.GetService<DbContext>();
}
public void Dispose()
{
_scope.Dispose();
}
}
services.AddSingleton<Func<DisposableScopedContextWrapper>>(provider =>() =>
{
var scope = provider.CreateScope();
return new DisposableScopedContextWrapper(scope);
});
Inject in your classes Func<DisposableScopedContextWrapper> func and use it
using (var wrapper = func())
{
wrapper.Context...
}
I am trying to create a moq for Microsoft.Azure.Documents.Client CreateDocumentQuery
I created an interface with class implementation where I instantiate an object of DocumentClient and make corresponding calls. This is to help moq easy for this.
Here are the code:
public interface IASDocumentClient
{
IOrderedQueryable<T> CreateDocumentQuery<T>(Uri documentCollectionUri, FeedOptions feedOptions = null);
}
public class ASDocumentClient : IASDocumentClient
{
DocumentClient client = null;
public ASDocumentClient(DocumentClient documentClient)
{
this.client = documentClient;
}
public IOrderedQueryable<Document> CreateDocumentQuery(Uri documentCollectionOrDatabaseUri, FeedOptions feedOptions = null)
{
return client.CreateDocumentQuery(documentCollectionOrDatabaseUri, feedOptions);
}
public IQueryable<T> CreateDocumentQuery<T>(Uri documentCollectionOrDatabaseUri, SqlQuerySpec querySpec, FeedOptions feedOptions = null)
{
return client.CreateDocumentQuery<T>(documentCollectionOrDatabaseUri, querySpec, feedOptions);
}
}
public class DocumentDBRepositoryUnitTest : IDisposable
{
IDocumentDBRepository<TestDocumentDBEntity> documentDBRepository;
List<TestDocumentDBEntity> items = new List<TestDocumentDBEntity>();
//Pre-test
public DocumentDBRepositoryUnitTest()
{
Mock<IASDocumentClient> documentClient = new Mock<IASDocumentClient>();
documentClient.Setup(x => x.CreateDocumentQuery<Document>(It.IsAny<Uri>(), It.IsAny<FeedOptions>())).Returns(queryDocuments);
}
//Not working now
[Fact]
public void GetItemsAsyncTest()
{
//Arrange
//Act
var retTask = documentDBRepository.GetItemsAsync(x => true);
//Assert
Assert.NotNull(retTask);
Assert.NotNull(retTask.Result);
}
//Post-test
public void Dispose()
{
items = new List<TestDocumentDBEntity>();
}
}
public class DocumentDBRepository<T> : IDocumentDBRepository<T> where T : BaseDocumentDBEntity, new()
{
private string cosmosDbUri;
private string cosmosDbAuthKey;
private string databaseId;
private string collectionId;
private IASDocumentClient client=null;
public DocumentDBRepository(IASDocumentClient client, string databaseId, string collectionId)
{
this.client = client;
this.databaseId = databaseId;
this.collectionId = collectionId;
if (!string.IsNullOrEmpty(this.databaseId) && !string.IsNullOrEmpty(this.collectionId))
Initialize();
}
public async Task<IEnumerable<T>> GetItemsAsync(Expression<Func<T, bool>> predicate)
{
IDocumentQuery<T> query = client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(this.databaseId, this.collectionId), new FeedOptions { MaxItemCount = -1 })
.Where(predicate)
.AsDocumentQuery();
List<T> results = new List<T>();
while (query.HasMoreResults)
{
results.AddRange(await query.ExecuteNextAsync<T>());
}
return results;
}
}
When I run the test, its not even reaching out to the mock Setup for CreateDocumentQuery:
documentClient.Setup(x => x.CreateDocumentQuery(It.IsAny(), It.IsAny())).Returns(queryDocuments);
Any idea?
The method you are trying to mock is non-virtual. You can only mock abstract methods on a class or mock an interface of a class.
https://github.com/Moq/moq4/wiki/Quickstart - you can see here that IFoo is an interface and Bar and Baz are concrete classes with abstract methods.
I would suggest you create an abstraction on top of this code. The repository pattern looks to be suitable in this case as you seem to be persisting data. This will allow you to mock the repository you create wherever it will be used e.g. in some kind of service layer. As for testing the actual implementation (the bit where you use the Azure SDK) I would suggest writing an integration test that includes this dependency actually saving the data, either to Storage Emulator or a storage account in Azure.
ASDocumentClient does nothing its a 1: 1 wrapper around client so test has negative value ( 0 value + the cost of having the test). You should also not unit test DocumentDB unless you find a specific issue that's Microsofts job ( that can be picked up with integration tests)
IDocumentDBRepository should be tested and can be via your wrapper interface.
Not having ASDocumentClient / IASDocumentClient wrappers to test Repository is a different but useful question.
I'm writing a sync service between our Salesforce environment and our local environment. My use of the Salesforce API is purely on a batch level due to limitations on # of API requests per day, although I do have details on failures at the atomic level. However, I would like to save changes on a atomic level in my local environment as I don't want an entire transaction to fail if one entity fails.
I am using Entity Framework 6 with a Unit of Work and Repository pattern. Here is my relevant code (Implementation Details Below):
IUnitOfWork
public interface IUnitOfWork: IDisposable
{
IReadUpdateRepository<EntityType1> EntityType1Repository { get; }
ISyncRepository<EntityType2> EntityType2Repository { get; }
ISyncRepository<EntityType3> EntityType3Repository { get; }
...other repos
void SaveChanges();
}
Implementation of IUnitOfWork
public class UnitOfWork : IUnitOfWork
{
private bool _isDisposed;
private DbContext _context;
private ISyncRepository<Entity> _entityRepo;
...other private repos
public UnitOfWork(DbContext context)
{
_context = context;
}
public ISyncRepository<Entity> EntityRepository
{
get
{
if (_entityRepo == null)
_entityRepo = new GenericSyncRepository<Entity>(_context);
return _entityRepo ;
}
}
...other getters for other repos
public void SaveChanges()
{
//client classes handle all errors here
_context.SaveChanges();
}
private void dispose(bool isDisposing)
{
if (!_isDisposed)
{
if (isDisposing)
_context.Dispose();
}
_isDisposed = true;
}
public void Dispose()
{
dispose(true);
}
}
ISyncRepository
public interface ISyncRepository<T> where T : BaseEntity
{
void DeleteItems(IEnumerable<T> items);
void DeleteItemsById(IEnumerable<int> ids);
void DeleteItem(T item);
void InsertItems(IEnumerable<T> items);
void Insert(T item);
T GetItemById(int id);
List<T> GetItems(Expression<Func<T, bool>> predicate = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, string includeProperties = "");
}
Implementation of ISyncRepository
public class GenericSyncRepository<T> : ISyncRepository<T> where T : BaseEntity
{
private readonly DbContext _context;
private readonly DbSet<T> _set;
public GenericSyncRepository(DbContext context)
{
_context = context;
_set = _context.Set<T>();
}
public T GetItemById(int id)
{
var result = _set.Find(id);
return result;
}
public List<T> GetItems(Expression<Func<T, bool>> predicate = null, Func<IQueryable<T>,IOrderedQueryable<T>> orderBy = null ,string includeProperties = "")
{
IQueryable<T> query = _set.AsExpandable();
if (predicate != null)
{
query = query.Where(predicate);
}
if (!String.IsNullOrEmpty(includeProperties))
{
var splitProps = includeProperties.Split(',');
foreach (var prop in splitProps)
{
query = query.Include(prop);
}
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
return query.ToList();
}
public void DeleteItemsById(IEnumerable<int> ids)
{
var items = ids.Select(i => _set.Find(i));
DeleteItems(items);
}
public void DeleteItem(T item)
{
_set.Remove(item);
}
public void DeleteItems(IEnumerable<T> items)
{
_context.Set<T>().RemoveRange(items);
}
public void Insert(T item)
{
_set.Add(item);
}
public void InsertItems(IEnumerable<T> items)
{
_set.AddRange(items);
}
public List<T> GetViewItems(Expression<Func<T, bool>> predicate)
{
IQueryable<T> query = _set.AsExpandable();
return query.Where(predicate).ToList();
}
}
I am using this repository in services that relate to each object group involved in the sync, into which I inject the IUnitOfWork via the constructor:
private readonly IUnitOfWork _uow;
public EntityDataService(IUnitOfWork uow, ICamsSfDTOMapper mapper)
{
_uow = uow;
}
Then use the UoW to fetch repositories to performs queries:
var repo = _unitOfWork.PartyRepository;
var p = repo.GetItems(p => Ids.Contains(someValue));
And when inserts or updates are made, I can call SaveChanges:
_unitOfWork.SaveChanges();
This works great for data retrieval, however for data persistence, I run into a snag. On the local domain side, I want to iterate through objects one by one, saving changes or inserting and calling SaveChanges to persist. If an error occurs, I store it in a result object that I log at the end of each sync step and continue onto the next object to do work on.
However, as my app is currently structured, if a database exception occurs on SaveChanges, that validation error remains in the context until it is disposed. Since this context is injected into each repository in the UoW class, it sticks around for what I presume is the life of the entire sync process.
I am using Autofac for DI in a WindowsService hosted using TopShelf and I'm registering my UoW as such:
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().WithParameter("context", new EntityModel());
Here are my questions:
Should this question be posted to Code Review :)
I feel like I should invert my repository with UoW, with UoW being passed into each repository and my repositories being passed into each service (a service can make use of more than one repository)
Does it make sense to spin up a new DbContext for each SaveChanges and to have one DbContext (the one passed into UoW) as my read context and all other contexts new'ed up when SavingChanges?
Any help would be appreciated.
Doesn't bother me, I only use SO and don't intend to go anywhere else, and don't mind having questions like these on SO.
I would highly recommend that your service as for IRepository and your repository ask for IUoW. I even like to think of IRepository as more than just a barrier to EF. I normally only use a repository if either
I know for a fact in advance I will be implementing a non EF repository as well otherwise
I want to implement some actual logic which I want abstracted from my service and would not make it generic. E.g. I would make a UserProfileRepository, which would have methods such as SyncCurrentUserProfile rather than exposing Add/Insert/Update. Exposing Add/Insert/Update adds nothing to the equation if I don't plan to use a non EF based model.
It depends. If you don't use tracking, and have a lot of unrelated changes over time. Than yes, as each time you add/update something it will add it to the context, which causes future additions/modifications become slower. But if you are adding/modifying 10 things here and there, probably wouldn't worry about it not unless you need a fresh context each time.
I am actually writing a blog post about this. Will try to post a link later.