How to mock a MongoDB collection of type IMongoCollection<T> to return some predefined data? - mongodb

Below is my code:
Controller/Action:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(CustomerViewModel model, string returnUrl = null)
{
try
{
ViewData["ReturnUrl"] = returnUrl;
// when debugging the test, _dbContext.Customers throws exception
CustomerDoc existingCustomer = await _dbContext.Customers.Find(o => o.email == model.email).FirstOrDefaultAsync();
if (existingCustomer != null)
{
ModelState.AddModelError("Email", "email already used.");
}
// other checkings
if (!ModelState.IsValid)
{
return View(model);
}
// if model state is valid, do something here
}
catch (Exception ex)
{
return View(model);
}
return View(model);
}
And my unit test code is:
[Fact]
public async Task should_return_view_with_errors_when_email_already_exists()
{
IEnumerable<CustomerDoc> customers = new List<CustomerDoc>
{
new CustomerDoc
{
email = "test#test.com"
}
};
_dbContextMock.SetupAllProperties();
// below line is causing the error
_dbContextMock.Setup(c => c.Customers).Returns(() =>(IMongoCollection<CustomerDoc>)customers);
CustomerViewModel model = new CustomerViewModel
{
email = "test#test.com"
};
CreateController();
var result = await _controller.Register(model);
Assert.IsType<ViewResult>(result);
Assert.False(_controller.ModelState.IsValid);
Assert.True(_controller.ModelState.ContainsKey("Email"));
}
As you can see in my unit test code comment, I am trying to mock a IMongoCollection to return some data. But I am not able to do so because _dbContext.Customers is throwing exception.
How can I mock IMongoCollection to return some predefined data?
I am using
asp.net core 2.1.0
mongodb driver 2.7.0

You declare customers as a List:
IEnumerable<CustomerDoc> customers = new List<CustomerDoc>
but then try to cast it to IMongoCollection
() =>(IMongoCollection<CustomerDoc>)customers
There's two immediate directions (but both have further issues to deal with):
1) Just return the list without the cast
() => customers
but I can't see the type of c.Customers so I suspect this will just move the issue. I'll take a guess that it's IMongoCollection<CustomerDoc> which is why you're trying to do the cast in the first place? This is problematic as the .Returns would need to be be associated with a function performing the equivalent of c.Customer.Find(). Even so, it's probably better than the alternative.
2) Changing the customers variable to a type that implements IMongoCollection.
Option 1 feels like the way to go as option 2 forces you to start dealing with lots of logic that really shouldn't be relevant to this piece of code.

Related

AspNet Boilerplate Parallel DB Access through Entity Framework from an AppService

