EF 7 - Context returns null although values are there - entity-framework

I have a CQRS setup and I am trying to use domain events.
After I receive a command for a new order, I am adding the newly created Order object to the dbcontext.
public async Task<Guid> Handle(CreateOrderCommand message, CancellationToken cancellationToken)
{
...
var order = new Order(...);
...
order.SubmitOrder();
_orderRepository.Add(order);
await _orderRepository.UnitOfWork
.SaveEntitiesAsync(cancellationToken);
return order.Id;
}
The order.SubmitOrder() method is as follows
public void SubmitOrder()
{
AddDomainEvent(new OrderPlacedDomainEvent(Guid.NewGuid(), Id));
}
and orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken); is on overload to the UnitOfWork.SaveEntitiesAsync() as follows:
public async Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default)
{
// Dispatch Domain Events collection.
// Choices:
// A) Right BEFORE committing data (EF SaveChanges) into the DB will make a single transaction including
// side effects from the domain event handlers which are using the same DbContext with "InstancePerLifetimeScope" or "scoped" lifetime
// B) Right AFTER committing data (EF SaveChanges) into the DB will make multiple transactions.
// You will need to handle eventual consistency and compensatory actions in case of failures in any of the Handlers.
if (_mediator != null)
{
await _mediator.DispatchDomainEventsAsync(this);
}
// After executing this line all the changes (from the Command Handler and Domain Event Handlers)
// performed through the DbContext will be committed
await base.SaveChangesAsync(cancellationToken);
return true;
}
Note that the changes are not saved before the events are dispatched and the handler is called.
Now in the event handler when I am trying to get the order object from the context:
await _context.Orders.Include(o => o.OrderItems).SingleOrDefaultAsync(o => o.Id == id, cancellationToken: cancellationToken);
it returns null although the data is available in the context under _context.ChangeTracker.DebugView.LongView
Is there any way to get the order data here?

Well, solution found:
If Find/FindAsyncis used instead of SingleOrDefaultAsync it will return the values available in the change tracker.
public async Task<Order?> FindAsync(Guid id, CancellationToken cancellationToken = default)
=> await _context.Orders.FindAsync(new object[] { id }, cancellationToken: cancellationToken);

Related

EF Core System.ObjectDisposedException

I am getting this error mentioned in the title. The same code works for the first API call, but not for the 2nd call. It fails on the 2nd SaveChangesAsync() call
await dbContext.SaveChangesAsync().ConfigureAwait(false);
I am using
services.AddTransient<ProjectContext>();
in my startup.cs. I tried AddScope but it doesn't work.
Error:
System.ObjectDisposedException: 'Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
Object name: 'ProjectContext'.'
Code:
private readonly Action<InjectedControllerArgs, ProjectDto, NavigationElements> _postAfterSave = async (injectedControllerArgs, dto, nav) => {
var memberClient = new MemberClient { MemberId = member.MemberId,
InvitationEmail = member.Email,
ClientId = dto.ClientId,
LastModifierId = member.MemberId };
await dbContext.MemberClient!.AddAsync(memberClient).ConfigureAwait(false);
await dbContext.SaveChangesAsync().ConfigureAwait(false);
var role = await dbContext.Role.Where(r => "client-admin" == r.RoleCode)
.FirstOrDefaultAsync()
.ConfigureAwait(false);
await dbContext.MemberClientRole!.AddAsync(new MemberClientRole
{ LastModifierId = member.MemberId,
MemberClientId = memberClient.MemberClientId,
RoleId = role.RoleId })
.ConfigureAwait(false);
await dbContext.SaveChangesAsync().ConfigureAwait(false); }
public async Task<ActionResult<ProjectDto>> PostInsertAsync(int customerId, int clientId, ProjectDto dto)
{
if (dto == null) throw new ArgumentNullException(nameof(dto));
dto.CustomerId = customerId;
dto.ClientId = clientId;
return await _pmBaseController.PostAsync(new NavigationElements(customerId, clientId, null), Topic, dto,
_returnOne, _postBeforeSave, _postAfterSave).ConfigureAwait(false);
}
public async Task<IActionResult> PutAsync(NavigationElements nav,
TopicName topic,
int id,
TDto dto,
Func<InjectedControllerArgs, NavigationElements, int, Task<T>> returnOne,
Func<TDto, int, TDto>? putBeforeSave = null,
Action<InjectedControllerArgs, TDto, NavigationElements>? putAfterSave = null
) ...
Your _postAfterSave delegate is asynchronous but does not return a Task which effectively means it is async void so your _pmBaseController is unable to await it which means execution will proceed before this Action is done. That the first SaveChanges works is timing / coincidence.
Try changing the type from Action<InjectedControllerArgs, ProjectDto, NavigationElements> to Func<InjectedControllerArgs, ProjectDto, NavigationElements, Task> and make sure to await it everywhere.