We are using ASP.NET Zero and are running into issues with parallel processing from an AppService. We know requests must be transactional, but unfortunately we need to break out to slow running APIs for numerous calls, so we have to do parallel processing.
As expected, we are running into a DbContext contingency issue on the second database call we make:
System.InvalidOperationException: A second operation started on this context
before a previous operation completed. This is usually caused by different
threads using the same instance of DbContext, however instance members are
not guaranteed to be thread safe. This could also be caused by a nested query
being evaluated on the client, if this is the case rewrite the query avoiding
nested invocations.
We read that a new UOW is required, so we tried using both the method attribute and the explicit UowManager, but neither of the two worked.
We also tried creating instances of the referenced AppServices using the IocResolver, but we are still not able to get a unique DbContext per thread (please see below).
public List<InvoiceDto> CreateInvoices(List<InvoiceTemplateLineItemDto> templateLineItems)
{
List<InvoiceDto> invoices = new InvoiceDto[templateLineItems.Count].ToList();
ConcurrentQueue<Exception> exceptions = new ConcurrentQueue<Exception>();
Parallel.ForEach(templateLineItems, async (templateLineItem) =>
{
try
{
XAppService xAppService = _iocResolver.Resolve<XAppService>();
InvoiceDto invoice = await xAppService
.CreateInvoiceInvoiceItem();
invoices.Insert(templateLineItems.IndexOf(templateLineItem), invoice);
}
catch (Exception e)
{
exceptions.Enqueue(e);
}
});
if (exceptions.Count > 0) throw new AggregateException(exceptions);
return invoices;
}
How can we ensure that a new DbContext is availble per thread?
I was able to replicate and resolve the problem with a generic version of ABP. I'm still experiencing the problem in my original solution, which is far more complex. I'll have to do some more digging to determine why it is failing there.
For others that come across this problem, which is exactly the same issue as reference here, you can simply disable the UnitOfWork through an attribute as illustrated in the code below.
public class InvoiceAppService : ApplicationService
{
private readonly InvoiceItemAppService _invoiceItemAppService;
public InvoiceAppService(InvoiceItemAppService invoiceItemAppService)
{
_invoiceItemAppService = invoiceItemAppService;
}
// Just add this attribute
[UnitOfWork(IsDisabled = true)]
public InvoiceDto GetInvoice(List<int> invoiceItemIds)
{
_invoiceItemAppService.Initialize();
ConcurrentQueue<InvoiceItemDto> invoiceItems =
new ConcurrentQueue<InvoiceItemDto>();
ConcurrentQueue<Exception> exceptions = new ConcurrentQueue<Exception>();
Parallel.ForEach(invoiceItemIds, (invoiceItemId) =>
{
try
{
InvoiceItemDto invoiceItemDto =
_invoiceItemAppService.CreateAsync(invoiceItemId).Result;
invoiceItems.Enqueue(invoiceItemDto);
}
catch (Exception e)
{
exceptions.Enqueue(e);
}
});
if (exceptions.Count > 0) {
AggregateException ex = new AggregateException(exceptions);
Logger.Error("Unable to get invoice", ex);
throw ex;
}
return new InvoiceDto {
Date = DateTime.Now,
InvoiceItems = invoiceItems.ToArray()
};
}
}
public class InvoiceItemAppService : ApplicationService
{
private readonly IRepository<InvoiceItem> _invoiceItemRepository;
private readonly IRepository<Token> _tokenRepository;
private readonly IRepository<Credential> _credentialRepository;
private Token _token;
private Credential _credential;
public InvoiceItemAppService(IRepository<InvoiceItem> invoiceItemRepository,
IRepository<Token> tokenRepository,
IRepository<Credential> credentialRepository)
{
_invoiceItemRepository = invoiceItemRepository;
_tokenRepository = tokenRepository;
_credentialRepository = credentialRepository;
}
public void Initialize()
{
_token = _tokenRepository.FirstOrDefault(x => x.Id == 1);
_credential = _credentialRepository.FirstOrDefault(x => x.Id == 1);
}
// Create an invoice item using info from an external API and some db records
public async Task<InvoiceItemDto> CreateAsync(int id)
{
// Get db record
InvoiceItem invoiceItem = await _invoiceItemRepository.GetAsync(id);
// Get price
decimal price = await GetPriceAsync(invoiceItem.Description);
return new InvoiceItemDto {
Id = id,
Description = invoiceItem.Description,
Amount = price
};
}
private async Task<decimal> GetPriceAsync(string description)
{
// Simulate a slow API call to get price using description
// We use the token and credentials here in the real deal
await Task.Delay(5000);
return 100.00M;
}
}

Test a method that does something as well as throws an exception

I'm getting started with TDD (and MoQ). I have a method that takes in an Order and creates a unique CustomerId for that Order.
public class CustomerService
{
public CustomerService(IOrderRepository orderRepo)
{
_orderRepo = orderRepo;
}
public string Create(Order order)
{
//1. Save the Order that comes in, irrespective of being valid or not.
_orderRepo.Save(order);
//2. If the order is invalid, throw an exception.
if (!isValid(order))
throw new ArgumentException();
//3. Create a Customer, generate a unique CustomerId and return that.
return createCustomerAndGenerateCustomerId(order);
}
}
The following test doesn't seem to be working correctly:
[Test]
[ExpectedException(typeof(ArgumentException))]
public void Create_WhenPassedInvalidOrder_StillPersistsIt()
{
//Arrange
var order = new Order(); //This is invalid, as it has some mandatory fields missing
var mockOrderRepo = new Mock<IOrderRepository>();
var customerService = new CustomerService(mockOrderRepo.Object);
//Act
customerService.Create(order); //This should call Save() on the order repo, but still throw an exception.
//Assert
mockOrderRepo.Verify(o => o.Save(order), Times.Once());
}
This test is always passing, even if I don't call _orderRepo.Save(). What am I doing wrong?
You cannot use ExpectedException in this scenario because Nunit will try/catch the exception on the test level so your mockOrderRepo.Verify never gets called.
So you manually need to try catch your customerService.Create call - and if you want manually assert on the thrown exception - or you the Assert.Throws if you're using Nunit 2.5 or higher:
[Test]
public void Create_WhenPassedInvalidOrder_StillPersistsIt()
{
//Arrange
var order = new Order();
var mockOrderRepo = new Mock<IOrderRepository>();
var customerService = new CustomerService(mockOrderRepo.Object);
//Act
Assert.Throws<ArgumentException>(() => customerService.Create(order));
//Assert
mockOrderRepo.Verify(o => o.Save(order), Times.Once());
}

Is this a good pattern for PATCH

I am implementing a REST style API that allows an object to be PATCH'ed. The intention of the PATCH operation is to allow one or more properties in a class to be updated without touching an of the other properties that may be set.
The are examples of partial updates on the ServiceStack OrmLite page, but it seems to need hardcoding to indicate which fields will be partially updated. In my scenario it is upto the depend application to decide which fields to send.
I also have to cope with the scenario that the object may not have been persisted yet.
To get around this I have implemented the following:
public object Patch(Myclass request)
{
HttpStatusCode SuccessCode;
try
{
var result = (MyClass)Get(request);
if (result != null)
{
request.PopulateWithNonDefaultValues(result);
dbFactory.Run(dbCmd => dbCmd.UpdateNonDefaults(request, r => r.myId == request.myId));
}
else
{
dbFactory.Run(dbCmd => dbCmd.Save(request));
}
SuccessCode = HttpStatusCode.Accepted;
}
catch (Exception e)
{
log.Error(e);
SuccessCode = HttpStatusCode.InternalServerError;
}
return new HttpResult()
{
StatusCode = SuccessCode
};
}
It works, but something doesn't feel right, I'm sure there must be a better way?
That looks ok although you're code will be a lot shorter if you just throw let it throw C# Exceptions when there's an error and if you're inheriting from ServiceStack's New API base Service class you can use the already available Db property, e.g:
public object Patch(Myclass request)
{
var result = (MyClass)Get(request);
if (result != null)
{
request.PopulateWithNonDefaultValues(result);
Db.UpdateNonDefaults(request, r => r.myId == request.myId);
}
else
{
Db.Save(request);
}
return new HttpResult
{
StatusCode = HttpStatusCode.Accepted
};
}

Entity Framework + ODATA: side-stepping the pagination