How to Mock or Fake MongoDB 's static method FindAsync() in .Net Unit test cases using MOQ or FakeItEasy or Pose

I have been trying to mock/fake the static method FindAsync() in my Unit test cases using Wrappers, some concepts of Pose.
As the static methods cannot be mocked or faked normally.
It is not successful.
The code in the repository layer which I want to unit test points to the IMongoCollectionExtension.FindAsync() method.
This is the method I am trying to mock
public async Task<MyClass> GetItem(Guid id)
{
var filter = Builders<MyClass>.Filter.Eq(m => m.Id, id);
var result = await _context.MyCollection.FindAsync(filter);
return result.FirstOrDefault();
}
This FindAsync() is pointing to IMongoCollectionExtensions STATIC class
public static Task<IAsyncCursor<TDocument>> FindAsync<TDocument>(this IMongoCollection<TDocument> collection, FilterDefinition<TDocument> filter, FindOptions<TDocument, TDocument> options = null, CancellationToken cancellationToken = default(CancellationToken));
So as it is pointing to Static class and a static method I started writing wrapper to mock,
First Method tried using Wrapper:
This is wrapper I have created.
public interface IMongoCollectionExtensionsWrapper
{
Task<IAsyncCursor<MyClass>> FindAsync<MyClass>(IMongoCollection<MyClass> collection, FilterDefinition<MyClass> filter, FindOptions<MyClass, MyClass> options = null, CancellationToken cancellationToken = default(CancellationToken));
}
public class MongoCollectionExtensionsWrapper : IMongoCollectionExtensionsWrapper
{
public Task<IAsyncCursor<MyClass>> FindAsync<MyClass>(IMongoCollection<MyClass> collection, FilterDefinition<MyClass> filter, FindOptions<MyClass, MyClass> options = null, CancellationToken cancellationToken = default(CancellationToken))
{
return collection.FindAsync(filter, options, cancellationToken);
}
}
public static class FakeExtensions
{
public static IMongoCollectionExtensionsWrapper defaultmcExtWrapper = new MongoCollectionExtensionsWrapper();
public static Task<IAsyncCursor<MyClass>> FindAsync(this IMongoCollection<MyClass> collection, FilterDefinition<MyClass> filter, FindOptions<MyClass, MyClass> options = null, CancellationToken cancellationToken = default(CancellationToken))
{
return defaultmcExtWrapper.FindAsync(collection, filter, options, cancellationToken);
}
}
As the wrapper was not working properly i checked out free framework Pose to mock static methods. That was not successful too.
Second trial using Pose
Shim findshim = Shim.Replace(() => IMongoCollectionExtensions.FindAsync(Is.A<IMongoCollection<MyClass>>(), filter, null, CancellationToken.None)).With(delegate (IMongoCollection<MyClass> mc, FilterDefinition<MyClass> f, FindOptions<MyClass, MyClass> o, CancellationToken ca) { return Task.FromResult(_fakeOutput.FakedObject); });
NOTE: _fakeOutput is a faked Cursor holding an IEnumerable. It works fine.
PoseContext.Isolate(() =>
{
Task.Run(() =>
{
var exp = Task.FromResult(item1);
var myres = _Repo.GetItem(Id);
Assert.Equal(exp, myres);
});
}, findshim);
var myres = _Repo.GetItem(Id);
In both the trials, I have tried mocking IMongoCollectionExtensions.FindAsync() but result (output of the method i want to unit test after setting up mock/fake) in both cases are null
and when I tried below Assertion if the FindAsync() method of IMongoCollectionExtension has Happened or not, but it didn't hit. I dont understand when the method i want to unit test is pointing to IMongoCollectionExtension.FindAsync() only but it is not hitting.
fakeIMongoCollExt.CallsTo(x => x.FindAsync(A<IMongoCollection<MyClass>>.Ignored, A<FilterDefinition<MyClass>>.Ignored, null, CancellationToken.None)).MustHaveHappened();
(Method signature has MongoCollections as first parameter - Extension Method)
is showing that it didn't hit that method.
So I tried checking MustHaveHappened() for IMongoCollection.FindAsync() (It is interface method not the static class method which we are discussing above) which also tells that "The target of this call is not the fake object being configured."
I am not sure how FindAsync() is pointing. How to proceed with unit test cases. Please let me know if you have any idea.. Thanks in Advance..
IMongoCollections.FindAsync() Mocking