The project I'm working on has the Entity Framework on top of an OData layer. The Odata layer has it's server side pagination turned to a value of 75. My reading on the subject leads me to believe that this pagination value is used across the board, rather than a per table basis. The table that I'm currently looking to extract all the data from is, of course, more than 75 rows. Using the entity framework, my code is simply thus:
public IQueryable<ProductColor> GetProductColors()
{
return db.ProductColors;
}
where db is the entity context. This is returning the first 75 records. I read something where I could append a parameter inlinecount set to allpages giving me the following code:
public IQueryable<ProductColor> GetProductColors()
{
return db.ProductColors.AddQueryOption("inlinecount","allpages");
}
However, this too returns 75 rows!
Can anyone shed light on how to truly get all the records regardless of the OData server-side pagination stuff?
important: I cannot remove the pagination or turn it off! It's extremely valuable in other scenarios where performance is a concern.
Update:
Through some more searching I've found an MSDN that describes how to do this task.
I'd love to be able to turn it into a full Generic method but, this was as close as I could get to a generic without using reflection:
public IQueryable<T> TakeAll<T>(QueryOperationResponse<T> qor)
{
var collection = new List<T>();
DataServiceQueryContinuation<T> next = null;
QueryOperationResponse<T> response = qor;
do
{
if (next != null)
{
response = db.Execute<T>(next) as QueryOperationResponse<T>;
}
foreach (var elem in response)
{
collection.Add(elem);
}
} while ((next = response.GetContinuation()) != null);
return collection.AsQueryable();
}
calling it like:
public IQueryable<ProductColor> GetProductColors()
{
QueryOperationResponse<ProductColor> response = db.ProductColors.Execute() as QueryOperationResponse<ProductColor>;
var productColors = this.TakeAll<ProductColor>(response);
return productColors.AsQueryable();
}
If unable turn off paging you'll receive 75 row by call, always. You can get all rows in following ways:
Add another IQueryable<ProductColor> AllProductColors and modify
public static void InitializeService(DataServiceConfiguration config)
{
config.UseVerboseErrors = true;
config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
config.SetEntitySetPageSize("ProductColors", 75); - Note only paged queries are present
config.SetServiceOperationAccessRule("*", ServiceOperationRights.AllRead);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
}
You should call ProductColors as many as needed, for example
var cat = new NetflixCatalog(new Uri("http://odata.netflix.com/v1/Catalog/"));
var x = from t in cat.Titles
where t.ReleaseYear == 2009
select t;
var response = (QueryOperationResponse<Title>)((DataServiceQuery<Title>)x).Execute();
while (true)
{
foreach (Title title in response)
{
Console.WriteLine(title.Name);
}
var continuation = response.GetContinuation();
if (continuation == null)
{
break;
}
response = cat.Execute(continuation);
}
I use Rx with following code
public sealed class DataSequence<TEntry> : IObservable<TEntry>
{
private readonly DataServiceContext context;
private readonly Logger logger = LogManager.GetCurrentClassLogger();
private readonly IQueryable<TEntry> query;
public DataSequence(IQueryable<TEntry> query, DataServiceContext context)
{
this.query = query;
this.context = context;
}
public IDisposable Subscribe(IObserver<TEntry> observer)
{
QueryOperationResponse<TEntry> response;
try
{
response = (QueryOperationResponse<TEntry>)((DataServiceQuery<TEntry>)query).Execute();
if (response == null)
{
return Disposable.Empty;
}
}
catch (Exception ex)
{
logger.Error(ex);
return Disposable.Empty;
}
var initialState = new State
{
CanContinue = true,
Response = response
};
IObservable<TEntry> sequence = Observable.Generate(
initialState,
state => state.CanContinue,
MoveToNextState,
GetCurrentValue,
Scheduler.ThreadPool).Merge();
return new CompositeDisposable(initialState, sequence.Subscribe(observer));
}
private static IObservable<TEntry> GetCurrentValue(State state)
{
if (state.Response == null)
{
return Observable.Empty<TEntry>();
}
return state.Response.ToObservable();
}
private State MoveToNextState(State state)
{
DataServiceQueryContinuation<TEntry> continuation = state.Response.GetContinuation();
if (continuation == null)
{
state.CanContinue = false;
return state;
}
QueryOperationResponse<TEntry> response;
try
{
response = context.Execute(continuation);
}
catch (Exception)
{
state.CanContinue = false;
return state;
}
state.Response = response;
return state;
}
private sealed class State : IDisposable
{
public bool CanContinue { get; set; }
public QueryOperationResponse<TEntry> Response { get; set; }
public void Dispose()
{
CanContinue = false;
}
}
}
so for get any data thru OData, create a sequence and Rx does the rest
var sequence = new DataSequence<Product>(context.Products, context);
sequence.OnErrorResumeNext(Observable.Empty<Product>())
.ObserveOnDispatcher().SubscribeOn(Scheduler.NewThread).Subscribe(AddProduct, logger.Error);
The page size is set by the service author and can be set per entity set (but a service may choose to apply the same page size to all entity sets). There's no way to avoid it from the client (which is by design since it's a security feature).
The inlinecount option asks the server to include the total count of the results (just the number), it doesn't disable the paging.
From the client the only way to read all the data is to issue the request which will return the first page and it may contain a next link which you request to read the next page and so on until the last response doesn't have the next link.
If you're using the WCF Data Services client library it has support for continuations (the next link) and a simple sample can be found in this blog post (for example): http://blogs.msdn.com/b/phaniraj/archive/2010/04/25/server-driven-paging-with-wcf-data-services.aspx

ApplyCurrentValues does not seem to work

I try to do the following with entity framework 4 :
public void Update(Site entity)
{
using (db)
{
db.Sites.Attach(db.Sites.Single(s => s.Id == entity.Id));
db.Sites.ApplyCurrentValues(entity);
db.SaveChanges();
}
}
But when i try to update a site through this method i get an error telling me that :
The conversion of a datetime2 data
type to a datetime data type resulted
in an out-of-range value. The
statement has been terminated.
And this is because the original site for some reason is not loaded through the Attach() method.
Can someone help with this ?
/Martin
You don't need to "attach" something you are already retrieving (Ladislav is right). Once you retrieve an entity (e.g SingleOrDefault), it is "in the graph" (EF memory - so it can do optimistic concurrency).
If your trying to do an UPDATE< and the "entity" your passing through is new/detached...
Try the stub technique:
public void Update(Site entity)
{
using (db)
{
var stub = new Site { Id = entity.Id }; // create stub with given key
db.Sites.Attach(stub); // stub is now in graph
db.Sites.ApplyCurrentValues(entity); // override graph (stub) with entity
db.SaveChanges();
}
}
That being said, the error you have provided points to some other issue (data conversion).
Have you checked the "date" values you are passing through with the data type on the model?
public ActionResult Edit(int id, Client collection)
{
try
{
// make sure the rec is in the context
var rec = dbEntities.Clients.First(r => r.ClientID == id);
// update the rec in the context with the parm values
dbEntities.Clients.ApplyCurrentValues(collection);
// make the changes permanent
dbEntities.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}