External login fails asp.net mvc core 2.2

Did scaffold some identity pages (Login, Logout, Register) for translation purposes and added Google as an external provider. It works, but when I hit Register on the Identity/Account/ExternalLogin page I get the following error:
InvalidOperationException: The instance of entity type 'IdentityUserLogin' cannot be tracked because another instance with the same key value for {'LoginProvider', 'ProviderKey'} is already being tracked.
Did scaffold ExternalLogin to check what might be wrong: here is the (standard) code:
public async Task<IActionResult> OnPostConfirmationAsync(string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
// Get the information about the user from the external login provider
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
ErrorMessage = "Error loading external login information during confirmation.";
return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
}
if (ModelState.IsValid)
{
var user = new IdentityUser { UserName = Input.Email, Email = Input.Email };
var result = await _userManager.CreateAsync(user);
if (result.Succeeded)
{
result = await _userManager.AddLoginAsync(user, info);
if (result.Succeeded)
...
The await _userManager.AddLoginAsync(user, info) gives the exception mentioned.
InvalidOperationException: The instance of entity type 'IdentityUserLogin<string>' cannot be tracked because another instance with the same key value for {'LoginProvider', 'ProviderKey'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap<TKey>.ThrowIdentityConflict(InternalEntityEntry entry)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap<TKey>.Add(TKey key, InternalEntityEntry entry, bool updateDuplicate)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap<TKey>.Add(TKey key, InternalEntityEntry entry)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NullableKeyIdentityMap<TKey>.Add(InternalEntityEntry entry)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry entry)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, bool acceptChanges)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState entityState, bool acceptChanges, Nullable<EntityState> forceStateWhenUnknownKey)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.PaintAction(EntityEntryGraphNode node, bool force)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph<TState>(EntityEntryGraphNode node, TState state, Func<EntityEntryGraphNode, TState, bool> handleNode)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.AttachGraph(InternalEntityEntry rootEntry, EntityState entityState, bool forceStateWhenUnknownKey)
Microsoft.EntityFrameworkCore.DbContext.SetEntityState(InternalEntityEntry entry, EntityState entityState)
Microsoft.EntityFrameworkCore.DbContext.SetEntityState<TEntity>(TEntity entity, EntityState entityState)
Microsoft.EntityFrameworkCore.DbContext.Add<TEntity>(TEntity entity)
Microsoft.EntityFrameworkCore.Internal.InternalDbSet<TEntity>.Add(TEntity entity)
Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserOnlyStore<TUser, TContext, TKey, TUserClaim, TUserLogin, TUserToken>.AddLoginAsync(TUser user, UserLoginInfo login, CancellationToken cancellationToken)
Microsoft.AspNetCore.Identity.UserManager<TUser>.AddLoginAsync(TUser user, UserLoginInfo login)
Project.Areas.Identity.Pages.Account.ExternalLoginModel.OnPostConfirmationAsync(string returnUrl) in ExternalLogin.cshtml.cs
+
result = await _userManager.AddLoginAsync(user, info);
How to solve this? Thanks in advance for any help!

Stack overflow exception while returning an object asynchronously

While using MongoDB C# driver with WebApi I came to the following problem. When I want to read all documents (or even just one) from the database the repo's function will get the correct data but in WebApi the object returned from the repo causes a stack overflow. I suspect that I am doing something wrong with the way the objects are returned.
WebApi where the Repo's method is called:
// GET api/<controller>
public async Task<List<Event>> Get()
{
return await _repo.FindAll();
}
// GET api/<controller>/5
public async Task<Event> Get(string id)
{
Event e = await _repo.FindById(id);
return e;
}
And corresponding methods in the Repo:
public async Task<Event> FindById(string id)
{
Event e = await _collection.Find<Event>(x => x.ID == ObjectId.Parse(id)).FirstAsync();
return e;
}
public async Task<List<Event>> FindAll()
{
var filter = new BsonDocument();
List<Event> list = await _collection.Find(filter).ToListAsync();
return await Task<List<Event>>.FromResult(list);
}
Thanks for all the help in advance!
Edit: I found that when I return string from the function instead of Event the whole thing works.
What I think is making problems is the ID property in the Event.
The problem was that the Event had an ObjecId property. The JSON.Net doesn't know about that type. See the solution here: JSON.NET cast error when serializing Mongo ObjectId

Check if an insert or update was successful in Entity Framework

In ADO.NET, ExecuteNonQuery() "For UPDATE, INSERT, and DELETE statements, the return value is the number of rows affected by the command" (http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.executenonquery.aspx)
In EF v1, context.SaveChanges() method returns "The number of objects in an Added, Modified, or Deleted state when SaveChanges was called." (http://msdn.microsoft.com/en-us/library/bb739065.aspx)
Please tell, when multiple entities (or single entity) are added or updated to context and context.SaveChanges() method is called, how to check if actual INSERT or UPDATE was successful.
Can we assume if there was NO exception that INSERT(s) or UPDATE(s) was successful ?
Thank You
Yes, if there is no exception you may assume that the statements executed successfully.
In EntityFramework, SaveChangesAsync() returns an int.
So you can check if it is > 0 or not.
If something happens with SaveChangesAsync() it will return the number of effected rows and this means if value > 0 then true. So simply, you can have below scenerio:
INSERT
public Task<bool> CreateEntity(Entity entity){
if(entity == null)
return false;
await _dataContext.Entities.AddAsync(entity);
var created = await _dataContext.SaveChangesAsync();
return created > 0;
}
UPDATE
public async Task<bool> UpdateEntity(Entity entityToUpdate)
{
if(entityToUpdate == null)
return false;
_dataContext.Posts.Update(entityToUpdate);
var updated = await _dataContext.SaveChangesAsync();
return updated > 0;
}
DELETE
public async Task<bool> DeleteEntity(int entityId)
{
var entity = await _dataContext.Entities.FindAsync(entityId);
if (entity == null)
return false;
_dataContext.Entities.Remove(entity);
var deleted = await _dataContext.SaveChangesAsync();
return deleted > 0;
}
And in your methods, now you can simply check if that change is success or not:
For a simple MVC scenerio:
public Task<IActionResult> CreateEntity(EntityModel model)
{
if(model == null)
return StatusCode(404);
var entity = new Entity
{
attribute1 = model.attribute1,
attribute2 = model.attribute3
};
var isCreated = await _entityService.CreateEntity(entity);
if(isCreated)
{
//do something and return a view.
}
else
{
//you can return a status code, or an error view.
}
}
You can do the same practice for Update & Delete
Maybe this is not direct answer to the question, but may help.
By default all commands are encapsulated in one DbTransaction when SaveChanges method is called (Julia Lerman, Programming Entity Framework). So, or all commands will be successfully executed, or neither. That's one way to know if insert, or update or delete was successful.
define variable SaveStatus
var SaveStatus=context.SaveChanges()
then you can know if the creation has been done by getting the value of SaveStatus=1
in the case of "SaveStatus=0" it means no record has been created or affected